diff --git a/cli.py b/cli.py index 4b4f069..93eebc0 100755 --- a/cli.py +++ b/cli.py @@ -22,6 +22,11 @@ import sys, cmd, argparse, getpass, time import config +from db import get_store, Folder +from managers.folder import FolderManager +from managers.user import UserManager +from scanner import Scanner + class CLIParser(argparse.ArgumentParser): def error(self, message): self.print_usage(sys.stderr) @@ -53,7 +58,7 @@ class CLI(cmd.Cmd): return method - def __init__(self): + def __init__(self, store): cmd.Cmd.__init__(self) # Generate do_* and help_* methods @@ -69,6 +74,8 @@ class CLI(cmd.Cmd): for action, subparser in getattr(self.__class__, command + '_subparsers').choices.iteritems(): setattr(self, 'help_{} {}'.format(command, action), subparser.print_help) + self.__store = store + def do_EOF(self, line): return True @@ -105,17 +112,17 @@ class CLI(cmd.Cmd): def folder_list(self): print 'Name\t\tPath\n----\t\t----' - print '\n'.join('{0: <16}{1}'.format(f.name, f.path) for f in db.Folder.query.filter(db.Folder.root == True)) + print '\n'.join('{0: <16}{1}'.format(f.name, f.path) for f in self.__store.find(Folder, Folder.root == True)) def folder_add(self, name, path): - ret = FolderManager.add(name, path) + ret = FolderManager.add(self.__store, name, path) if ret != FolderManager.SUCCESS: print FolderManager.error_str(ret) else: print "Folder '{}' added".format(name) def folder_delete(self, name): - ret = FolderManager.delete_by_name(name) + ret = FolderManager.delete_by_name(self.__store, name) if ret != FolderManager.SUCCESS: print FolderManager.error_str(ret) else: @@ -134,19 +141,19 @@ class CLI(cmd.Cmd): print "Scanning '{0}': {1}% ({2}/{3})".format(self.__name, (scanned * 100) / total, scanned, total) self.__last_display = time.time() - s = Scanner(db.session) + s = Scanner(self.__store) if folders: - folders = map(lambda n: db.Folder.query.filter(db.Folder.name == n and db.Folder.root == True).first() or n, folders) + folders = map(lambda n: self.__store.find(Folder, Folder.name == n, Folder.root == True).one() or n, folders) if any(map(lambda f: isinstance(f, basestring), folders)): print "No such folder(s): " + ' '.join(f for f in folders if isinstance(f, basestring)) - for folder in filter(lambda f: isinstance(f, db.Folder), folders): - FolderManager.scan(folder.id, s, TimedProgressDisplay(folder.name)) + for folder in filter(lambda f: isinstance(f, Folder), folders): + FolderManager.scan(self.__store, folder.id, s, TimedProgressDisplay(folder.name)) else: - for folder in db.Folder.query.filter(db.Folder.root == True): - FolderManager.scan(folder.id, s, TimedProgressDisplay(folder.name)) + for folder in self.__store.find(Folder, Folder.root == True): + FolderManager.scan(self.__store, folder.id, s, TimedProgressDisplay(folder.name)) added, deleted = s.stats() - db.session.commit() + self.__store.commit() print "Scanning done" print 'Added: %i artists, %i albums, %i tracks' % (added[0], added[1], added[2]) @@ -219,15 +226,9 @@ if __name__ == "__main__": if not config.check(): sys.exit(1) - import db - db.init_db() - - from managers.folder import FolderManager - from managers.user import UserManager - from scanner import Scanner - + cli = CLI(get_store(config.get('base', 'database_uri'))) if len(sys.argv) > 1: - CLI().onecmd(' '.join(sys.argv[1:])) + cli.onecmd(' '.join(sys.argv[1:])) else: - CLI().cmdloop() + cli.cmdloop() diff --git a/db.py b/db.py index bd4eb99..6874f4e 100644 --- a/db.py +++ b/db.py @@ -19,7 +19,10 @@ # along with this program. If not, see . from storm.properties import * -from storm.references import * +from storm.references import Reference, ReferenceSet +from storm.database import create_database +from storm.store import Store +from storm.variables import Variable import uuid, datetime, time import os.path @@ -27,6 +30,18 @@ import os.path def now(): return datetime.datetime.now().replace(microsecond = 0) +class UnicodeOrStrVariable(Variable): + __slots__ = () + + def parse_set(self, value, from_db): + if isinstance(value, unicode): + return value + elif isinstance(value, str): + return unicode(value) + raise TypeError("Expected unicode, found %r: %r" % (type(value), value)) + +Unicode.variable_class = UnicodeOrStrVariable + class Folder(object): __storm_table__ = 'folder' @@ -353,3 +368,8 @@ class PlaylistTrack(object): Playlist.tracks = ReferenceSet(Playlist.id, PlaylistTrack.playlist_id, PlaylistTrack.track_id, Track.id) +def get_store(database_uri): + database = create_database(database_uri) + store = Store(database) + return store + diff --git a/managers/folder.py b/managers/folder.py index 3235361..fa934c9 100644 --- a/managers/folder.py +++ b/managers/folder.py @@ -19,7 +19,7 @@ # along with this program. If not, see . import os.path, uuid -from db import Folder, Artist, session +from db import Folder, Artist, Album, Track class FolderManager: SUCCESS = 0 @@ -30,7 +30,7 @@ class FolderManager: NO_SUCH_FOLDER = 5 @staticmethod - def get(uid): + def get(store, uid): if isinstance(uid, basestring): try: uid = uuid.UUID(uid) @@ -41,33 +41,36 @@ class FolderManager: else: return FolderManager.INVALID_ID, None - folder = Folder.query.get(uid) + folder = store.get(Folder, uid) if not folder: return FolderManager.NO_SUCH_FOLDER, None return FolderManager.SUCCESS, folder @staticmethod - def add(name, path): - if Folder.query.filter(Folder.name == name and Folder.root == True).first(): + def add(store, name, path): + if not store.find(Folder, Folder.name == name, Folder.root == True).is_empty(): return FolderManager.NAME_EXISTS path = os.path.abspath(path) if not os.path.isdir(path): return FolderManager.INVALID_PATH - folder = Folder.query.filter(Folder.path == path).first() - if folder: + if not store.find(Folder, Folder.path == path).is_empty(): return FolderManager.PATH_EXISTS - folder = Folder(root = True, name = name, path = path) - session.add(folder) - session.commit() + folder = Folder() + folder.root = True + folder.name = name + folder.path = path + + store.add(folder) + store.commit() return FolderManager.SUCCESS @staticmethod - def delete(uid): - status, folder = FolderManager.get(uid) + def delete(store, uid): + status, folder = FolderManager.get(store, uid) if status != FolderManager.SUCCESS: return status @@ -75,37 +78,37 @@ class FolderManager: return FolderManager.NO_SUCH_FOLDER # delete associated tracks and prune empty albums/artists - for artist in Artist.query.all(): - for album in artist.albums[:]: - for track in filter(lambda t: t.root_folder.id == folder.id, album.tracks): - album.tracks.remove(track) - session.delete(track) - if len(album.tracks) == 0: - artist.albums.remove(album) - session.delete(album) - if len(artist.albums) == 0: - session.delete(artist) + potentially_removed_albums = set() + for track in store.find(Track, Track.root_folder_id == folder.id): + potentially_removed_albums.add(track.album) + store.remove(track) + potentially_removed_artists = set() + for album in filter(lambda album: album.tracks.count() == 0, potentially_removed_albums): + potentially_removed_artists.add(album.artist) + store.remove(album) + for artist in filter(lambda artist: artist.albums.count() == 0, potentially_removed_artists): + store.remove(artist) def cleanup_folder(folder): for f in folder.children: cleanup_folder(f) - session.delete(folder) + store.remove(folder) cleanup_folder(folder) - session.commit() + store.commit() return FolderManager.SUCCESS @staticmethod - def delete_by_name(name): - folder = Folder.query.filter(Folder.name == name and Folder.root == True).first() + def delete_by_name(store, name): + folder = store.find(Folder, Folder.name == name, Folder.root == True).one() if not folder: return FolderManager.NO_SUCH_FOLDER - return FolderManager.delete(folder.id) + return FolderManager.delete(store, folder.id) @staticmethod - def scan(uid, scanner, progress_callback = None): - status, folder = FolderManager.get(uid) + def scan(store, uid, scanner, progress_callback = None): + status, folder = FolderManager.get(store, uid) if status != FolderManager.SUCCESS: return status diff --git a/managers/user.py b/managers/user.py index c6cc3b9..b748a29 100644 --- a/managers/user.py +++ b/managers/user.py @@ -21,7 +21,7 @@ import string, random, hashlib import uuid -from db import User, session +from db import User class UserManager: SUCCESS = 0 diff --git a/scanner.py b/scanner.py index 4c9cf90..f7a7186 100644 --- a/scanner.py +++ b/scanner.py @@ -27,11 +27,8 @@ def get_mime(ext): return mimetypes.guess_type('dummy.' + ext, False)[0] or config.get('mimetypes', ext) or 'application/octet-stream' class Scanner: - def __init__(self, session): - self.__session = session - self.__tracks = db.Track.query.all() - self.__artists = db.Artist.query.all() - self.__folders = db.Folder.query.all() + def __init__(self, store): + self.__store = store self.__added_artists = 0 self.__added_albums = 0