diff --git a/supysonic/daemon/__init__.py b/supysonic/daemon/__init__.py index 2b96a4a..0fc5c92 100644 --- a/supysonic/daemon/__init__.py +++ b/supysonic/daemon/__init__.py @@ -62,7 +62,7 @@ class Daemon(object): if extensions: extensions = extensions.split(' ') - self.__scanner = ScannerThread(args = folders, kwargs = { 'force': force, 'extensions': extensions }) + self.__scanner = ScannerThread(self.__watcher, folders, kwargs = { 'force': force, 'extensions': extensions, 'notify_watcher': False }) self.__scanner.start() def terminate(self): @@ -71,25 +71,31 @@ class Daemon(object): self.__watcher.stop() class ScannerThread(Thread): - def __init__(self, *args, **kwargs): + def __init__(self, watcher, folders, *args, **kwargs): super(ScannerThread, self).__init__(*args, **kwargs) + self.__watcher = watcher + self.__folders = folders self.__scanned = {} def run(self): - force = self._kwargs.get('force', False) - extensions = self._kwargs.get('extensions') - s = Scanner(force = force, extensions = extensions) + s = Scanner(*self._args, **self._kwargs) with db_session: - if self._args: - folders = Folder.select(lambda f: f.root and f.name in self._args) + if self.__folders: + folders = Folder.select(lambda f: f.root and f.name in self.__folders) else: folders = Folder.select(lambda f: f.root) for f in folders: name = f.name - logger.info('Scanning %s', name) - s.scan(f, lambda x: self.__scanned.update({ name: x })) + if self.__watcher is not None: + self.__watcher.remove_folder(f) + try: + logger.info('Scanning %s', name) + s.scan(f, lambda x: self.__scanned.update({ name: x })) + finally: + if self.__watcher is not None: + self.__watcher.add_folder(f) s.finish() diff --git a/supysonic/scanner.py b/supysonic/scanner.py index d00b76c..895edc2 100644 --- a/supysonic/scanner.py +++ b/supysonic/scanner.py @@ -16,6 +16,8 @@ from datetime import datetime from pony.orm import db_session from .covers import find_cover_in_folder, CoverFile +from .daemon.exceptions import DaemonUnavailableError +from .daemon.client import DaemonClient from .db import Folder, Artist, Album, Track, User from .db import StarredFolder, StarredArtist, StarredAlbum, StarredTrack from .db import RatingFolder, RatingTrack @@ -34,11 +36,12 @@ class Stats(object): self.errors = [] class Scanner: - def __init__(self, force = False, extensions = None): + def __init__(self, force = False, extensions = None, notify_watcher = True): if extensions is not None and not isinstance(extensions, list): raise TypeError('Invalid extensions type') self.__force = force + self.__notify = notify_watcher self.__stats = Stats() self.__extensions = extensions @@ -47,6 +50,11 @@ class Scanner: if not isinstance(folder, Folder): raise TypeError('Expecting Folder instance, got ' + str(type(folder))) + if self.__notify: + daemon = DaemonClient() + try: daemon.remove_watched_folder(folder.path) + except DaemonUnavailableError: pass + # Scan new/updated files to_scan = [ folder.path ] scanned = 0 @@ -96,6 +104,10 @@ class Scanner: folder.last_scan = int(time.time()) + if self.__notify: + try: daemon.add_watched_folder(folder.path) + except DaemonUnavailableError: pass + @db_session def finish(self): self.__stats.deleted.albums = Album.prune() diff --git a/supysonic/watcher.py b/supysonic/watcher.py index 325d767..60b4610 100644 --- a/supysonic/watcher.py +++ b/supysonic/watcher.py @@ -224,6 +224,12 @@ class ScannerProcessingQueue(Thread): self.__timer = Timer(self.__timeout, self.__wakeup) self.__timer.start() + def unschedule_paths(self, basepath): + with self.__cond: + for path in self.__queue.keys(): + if path.startswith(basepath): + del self.__queue[path] + def __wakeup(self): with self.__cond: self.__cond.notify() @@ -262,10 +268,18 @@ class SupysonicWatcher(object): watch = self.__observer.schedule(self.__handler, path, recursive = True) self.__folders[path] = watch - def remove_folder(self, path): + def remove_folder(self, folder): + if isinstance(folder, Folder): + path = folder.path + elif isinstance(folder, strtype): + path = folder + else: + raise TypeError('Expecting string or Folder, got ' + str(type(folder))) + logger.info("Unscheduling watcher for %s", path) self.__observer.unschedule(self.__folders[path]) del self.__folders[path] + self.__queue.unschedule_paths(path) def start(self): self.__queue = ScannerProcessingQueue(self.__delay)