def __scan(self, uris, saved): """ Scan music collection for music files @param uris as [str] @param saved as bool @thread safe """ modifications = False if self.__history is None: self.__history = History() mtimes = App().tracks.get_mtimes() (new_tracks, new_dirs) = self.__get_objects_for_uris(uris) orig_tracks = App().tracks.get_uris() was_empty = len(orig_tracks) == 0 count = len(new_tracks) + len(orig_tracks) # Add monitors on dirs if self.__inotify is not None: for d in new_dirs: if d.startswith("file://"): self.__inotify.add_monitor(d) i = 0 # Look for new files/modified file SqlCursor.add(App().db) try: to_add = [] for uri in new_tracks: if self.__thread is None: SqlCursor.remove(App().db) return try: GLib.idle_add(self.__update_progress, i, count) f = Gio.File.new_for_uri(uri) # We do not use time::modified because many tag editors # just preserve this setting try: info = f.query_info("time::changed", Gio.FileQueryInfoFlags.NONE, None) mtime = int( info.get_attribute_as_string("time::changed")) except: # Fallback for remote fs info = f.query_info("time::modified", Gio.FileQueryInfoFlags.NONE, None) mtime = int( info.get_attribute_as_string("time::modified")) # If songs exists and mtime unchanged, continue, # else rescan if uri in orig_tracks: orig_tracks.remove(uri) i += 1 if not saved or mtime <= mtimes.get(uri, mtime + 1): i += 1 continue else: SqlCursor.allow_thread_execution(App().db) self.__del_from_db(uri) # If not saved, use 0 as mtime, easy delete on quit if not saved: mtime = 0 # On first scan, use modification time # Else, use current time elif not was_empty: mtime = int(time()) to_add.append((uri, mtime)) except Exception as e: Logger.error("CollectionScanner::__scan(mtime): %s" % e) if to_add or orig_tracks: modifications = True # Clean deleted files # Now because we need to populate history # Only if we are saving if saved: for uri in orig_tracks: i += 1 GLib.idle_add(self.__update_progress, i, count) self.__del_from_db(uri) SqlCursor.allow_thread_execution(App().db) # Add files to db for (uri, mtime) in to_add: try: Logger.debug("Adding file: %s" % uri) i += 1 GLib.idle_add(self.__update_progress, i, count) self.__add2db(uri, mtime) SqlCursor.allow_thread_execution(App().db) except Exception as e: Logger.error("CollectionScanner::__scan(add): %s, %s" % (e, uri)) except Exception as e: Logger.error("CollectionScanner::__scan(): %s" % e) SqlCursor.commit(App().db) SqlCursor.remove(App().db) GLib.idle_add(self.__finish, modifications and saved) if not saved: self.__play_new_tracks(new_tracks) del self.__history self.__history = None
def __scan_files(self, files, db_uris, scan_type): """ Scan music collection for new audio files @param files as [str] @param db_uris as [str] @param scan_type as ScanType @return new track uris as [str] @thread safe """ SqlCursor.add(App().db) i = 0 # New tracks present in collection new_tracks = [] # Get mtime of all tracks to detect which has to be updated db_mtimes = App().tracks.get_mtimes() count = len(files) + 1 try: # Scan new files for (mtime, uri) in files: # Handle a stop request if self.__thread is None and scan_type != ScanType.EPHEMERAL: raise Exception("Scan add cancelled") try: if not self.__scan_to_handle(uri): continue if mtime > db_mtimes.get(uri, 0): # If not saved, use 0 as mtime, easy delete on quit if scan_type == ScanType.EPHEMERAL: mtime = 0 # Do not use mtime if not intial scan elif db_mtimes: mtime = int(time()) Logger.debug("Adding file: %s" % uri) self.__add2db(uri, mtime) SqlCursor.allow_thread_execution(App().db) new_tracks.append(uri) except Exception as e: Logger.error("CollectionScanner:: __scan_add_files: % s" % e) i += 1 self.__update_progress(i, count) if scan_type != ScanType.EPHEMERAL and self.__thread is not None: # We need to check files are always in collections if scan_type == ScanType.FULL: collections = App().settings.get_music_uris() else: collections = None for uri in db_uris: # Handle a stop request if self.__thread is None: raise Exception("Scan del cancelled") in_collection = True if collections is not None: in_collection = False for collection in collections: if collection in uri: in_collection = True break f = Gio.File.new_for_uri(uri) if not in_collection or not f.query_exists(): self.del_from_db(uri, True) SqlCursor.allow_thread_execution(App().db) except Exception as e: Logger.warning("CollectionScanner:: __scan_files: % s" % e) SqlCursor.commit(App().db) SqlCursor.remove(App().db) return new_tracks