refactor out library class

This commit is contained in:
dave 2020-10-05 23:06:07 -07:00
parent bfcb528ddf
commit c910de0eb0
5 changed files with 90 additions and 156 deletions

View File

@ -2,7 +2,7 @@ import logging
import subprocess import subprocess
from time import time from time import time
from threading import Thread from threading import Thread
from pysonic.library import LETTER_GROUPS from pysonic.database import LETTER_GROUPS
from pysonic.types import MUSIC_TYPES from pysonic.types import MUSIC_TYPES
from pysonic.apilib import formatresponse, ApiResponse from pysonic.apilib import formatresponse, ApiResponse
import cherrypy import cherrypy
@ -11,16 +11,15 @@ logging = logging.getLogger("api")
class PysonicSubsonicApi(object): class PysonicSubsonicApi(object):
def __init__(self, db, library, options): def __init__(self, db, options):
self.db = db self.db = db
self.library = library
self.options = options self.options = options
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def index(self): def index(self):
response = ApiResponse() response = ApiResponse()
response.add_child("totals", **self.library.db.get_stats()) response.add_child("totals", **self.db.get_stats())
return response return response
@cherrypy.expose @cherrypy.expose
@ -46,7 +45,7 @@ class PysonicSubsonicApi(object):
def getMusicFolders_view(self, **kwargs): def getMusicFolders_view(self, **kwargs):
response = ApiResponse() response = ApiResponse()
response.add_child("musicFolders") response.add_child("musicFolders")
for folder in self.library.get_libraries(): for folder in self.db.get_libraries():
response.add_child("musicFolder", _parent="musicFolders", id=folder["id"], name=folder["name"]) response.add_child("musicFolder", _parent="musicFolders", id=folder["id"], name=folder["name"])
return response return response
@ -58,7 +57,7 @@ class PysonicSubsonicApi(object):
# TODO real lastmodified date # TODO real lastmodified date
# TODO deal with ignoredArticles # TODO deal with ignoredArticles
response.add_child("indexes", lastModified="1502310831000", ignoredArticles="The El La Los Las Le Les") response.add_child("indexes", lastModified="1502310831000", ignoredArticles="The El La Los Las Le Les")
artists = self.library.get_artists(sortby="name", order="asc") artists = self.db.get_artists(sortby="name", order="asc")
for letter in LETTER_GROUPS: for letter in LETTER_GROUPS:
index = response.add_child("index", _parent="indexes", name=letter.upper()) index = response.add_child("index", _parent="indexes", name=letter.upper())
for artist in artists: for artist in artists:
@ -83,7 +82,7 @@ class PysonicSubsonicApi(object):
qargs.update(limit=(offset, size)) qargs.update(limit=(offset, size))
albums = self.library.get_albums(**qargs) albums = self.db.get_albums(**qargs)
response = ApiResponse() response = ApiResponse()
@ -111,7 +110,7 @@ class PysonicSubsonicApi(object):
List either and artist or album dir List either and artist or album dir
""" """
dir_id = int(id) dir_id = int(id)
dirtype, dirinfo, entity = self.library.db.get_subsonic_musicdir(dirid=dir_id) dirtype, dirinfo, entity = self.db.get_subsonic_musicdir(dirid=dir_id)
response = ApiResponse() response = ApiResponse()
@ -171,7 +170,7 @@ class PysonicSubsonicApi(object):
def stream_view(self, id, maxBitRate="256", **kwargs): def stream_view(self, id, maxBitRate="256", **kwargs):
maxBitRate = int(maxBitRate) maxBitRate = int(maxBitRate)
assert maxBitRate >= 32 and maxBitRate <= 320 assert maxBitRate >= 32 and maxBitRate <= 320
song = self.library.get_song(int(id)) song = self.db.get_songs(id=int(id))[0]
fpath = song["_fullpath"] fpath = song["_fullpath"]
media_bitrate = song.get("bitrate") / 1024 if song.get("bitrate") else 320 media_bitrate = song.get("bitrate") / 1024 if song.get("bitrate") else 320
to_bitrate = min(maxBitRate, to_bitrate = min(maxBitRate,
@ -219,7 +218,7 @@ class PysonicSubsonicApi(object):
if proc.returncode is None or proc.returncode == 0: if proc.returncode is None or proc.returncode == 0:
logging.warning("transcoded {} in {}s".format(id, int(time() - start))) logging.warning("transcoded {} in {}s".format(id, int(time() - start)))
# if completed: # if completed:
# self.library.report_transcode(id, to_bitrate, length) # self.db.report_transcode(id, to_bitrate, length)
else: else:
logging.error("transcode of {} exited with code {} after {}s".format(id, proc.returncode, logging.error("transcode of {} exited with code {} after {}s".format(id, proc.returncode,
int(time() - start))) int(time() - start)))
@ -248,7 +247,7 @@ class PysonicSubsonicApi(object):
""" """
if id.startswith("pl-"): # get art from first track in playlist if id.startswith("pl-"): # get art from first track in playlist
playlist_id = int(id[len("pl-"):]) playlist_id = int(id[len("pl-"):])
_, songs = self.library.get_playlist(playlist_id) songs = self.db.get_playlist_songs(playlist_id)
for song in songs: for song in songs:
if song["albumcoverid"]: if song["albumcoverid"]:
id = song["albumcoverid"] id = song["albumcoverid"]
@ -262,8 +261,7 @@ class PysonicSubsonicApi(object):
else: else:
id = int(id) id = int(id)
cover = self.library.get_cover(id) fpath = self.db.get_cover_path(id)
fpath = cover["_fullpath"]
type2ct = { type2ct = {
'jpg': 'image/jpeg', 'jpg': 'image/jpeg',
'png': 'image/png', 'png': 'image/png',
@ -280,14 +278,14 @@ class PysonicSubsonicApi(object):
break break
total += len(data) total += len(data)
yield data yield data
logging.info("\nSent {} bytes for {}".format(total, fpath)) logging.info("sent {} bytes for {}".format(total, fpath))
return content() return content()
getCoverArt_view._cp_config = {'response.stream': True} getCoverArt_view._cp_config = {'response.stream': True}
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def getArtistInfo_view(self, id, includeNotPresent="true", **kwargs): def getArtistInfo_view(self, id, includeNotPresent="true", **kwargs):
info = self.library.get_artist_info(id) info = self.db.get_artist_info(id)
response = ApiResponse() response = ApiResponse()
response.add_child("artistInfo") response.add_child("artistInfo")
response.set_attrs("artistInfo", **info) response.set_attrs("artistInfo", **info)
@ -296,7 +294,7 @@ class PysonicSubsonicApi(object):
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def getUser_view(self, username, **kwargs): def getUser_view(self, username, **kwargs):
user = {} if self.options.disable_auth else self.library.db.get_user(cherrypy.request.login) user = {} if self.options.disable_auth else self.db.get_user(cherrypy.request.login)
response = ApiResponse() response = ApiResponse()
response.add_child("user", response.add_child("user",
username=user["username"], username=user["username"],
@ -321,19 +319,19 @@ class PysonicSubsonicApi(object):
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def star_view(self, id, **kwargs): def star_view(self, id, **kwargs):
self.library.set_starred(cherrypy.request.login, int(id), starred=True) self.db.set_starred(cherrypy.request.login, int(id), starred=True)
return ApiResponse() return ApiResponse()
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def unstar_view(self, id, **kwargs): def unstar_view(self, id, **kwargs):
self.library.set_starred(cherrypy.request.login, int(id), starred=False) self.db.set_starred(cherrypy.request.login, int(id), starred=False)
return ApiResponse() return ApiResponse()
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def getStarred_view(self, **kwargs): def getStarred_view(self, **kwargs):
children = self.library.get_starred(cherrypy.request.login) children = self.db.get_starred(cherrypy.request.login)
response = ApiResponse() response = ApiResponse()
response.add_child("starred") response.add_child("starred")
for item in children: for item in children:
@ -355,7 +353,7 @@ class PysonicSubsonicApi(object):
""" """
response = ApiResponse() response = ApiResponse()
response.add_child("randomSongs") response.add_child("randomSongs")
children = self.library.db.get_songs(limit=size, sortby="random") children = self.db.get_songs(limit=size, sortby="random")
for song in children: for song in children:
moreargs = {} moreargs = {}
if song["format"]: if song["format"]:
@ -390,7 +388,7 @@ class PysonicSubsonicApi(object):
def getGenres_view(self, **kwargs): def getGenres_view(self, **kwargs):
response = ApiResponse() response = ApiResponse()
response.add_child("genres") response.add_child("genres")
for row in self.library.db.get_genres(): for row in self.db.get_genres():
response.add_child("genre", _parent="genres", value=row["name"], songCount=420, albumCount=69) response.add_child("genre", _parent="genres", value=row["name"], songCount=420, albumCount=69)
return response return response
@ -418,7 +416,7 @@ class PysonicSubsonicApi(object):
query = query.replace("*", "") # TODO handle this query = query.replace("*", "") # TODO handle this
artists = 0 artists = 0
for item in self.library.get_artists(name_contains=query): for item in self.db.get_artists(name_contains=query):
response.add_child("artist", _parent="searchResult2", id=item["id"], name=item["name"]) response.add_child("artist", _parent="searchResult2", id=item["id"], name=item["name"])
artists += 1 artists += 1
if artists >= artistCount: if artists >= artistCount:
@ -426,7 +424,7 @@ class PysonicSubsonicApi(object):
# TODO make this more efficient # TODO make this more efficient
albums = 0 albums = 0
for album in self.library.get_albums(name_contains=query): for album in self.db.get_albums(name_contains=query):
response.add_child("album", _parent="searchResult2", response.add_child("album", _parent="searchResult2",
id=album["dir"], id=album["dir"],
parent=album["artistdir"], parent=album["artistdir"],
@ -445,7 +443,7 @@ class PysonicSubsonicApi(object):
# TODO make this more efficient # TODO make this more efficient
songs = 0 songs = 0
for song in self.library.db.get_songs(title_contains=query): for song in self.db.get_songs(title_contains=query):
response.add_child("song", _parent="searchResult2", response.add_child("song", _parent="searchResult2",
id=song["id"], id=song["id"],
parent=song["albumdir"], parent=song["albumdir"],
@ -485,11 +483,11 @@ class PysonicSubsonicApi(object):
def savePlayQueue_view(self, id, current, position, **kwargs): def savePlayQueue_view(self, id, current, position, **kwargs):
print("TODO save playqueue with items {} current {} position {}".format(id, repr(current), repr(position))) print("TODO save playqueue with items {} current {} position {}".format(id, repr(current), repr(position)))
current = int(current) current = int(current)
song = self.library.get_song(current) song = self.db.get_songs(id=current)[0]
self.library.db.update_album_played(song['albumid'], time()) self.db.update_album_played(song['albumid'], time())
self.library.db.increment_album_plays(song['albumid']) self.db.increment_album_plays(song['albumid'])
if int(position) == 0: if int(position) == 0:
self.library.db.increment_track_plays(current) self.db.increment_track_plays(current)
# TODO save playlist with items ['378', '386', '384', '380', '383'] current 383 position 4471 # TODO save playlist with items ['378', '386', '384', '380', '383'] current 383 position 4471
# id entries are strings! # id entries are strings!
@ -498,19 +496,19 @@ class PysonicSubsonicApi(object):
def createPlaylist_view(self, name, songId, **kwargs): def createPlaylist_view(self, name, songId, **kwargs):
if type(songId) != list: if type(songId) != list:
songId = [songId] songId = [songId]
user = self.library.db.get_user(cherrypy.request.login) user = self.db.get_user(cherrypy.request.login)
self.library.db.add_playlist(user["id"], name, songId) self.db.add_playlist(user["id"], name, songId)
return ApiResponse() return ApiResponse()
#TODO the response should be the new playlist, check the cap #TODO the response should be the new playlist, check the cap
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def getPlaylists_view(self, **kwargs): def getPlaylists_view(self, **kwargs):
user = self.library.db.get_user(cherrypy.request.login) user = self.db.get_user(cherrypy.request.login)
response = ApiResponse() response = ApiResponse()
response.add_child("playlists") response.add_child("playlists")
for playlist in self.library.db.get_playlists(user["id"]): for playlist in self.db.get_playlists(user["id"]):
response.add_child("playlist", response.add_child("playlist",
_parent="playlists", _parent="playlists",
id=playlist["id"], id=playlist["id"],
@ -529,9 +527,10 @@ class PysonicSubsonicApi(object):
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def getPlaylist_view(self, id, **kwargs): def getPlaylist_view(self, id, **kwargs):
user = self.library.db.get_user(cherrypy.request.login) id = int(id)
plinfo, songs = self.library.get_playlist(int(id)) user = self.db.get_user(cherrypy.request.login)
plinfo = self.db.get_playlist(id)
songs = self.db.get_playlist_songs(id)
response = ApiResponse() response = ApiResponse()
response.add_child("playlist", response.add_child("playlist",
id=plinfo["id"], id=plinfo["id"],
@ -569,15 +568,16 @@ class PysonicSubsonicApi(object):
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def updatePlaylist_view(self, playlistId, songIndexToRemove=None, songIdToAdd=None, **kwargs): def updatePlaylist_view(self, playlistId, songIndexToRemove=None, songIdToAdd=None, **kwargs):
user = self.library.db.get_user(cherrypy.request.login) playlistId = int(playlistId)
plinfo, songs = self.library.get_playlist(int(playlistId)) user = self.db.get_user(cherrypy.request.login)
plinfo = self.db.get_playlist(playlistId)
assert plinfo["ownerid"] == user["id"] assert plinfo["ownerid"] == user["id"]
if songIndexToRemove: if songIndexToRemove:
self.library.db.remove_index_from_playlist(playlistId, songIndexToRemove) self.db.remove_index_from_playlist(playlistId, songIndexToRemove)
elif songIdToAdd: elif songIdToAdd:
self.library.db.add_to_playlist(playlistId, songIdToAdd) self.db.add_to_playlist(playlistId, songIdToAdd)
#TODO there are more modification methods #TODO there are more modification methods
return ApiResponse() return ApiResponse()
@ -585,9 +585,9 @@ class PysonicSubsonicApi(object):
@cherrypy.expose @cherrypy.expose
@formatresponse @formatresponse
def deletePlaylist_view(self, id, **kwargs): def deletePlaylist_view(self, id, **kwargs):
user = self.library.db.get_user(cherrypy.request.login) user = self.db.get_user(cherrypy.request.login)
plinfo, _ = self.library.get_playlist(int(id)) plinfo = self.db.get_playlist(int(id))
assert plinfo["ownerid"] == user["id"] assert plinfo["ownerid"] == user["id"]
self.library.delete_playlist(plinfo["id"]) self.db.delete_playlist(plinfo["id"])
return ApiResponse() return ApiResponse()

View File

@ -3,7 +3,6 @@ import logging
import cherrypy import cherrypy
from sqlite3 import DatabaseError from sqlite3 import DatabaseError
from pysonic.api import PysonicSubsonicApi from pysonic.api import PysonicSubsonicApi
from pysonic.library import PysonicLibrary
from pysonic.database import PysonicDatabase, DuplicateRootException from pysonic.database import PysonicDatabase, DuplicateRootException
@ -35,15 +34,14 @@ def main():
format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s") format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
db = PysonicDatabase(path=args.database_path) db = PysonicDatabase(path=args.database_path)
library = PysonicLibrary(db)
for dirname in args.dirs: for dirname in args.dirs:
dirname = os.path.abspath(dirname) dirname = os.path.abspath(dirname)
assert os.path.exists(dirname), "--dirs must be paths that exist" assert os.path.exists(dirname), "--dirs must be paths that exist"
try: try:
library.add_root_dir(dirname) db.add_root(dirname)
except DuplicateRootException: except DuplicateRootException:
pass pass
library.update() db.update()
for username, password in args.user: for username, password in args.user:
try: try:
@ -55,7 +53,7 @@ def main():
# logging.warning("Artists: {}".format([i["name"] for i in library.get_artists()])) # logging.warning("Artists: {}".format([i["name"] for i in library.get_artists()]))
# logging.warning("Albums: {}".format(len(library.get_albums()))) # logging.warning("Albums: {}".format(len(library.get_albums())))
api = PysonicSubsonicApi(db, library, args) api = PysonicSubsonicApi(db, args)
api_config = {} api_config = {}
if args.disable_auth: if args.disable_auth:
logging.warning("starting up with auth disabled") logging.warning("starting up with auth disabled")

View File

@ -1,3 +1,4 @@
import os
import sqlite3 import sqlite3
import logging import logging
from hashlib import sha512 from hashlib import sha512
@ -5,7 +6,17 @@ from time import time
from contextlib import closing from contextlib import closing
from collections import Iterable from collections import Iterable
from pysonic.scanner import PysonicFilesystemScanner
logging = logging.getLogger("database") logging = logging.getLogger("database")
LETTER_GROUPS = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "xyz", "0123456789"]
keys_in_table = ["title", "album", "artist", "type", "size"] keys_in_table = ["title", "album", "artist", "type", "size"]
@ -49,11 +60,18 @@ class PysonicDatabase(object):
self.db = None self.db = None
self.open() self.open()
self.migrate() self.migrate()
self.scanner = PysonicFilesystemScanner(self)
def open(self): def open(self):
self.db = sqlite3.connect(self.path, **self.sqlite_opts) self.db = sqlite3.connect(self.path, **self.sqlite_opts)
self.db.row_factory = dict_factory self.db.row_factory = dict_factory
def update(self):
"""
Start the library media scanner ands
"""
self.scanner.init_scan()
def migrate(self): def migrate(self):
# Create db # Create db
queries = ["""CREATE TABLE 'libraries' ( queries = ["""CREATE TABLE 'libraries' (
@ -150,6 +168,16 @@ class PysonicDatabase(object):
# logging.warning("db schema is version {}".format(version)) # logging.warning("db schema is version {}".format(version))
pass pass
def get_artist_info(self, item_id):
#TODO
return {"biography": "placeholder biography",
"musicBrainzId": "playerholder",
"lastFmUrl": "https://www.last.fm/music/Placeholder",
"smallImageUrl": "",
"mediumImageUrl": "",
"largeImageUrl": "",
"similarArtists": []}
@cursor @cursor
def get_stats(self, c): def get_stats(self, c):
songs = c.execute("SELECT COUNT(*) as cnt FROM songs").fetchone()['cnt'] songs = c.execute("SELECT COUNT(*) as cnt FROM songs").fetchone()['cnt']
@ -167,7 +195,7 @@ class PysonicDatabase(object):
:return: int :return: int
:raises: sqlite3.IntegrityError :raises: sqlite3.IntegrityError
""" """
assert path.startswith("/") path = os.path.abspath(os.path.normpath(path))
try: try:
c.execute("INSERT INTO libraries ('name', 'path') VALUES (?, ?)", (name, path, )) c.execute("INSERT INTO libraries ('name', 'path') VALUES (?, ?)", (name, path, ))
c.execute("COMMIT") c.execute("COMMIT")
@ -348,11 +376,16 @@ class PysonicDatabase(object):
return genres return genres
@cursor @cursor
def get_cover(self, c, coverid): def get_cover(self, c, cover_id):
cover = None cover = None
for cover in c.execute("SELECT * FROM covers WHERE id = ?", (coverid, )): for cover in c.execute("SELECT * FROM covers WHERE id = ?", (cover_id, )):
return cover return cover
def get_cover_path(self, cover_id):
cover = self.get_cover(cover_id)
library = self.get_libraries(cover["library"])[0]
return os.path.join(library["path"], cover["path"])
@cursor @cursor
def get_subsonic_musicdir(self, c, dirid): def get_subsonic_musicdir(self, c, dirid):
""" """
@ -469,12 +502,13 @@ class PysonicDatabase(object):
@cursor @cursor
def empty_playlist(self, c, playlist_id): def empty_playlist(self, c, playlist_id):
#TODO combine with ?? #TODO combine with delete_playlist
c.execute("DELETE FROM playlist_entries WHERE playlistid=?", (playlist_id, )) c.execute("DELETE FROM playlist_entries WHERE playlistid=?", (playlist_id, ))
c.execute("COMMIT") c.execute("COMMIT")
@cursor @cursor
def delete_playlist(self, c, playlist_id): def delete_playlist(self, c, playlist_id):
c.execute("DELETE FROM playlist_entries WHERE playlistid=?", (playlist_id, ))
c.execute("DELETE FROM playlists WHERE id=?", (playlist_id, )) c.execute("DELETE FROM playlists WHERE id=?", (playlist_id, ))
c.execute("COMMIT") c.execute("COMMIT")

View File

@ -1,98 +0,0 @@
import os
import logging
from pysonic.scanner import PysonicFilesystemScanner
from pysonic.types import MUSIC_TYPES
LETTER_GROUPS = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
"u", "v", "w", "xyz", "0123456789"]
logging = logging.getLogger("library")
def memoize(function):
memo = {}
def wrapper(*args):
if args in memo:
return memo[args]
else:
rv = function(*args)
memo[args] = rv
return rv
return wrapper
class NoDataException(Exception):
pass
class PysonicLibrary(object):
def __init__(self, database):
self.db = database
self.get_libraries = self.db.get_libraries
self.get_artists = self.db.get_artists
self.get_albums = self.db.get_albums
# self.get_song = self.db.get_song
# self.get_cover = self.db.get_cover
self.scanner = PysonicFilesystemScanner(self)
logging.info("library ready")
def update(self):
"""
Start the library media scanner ands
"""
self.scanner.init_scan()
def add_root_dir(self, path):
"""
The music library consists of a number of root dirs. This adds a new root
"""
path = os.path.abspath(os.path.normpath(path))
self.db.add_root(path)
# def get_artists(self, *args, **kwargs):
# artists = self.db.get_artists(*args, **kwargs)
# for item in artists:
# item["parent"] = item["libraryid"]
# return artists
# def get_albums(self, *args, **kwargs):
# albums = self.db.get_albums(*args, **kwargs)
# for item in albums:
# item["parent"] = item["artistid"]
# return albums
def get_artist_info(self, item_id):
#TODO
return {"biography": "placeholder biography",
"musicBrainzId": "playerholder",
"lastFmUrl": "https://www.last.fm/music/Placeholder",
"smallImageUrl": "",
"mediumImageUrl": "",
"largeImageUrl": "",
"similarArtists": []}
def get_cover(self, cover_id):
cover = self.db.get_cover(cover_id)
library = self.db.get_libraries(cover["library"])[0]
cover['_fullpath'] = os.path.join(library["path"], cover["path"])
return cover
def get_song(self, song_id):
song = self.db.get_songs(id=song_id)[0]
library = self.db.get_libraries(song["library"])[0]
song['_fullpath'] = os.path.join(library["path"], song["file"])
return song
def get_playlist(self, playlist_id):
playlist_info = self.db.get_playlist(playlist_id)
songs = self.db.get_playlist_songs(playlist_id)
return (playlist_info, songs)
def delete_playlist(self, playlist_id):
self.db.empty_playlist(playlist_id)
self.db.delete_playlist(playlist_id)

View File

@ -18,8 +18,8 @@ RE_NUMBERS = re.compile(r'^([0-9]+)')
class PysonicFilesystemScanner(object): class PysonicFilesystemScanner(object):
def __init__(self, library): def __init__(self, db):
self.library = library self.db = db
def init_scan(self): def init_scan(self):
self.scanner = Thread(target=self.rescan, daemon=True) self.scanner = Thread(target=self.rescan, daemon=True)
@ -31,7 +31,7 @@ class PysonicFilesystemScanner(object):
""" """
start = time() start = time()
logging.warning("Beginning library rescan") logging.warning("Beginning library rescan")
for parent in self.library.db.get_libraries(): for parent in self.db.get_libraries():
logging.info("Scanning {}".format(parent["path"])) logging.info("Scanning {}".format(parent["path"]))
self.scan_root(parent["id"], parent["path"]) self.scan_root(parent["id"], parent["path"])
logging.warning("Rescan complete in %ss", round(time() - start, 3)) logging.warning("Rescan complete in %ss", round(time() - start, 3))
@ -63,7 +63,7 @@ class PysonicFilesystemScanner(object):
:type path list :type path list
""" """
assert path assert path
# with closing(self.library.db.db.cursor()) as cursor: # with closing(self.db.db.cursor()) as cursor:
parent_id = 0 # 0 indicates a top level item in the library parent_id = 0 # 0 indicates a top level item in the library
for name in path: for name in path:
parent_id = self.create_or_get_dbdir(cursor, pid, parent_id, name) parent_id = self.create_or_get_dbdir(cursor, pid, parent_id, name)
@ -109,7 +109,7 @@ class PysonicFilesystemScanner(object):
if len(path) > 1: if len(path) > 1:
album = path[-1] album = path[-1]
with closing(self.library.db.db.cursor()) as cursor: with closing(self.db.db.cursor()) as cursor:
artist_id, artist_dirid = self.create_or_get_artist(cursor, pid, path[0]) artist_id, artist_dirid = self.create_or_get_artist(cursor, pid, path[0])
album_id = None album_id = None
@ -226,8 +226,8 @@ class PysonicFilesystemScanner(object):
q += "ORDER BY albumid" q += "ORDER BY albumid"
#TODO scraping ID3 etc from the media files can be parallelized #TODO scraping ID3 etc from the media files can be parallelized
with closing(self.library.db.db.cursor()) as reader, \ with closing(self.db.db.cursor()) as reader, \
closing(self.library.db.db.cursor()) as writer: closing(self.db.db.cursor()) as writer:
processed = 0 # commit batching counter processed = 0 # commit batching counter
for row in reader.execute(q): for row in reader.execute(q):
# Find meta, bail if the file was unreadable # Find meta, bail if the file was unreadable