Add DB implementation, models, logging.
This commit is contained in:
parent
bad288a24b
commit
3f3a6b286b
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
*.sw?
|
||||
/wdiwtlt
|
||||
nimble.develop
|
||||
nimble.paths
|
||||
|
4
config.nims
Normal file
4
config.nims
Normal file
@ -0,0 +1,4 @@
|
||||
# begin Nimble config (version 2)
|
||||
when withDir(thisDir(), system.fileExists("nimble.paths")):
|
||||
include "nimble.paths"
|
||||
# end Nimble config
|
@ -1,6 +1,8 @@
|
||||
import std/[os, uri]
|
||||
import mpv
|
||||
|
||||
import wdiwtlt/[models, db]
|
||||
|
||||
when isMainModule:
|
||||
var ctx: ptr handle
|
||||
try:
|
||||
|
@ -1,9 +1,7 @@
|
||||
import std/[json, jsonutils, options, sequtils, strutils, times]
|
||||
import std/[json, options, sequtils, strutils, times]
|
||||
import db_connector/db_sqlite
|
||||
import waterpark/sqlite
|
||||
import fiber_orm, namespaced_logging, timeutils
|
||||
|
||||
import ./models
|
||||
import fiber_orm, timeutils, uuids
|
||||
|
||||
export fiber_orm.NotFoundError
|
||||
export fiber_orm.PaginationParams
|
||||
@ -11,6 +9,8 @@ export fiber_orm.PagedRecords
|
||||
export fiber_orm.enableDbLogging
|
||||
export sqlite.close
|
||||
|
||||
import ./[logging, models]
|
||||
|
||||
type
|
||||
WdiwtltDb* = SqlitePool
|
||||
|
||||
@ -25,3 +25,53 @@ proc initDB*(dbPath: string): SqlitePool =
|
||||
|
||||
generateProcsForModels(WdiwtltDb,
|
||||
[Artist, Album, MediaFile, Tag, Playlist, Bookmark, Image])
|
||||
|
||||
generateJoinTableLookups(WdiwtltDb, Artist, Album, "artists_albums")
|
||||
generateJoinTableLookups(WdiwtltDb, Artist, MediaFile, "artists_media_files")
|
||||
generateJoinTableLookups(WdiwtltDb, Album, MediaFile, "albums_media_files")
|
||||
generateJoinTableLookups(WdiwtltDb, Playlist, MediaFile, "playlists_media_files")
|
||||
generateJoinTableLookups(WdiwtltDb, MediaFile, Tag, "media_files_tags")
|
||||
generateJoinTableLookups(WdiwtltDb, Artist, Image, "artists_images")
|
||||
generateJoinTableLookups(WdiwtltDb, Album, Image, "albums_images")
|
||||
|
||||
proc removeEmptyAlbums*(sql: WdiwtltDb) =
|
||||
## Remove albums that have no media files.
|
||||
|
||||
let query = """
|
||||
DELETE FROM albums WHERE id IN (
|
||||
SELECT DISTINCT al.id
|
||||
FROM albums al LEFT OUTER JOIN albums_media_files almf ON
|
||||
al.id = almf.album_id
|
||||
WHERE almf.album_id IS NULL)"""
|
||||
|
||||
getLogger("wdiwtlt/db").debug(%*{
|
||||
"msg": "Deleting empty albums.",
|
||||
"query": query})
|
||||
|
||||
proc removeEmptyArtists*(sql: WdiwtltDb) =
|
||||
## Remove artists that have no albums.
|
||||
|
||||
let query = """
|
||||
DELETE FROM artists WHERE id IN (
|
||||
SELECT DISTINCT ar.id
|
||||
FROM artists ar LEFT OUTER JOIN artists_albums aral ON
|
||||
ar.id = aral.artist_id
|
||||
WHERE aral.artist_id IS NULL)"""
|
||||
|
||||
getLogger("wdiwtlt/db").debug(%*{
|
||||
"msg": "Deleting empty artists.",
|
||||
"query": query})
|
||||
|
||||
proc removeEmptyPlaylists*(sql: WdiwtltDb) =
|
||||
## Remove playlists that have no media files.
|
||||
|
||||
let query = """
|
||||
DELETE FROM playlists WHERE id IN (
|
||||
SELECT DISTINCT pl.id
|
||||
FROM playlists pl LEFT OUTER JOIN playlists_media_files plmf ON
|
||||
pl.id = plmf.playlist_id
|
||||
WHERE plmf.playlist_id IS NULL)"""
|
||||
|
||||
getLogger("wdiwtlt/db").debug(%*{
|
||||
"msg": "Deleting empty playlists.",
|
||||
"query": query})
|
||||
|
45
src/main/nim/wdiwtlt/logging.nim
Normal file
45
src/main/nim/wdiwtlt/logging.nim
Normal file
@ -0,0 +1,45 @@
|
||||
import std/[options, os, strutils, unicode]
|
||||
import namespaced_logging, zero_functional
|
||||
import ./db
|
||||
|
||||
export namespaced_logging
|
||||
|
||||
var logService* {.threadvar.}: Option[LogService]
|
||||
|
||||
proc enableLogging*(svc: LogService = initLogService(), debug = false): LogService =
|
||||
if not (svc.cfg.appenders --> exists(it of ConsoleLogAppender)):
|
||||
svc.addAppender(initConsoleLogAppender(threshold = lvlAll))
|
||||
|
||||
enableDbLogging(svc)
|
||||
logService = some(svc)
|
||||
result = svc
|
||||
|
||||
|
||||
proc configureLoggingThresholds*(debug = false) =
|
||||
if not logService.isSome: return
|
||||
let logSvc = logService.get
|
||||
|
||||
if debug:
|
||||
logSvc.cfg.rootLevel = Level.lvlDebug
|
||||
logSvc.cfg.loggers.add([
|
||||
LoggerConfig(name: "wdiwtlt", threshold: some(Level.lvlDebug)),
|
||||
LoggerConfig(name: "fiber_orm", threshold: some(Level.lvlDebug)),
|
||||
])
|
||||
else: logSvc.cfg.rootLevel = Level.lvlInfo
|
||||
|
||||
logSvc.reloadThreadState()
|
||||
|
||||
|
||||
proc enableLoggingByEnvVar*(envVar = "DEBUG"): void =
|
||||
if not logService.isSome: discard enableLogging()
|
||||
let val = getEnv(envVar, "false").toLower()
|
||||
|
||||
configureLoggingThresholds(
|
||||
"true".startsWith(val) or
|
||||
"yes".startsWith(val) or
|
||||
"on".startsWith(val) or
|
||||
val == "1")
|
||||
|
||||
proc getLogger*(name: string): Option[Logger] =
|
||||
if logService.isSome: return some(logService.get.getLogger(name))
|
||||
else: return none[Logger]()
|
@ -1,4 +1,4 @@
|
||||
import std/[paths]
|
||||
import std/[paths, times]
|
||||
import namespaced_logging
|
||||
|
||||
import ./[db, models]
|
||||
@ -7,3 +7,34 @@ type
|
||||
MediaLibrary* = ref object
|
||||
db: WdiwtltDb
|
||||
libraryRoot: Path
|
||||
|
||||
proc clean*(lib: MediaLibrary) =
|
||||
removeEmptyAlbums(lib.db)
|
||||
removeEmptyArtists(lib.db)
|
||||
removeEmptyPlaylists(lib.db)
|
||||
|
||||
let expirationDate = now() - weeks(1)
|
||||
|
||||
let expiredPlaylists = lib.db.findPlaylstsWhere(
|
||||
"user_created = false AND last_used_at < ?",
|
||||
[expirationDate])
|
||||
|
||||
let expiredBookmarks = lib.db.findBookmarksWhere(
|
||||
"user_created = false AND last_used_at < ?",
|
||||
[expirationDate])
|
||||
|
||||
expiredPlaylists.applyIt(lib.db.deletePlaylist(it.id))
|
||||
expiredBookmarks.applyIt(lib.db.deleteBookmark(it.id))
|
||||
|
||||
proc rescanLibrary*(lib: MediaLibrary) =
|
||||
# TODO: Implement this. Below is AI-generated code.
|
||||
let mediaFiles = lib.db.getAllMediaFiles()
|
||||
for mf in mediaFiles:
|
||||
let filePath = lib.libraryRoot / mf.filePath
|
||||
if not filePath.existsFile:
|
||||
lib.db.deleteMediaFile(mf.id)
|
||||
continue
|
||||
|
||||
let fileHash = filePath.hashFile
|
||||
if fileHash != mf.fileHash:
|
||||
lib.db.updateMediaFile(mf.id, fileHash: fileHash)
|
||||
|
@ -1,4 +1,4 @@
|
||||
import std/[options, paths, times]
|
||||
import std/[options, times]
|
||||
import uuids
|
||||
|
||||
type
|
||||
@ -18,7 +18,7 @@ type
|
||||
discNumber*: int
|
||||
trackNumber*: Option[int]
|
||||
playCount*: int
|
||||
filePath*: Path
|
||||
filePath*: string
|
||||
fileHash*: string
|
||||
metaInfoSource*: string
|
||||
dateAdded*: DateTime
|
||||
|
@ -11,4 +11,7 @@ bin = @["wdiwtlt"]
|
||||
# Dependencies
|
||||
|
||||
requires "nim >= 2.2.0"
|
||||
requires @["mpv", "nimterop"]
|
||||
requires @["mpv", "nimterop", "uuids", "waterpark"]
|
||||
|
||||
# Dependencies from https://git.jdb-software.com/jdb/nim-packages
|
||||
requires @["db_migrate", "fiber_orm"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user