def add(self, uris): """ Add uris to collection """ GLib.idle_add(App().window.container.progress.pulse, True) walk_uris = list(uris) while walk_uris: uri = walk_uris.pop(0) if not uri: continue try: f = Gio.File.new_for_uri(uri) file_type = f.query_file_type(Gio.FileQueryInfoFlags.NONE, None) if file_type == Gio.FileType.DIRECTORY: infos = f.enumerate_children( "standard::name,standard::type,standard::is-hidden", Gio.FileQueryInfoFlags.NONE, None) for info in infos: f = infos.get_child(info) child_uri = f.get_uri() if info.get_is_hidden(): continue elif info.get_file_type() == Gio.FileType.DIRECTORY: walk_uris.append(child_uri) else: if is_audio(f): self.__add_file(f) elif is_audio(f): self.__add_file(f) else: Logger.info("Not an audio file %s" % uri) except Exception as e: Logger.error("CollectionImporter::add(): %s" % e) GLib.idle_add(App().window.container.progress.pulse, False)
def _on_dir_changed(self, monitor, changed_file, other_file, event): """ Prepare thread to handle changes """ # Stop collection scanner and wait if Lp().scanner.is_locked(): Lp().scanner.stop() GLib.timeout_add(1000, self._on_dir_changed, monitor, changed_file, other_file, event) # Run update delayed else: path = changed_file.get_path() # If a directory, monitor it if os.path.exists(path) and\ changed_file.query_file_type(Gio.FileQueryInfoFlags.NONE, None) == Gio.FileType.DIRECTORY: self.add_monitor(path) # If not an audio file, exit elif not is_audio(changed_file): return if self._timeout is not None: GLib.source_remove(self._timeout) self._timeout = None self._timeout = GLib.timeout_add(self._TIMEOUT, self._run_collection_update)
def _get_objects_for_paths(self, paths): """ Return all tracks/dirs for paths @param paths as string @return ([tracks path], [dirs path], track count) """ tracks = [] track_dirs = list(paths) count = 0 for path in paths: for root, dirs, files in os.walk(path): # Add dirs for d in dirs: track_dirs.append(os.path.join(root, d)) # Add files for name in files: filepath = os.path.join(root, name) try: f = Gio.File.new_for_path(filepath) if is_pls(f): pass elif is_audio(f): tracks.append(filepath) count += 1 else: debug("%s not detected as a music file" % filepath) except Exception as e: print("CollectionScanner::_get_objects_for_paths: %s" % e) return (tracks, track_dirs, count)
def __on_dir_changed(self, monitor, changed_file, other_file, event): """ Prepare thread to handle changes """ update = False # Stop collection scanner and wait if Lp().scanner.is_locked(): Lp().scanner.stop() GLib.timeout_add(self.__TIMEOUT, self.__on_dir_changed, monitor, changed_file, other_file, event) # Run update delayed else: uri = changed_file.get_uri() d = Gio.File.new_for_uri(uri) if d.query_exists(): # If a directory, monitor it if changed_file.query_file_type( Gio.FileQueryInfoFlags.NONE, None) == Gio.FileType.DIRECTORY: self.add_monitor(uri) # If not an audio file, exit elif is_audio(changed_file): update = True else: update = True if update: if self.__timeout is not None: GLib.source_remove(self.__timeout) self.__timeout = None self.__timeout = GLib.timeout_add(self.__TIMEOUT, self.__run_collection_update)
def __get_objects_for_paths(self, paths): """ Return all tracks/dirs for paths @param paths as string @return ([tracks path], [dirs path], track count) """ tracks = [] track_dirs = list(paths) for path in paths: for root, dirs, files in os.walk(path, followlinks=True): # Add dirs for d in dirs: track_dirs.append(os.path.join(root, d)) # Add files for name in files: path = os.path.join(root, name) uri = GLib.filename_to_uri(path) try: f = Gio.File.new_for_uri(uri) if is_pls(f): pass elif is_audio(f): tracks.append(uri) else: debug("%s not detected as a music file" % uri) except Exception as e: print( "CollectionScanner::__get_objects_for_paths: %s" % e) return (tracks, track_dirs)
def __on_dir_changed(self, monitor, changed_file, other_file, event): """ Prepare thread to handle changes """ update = False # Stop collection scanner and wait if Lp().scanner.is_locked(): Lp().scanner.stop() GLib.timeout_add(self.__TIMEOUT, self.__on_dir_changed, monitor, changed_file, other_file, event) # Run update delayed else: uri = changed_file.get_uri() d = Lio.File.new_for_uri(uri) if d.query_exists(): # If a directory, monitor it if changed_file.query_file_type( Gio.FileQueryInfoFlags.NONE, None) == Gio.FileType.DIRECTORY: self.add_monitor(uri) # If not an audio file, exit elif is_audio(changed_file): update = True else: update = True if update: if self.__timeout is not None: GLib.source_remove(self.__timeout) self.__timeout = None self.__timeout = GLib.timeout_add(self.__TIMEOUT, self.__run_collection_update)
def _scan(self, paths): sql = Objects.db.get_cursor() tracks = Objects.tracks.get_paths(sql) new_tracks = [] count = 0 for path in paths: for root, dirs, files in os.walk(path): for name in files: f = Gio.File.new_for_path(os.path.join(root, name)) if is_audio(f): new_tracks.append(os.path.join(root, name)) count += 1 i = 0 for filepath in new_tracks: GLib.idle_add(self._update_progress, i, count) mtime = int(os.path.getmtime(filepath)) try: if filepath not in tracks: infos = Objects.player.get_infos(filepath) if infos is not None: self._add2db(filepath, mtime, infos, sql) else: # Update tags by removing song and readd it if mtime != self._mtimes[filepath]: track_id = Objects.tracks.get_id_by_path(filepath, sql) album_id = Objects.tracks.get_album_id(track_id, sql) Objects.tracks.remove(filepath, sql) self._clean_compilation(album_id, sql) infos = Objects.player.get_infos(filepath) if infos is not None: self._add2db(filepath, mtime, infos, sql) tracks.remove(filepath) except Exception as e: print(ascii(filepath)) print("CollectionScanner::_scan(): %s" % e) i += 1 if self._smooth: sleep(0.001) # Clean deleted files if i > 0: for filepath in tracks: track_id = Objects.tracks.get_id_by_path(filepath, sql) album_id = Objects.tracks.get_album_id(track_id, sql) Objects.tracks.remove(filepath, sql) self._clean_compilation(album_id, sql) Objects.tracks.clean(sql) Objects.albums.sanitize(sql) self._restore_popularities(sql) sql.commit() sql.close() GLib.idle_add(self._finish)
def _on_files_opened(self, app, files, hint, data): self._external_files = [] for f in files: if self._parser.parse(f.get_uri(), False) ==\ TotemPlParser.ParserResult.SUCCESS: self._parsing += 1 elif is_audio(f): self._external_files.append(f.get_path()) if not Objects.window.is_visible(): self.do_activate() if self._parsing == 0: Objects.window.load_external(self._external_files)
def __get_objects_for_uris(self, uris): """ Return all tracks/dirs for uris @param uris as string @return (track uri as [str], track dirs as [str], ignore dirs as [str]) """ tracks = [] ignore_dirs = [] track_dirs = list(uris) walk_uris = list(uris) while walk_uris: uri = walk_uris.pop(0) empty = True try: d = Gio.File.new_for_uri(uri) infos = d.enumerate_children( "standard::name,standard::type,standard::is-hidden", Gio.FileQueryInfoFlags.NONE, None) except Exception as e: print("CollectionScanner::__get_objects_for_uris():", e) ignore_dirs.append(uri) continue for info in infos: f = infos.get_child(info) child_uri = f.get_uri() empty = False if info.get_is_hidden(): continue elif info.get_file_type() == Gio.FileType.DIRECTORY: track_dirs.append(child_uri) walk_uris.append(child_uri) else: try: f = Gio.File.new_for_uri(child_uri) if is_pls(f): pass elif is_audio(f): tracks.append(child_uri) else: debug("%s not detected as a music file" % child_uri) except Exception as e: print( "CollectionScanner::" "__get_objects_for_uris():", e) # If a root uri is empty # Ensure user is not doing something bad if empty and uri in uris: ignore_dirs.append(uri) return (tracks, track_dirs, ignore_dirs)
def __get_objects_for_uris(self, uris): """ Return all tracks/dirs for uris @param uris as string @return (track uri as [str], track dirs as [str]) """ tracks = [] files = [] track_dirs = [] walk_uris = list(uris) while walk_uris: uri = walk_uris.pop(0) try: # Directly add files, walk through directories f = Gio.File.new_for_uri(uri) info = f.query_info( "standard::name,standard::type,standard::is-hidden", Gio.FileQueryInfoFlags.NONE, None) if info.get_file_type() == Gio.FileType.DIRECTORY: track_dirs.append(uri) infos = f.enumerate_children( "standard::name,standard::type,standard::is-hidden", Gio.FileQueryInfoFlags.NONE, None) for info in infos: f = infos.get_child(info) child_uri = f.get_uri() if info.get_is_hidden(): continue elif info.get_file_type() == Gio.FileType.DIRECTORY: track_dirs.append(child_uri) walk_uris.append(child_uri) else: files.append(f) else: files.append(f) except Exception as e: Logger.error( "CollectionScanner::__get_objects_for_uris(): %s" % e) for f in files: try: if is_pls(f): App().playlists.import_tracks(f) elif is_audio(f): tracks.append(f.get_uri()) else: Logger.debug("%s not detected as a music file" % f.get_uri()) except Exception as e: Logger.error( "CollectionScanner::__get_objects_for_uris(): %s" % e) return (tracks, track_dirs)
def __get_objects_for_uris(self, uris): """ Return all tracks/dirs for uris @param uris as string @return (track uri as [str], track dirs as [str], ignore dirs as [str]) """ tracks = [] ignore_dirs = [] track_dirs = list(uris) walk_uris = list(uris) while walk_uris: uri = walk_uris.pop(0) empty = True try: d = Lio.File.new_for_uri(uri) infos = d.enumerate_children( 'standard::name,standard::type,standard::is-hidden', Gio.FileQueryInfoFlags.NONE, None) except Exception as e: print("CollectionScanner::__get_objects_for_uris():", e) continue for info in infos: f = infos.get_child(info) child_uri = f.get_uri() empty = False if info.get_is_hidden(): continue elif info.get_file_type() == Gio.FileType.DIRECTORY: track_dirs.append(child_uri) walk_uris.append(child_uri) else: try: f = Lio.File.new_for_uri(child_uri) if is_pls(f): pass elif is_audio(f): tracks.append(child_uri) else: debug("%s not detected as a music file" % uri) except Exception as e: print("CollectionScanner::" "__get_objects_for_uris():", e) # If a root uri is empty # Ensure user is not doing something bad if empty and uri in uris: ignore_dirs.append(uri) return (tracks, track_dirs, ignore_dirs)
def __scan_to_handle(self, uri): """ Check if file has to be handle by scanner @param f as Gio.File @return bool """ try: f = Gio.File.new_for_uri(uri) # Scan file if App().settings.get_value("import-playlists") and is_pls(f): # Handle playlist App().playlists.import_tracks(f) elif is_audio(f): return True else: Logger.debug("Not detected as a music file: %s" % f.get_uri()) except Exception as e: Logger.error("CollectionScanner::__scan_to_handle(): %s" % e) return False
def __on_dir_changed(self, monitor, changed_file, other_file, event): """ Stop collection scanner if running Delayed update by default @param monitor as Gio.FileMonitor @param changed_file as Gio.File/None @param other_file as Gio.File/None @param event as Gio.FileMonitorEvent """ if self.__disable_timeout_id is not None: return changed_uri = changed_file.get_uri() # Do not monitor our self if changed_uri in self.__monitors.keys() and\ self.__monitors[changed_uri] == monitor: return # Ignore non audio/dir if changed_file.query_exists() and\ not is_audio(changed_file) and\ changed_file.query_file_type(Gio.FileQueryInfoFlags.NONE, None) != Gio.FileType.DIRECTORY: return # Stop collection scanner and wait if App().scanner.is_locked(): App().scanner.stop() GLib.timeout_add(self.__TIMEOUT, self.__on_dir_changed, monitor, changed_file, other_file, event) # Run update delayed else: if self.__collection_timeout_id is not None: GLib.source_remove(self.__collection_timeout_id) if changed_file.has_parent(): uris = [changed_file.get_parent().get_uri()] else: uris = [changed_uri] self.__collection_timeout_id = GLib.timeout_add( self.__TIMEOUT, self.__run_collection_update, uris)
def __get_objects_for_paths(self, paths): """ Return all tracks/dirs for paths @param paths as string @return (track path as [str], track dirs as [str], ignore dirs as [str]) """ tracks = [] ignore_dirs = [] track_dirs = list(paths) for path in paths: tracks_for_path = [] for root, dirs, files in os.walk(path, followlinks=True): # Add dirs for d in dirs: track_dirs.append(os.path.join(root, d)) # Add files for name in files: path = os.path.join(root, name) uri = GLib.filename_to_uri(path) try: f = Gio.File.new_for_uri(uri) if is_pls(f): pass elif is_audio(f): tracks_for_path.append(uri) else: debug("%s not detected as a music file" % uri) except Exception as e: print("CollectionScanner::__get_objects_for_paths: %s" % e) # If a path is empty # Ensure user is not doing something bad if not tracks_for_path: ignore_dirs.append(GLib.filename_to_uri(path)) tracks += tracks_for_path return (tracks, track_dirs, ignore_dirs)
def __on_command_line(self, app, app_cmd_line): """ Handle command line @param app as Gio.Application @param options as Gio.ApplicationCommandLine """ try: args = app_cmd_line.get_arguments() options = app_cmd_line.get_options_dict() if options.contains("debug"): self.debug = True # We are forced to enable scrobblers here if we want full debug if not self.scrobblers: if LastFM is not None: self.scrobblers = [LastFM("lastfm"), LastFM("librefm")] self.load_listenbrainz() if options.contains("set-rating"): value = options.lookup_value("set-rating").get_string() try: value = min(max(0, int(value)), 5) if self.player.current_track.id is not None: self.player.current_track.set_rate(value) except Exception as e: Logger.error("Application::__on_command_line(): %s", e) pass elif options.contains("play-pause"): self.player.play_pause() elif options.contains("stop"): self.player.stop() ## anhsirk0 edits elif options.contains("set-next"): try: track_id = int(args[1]) try: self.player.append_to_queue(track_id, notify=True) except: pass except: pass ## anhsirk0 edits ends elif options.contains("play-ids"): try: value = options.lookup_value("play-ids").get_string() ids = value.split(";") tracks = [] for id in ids: if id[0:2] == "a:": album = Album(int(id[2:])) tracks += album.tracks else: tracks.append(Track(int(id[2:]))) self.player.load(tracks[0]) self.player.populate_playlist_by_tracks( tracks, [Type.SEARCH]) except Exception as e: Logger.error("Application::__on_command_line(): %s", e) pass elif options.contains("next"): self.player.next() elif options.contains("prev"): self.player.prev() elif options.contains("emulate-phone"): self.__window.toolbar.end.devices_popover.add_fake_phone() elif len(args) > 1: uris = [] pls = [] for uri in args[1:]: try: uri = GLib.filename_to_uri(uri) except: pass f = Gio.File.new_for_uri(uri) if not f.query_exists(): uri = GLib.filename_to_uri( "%s/%s" % (GLib.get_current_dir(), uri)) f = Gio.File.new_for_uri(uri) if is_audio(f): uris.append(uri) elif is_pls(f): pls.append(uri) else: info = f.query_info(Gio.FILE_ATTRIBUTE_STANDARD_TYPE, Gio.FileQueryInfoFlags.NONE, None) if info.get_file_type() == Gio.FileType.DIRECTORY: uris.append(uri) if pls: from gi.repository import TotemPlParser parser = TotemPlParser.Parser.new() parser.connect("entry-parsed", self.__on_entry_parsed, uris) parser.parse_async(uri, True, None, self.__on_parse_finished, uris) else: self.__on_parse_finished(None, None, uris) elif self.__window is not None: if not self.__window.is_visible(): self.__window.present() self.player.emit("status-changed") self.player.emit("current-changed") Gdk.notify_startup_complete() except Exception as e: Logger.error("Application::__on_command_line(): %s", e) return 0
def _scan(self, paths, smooth): sql = Objects.db.get_cursor() tracks = Objects.tracks.get_paths(sql) self._is_empty = len(tracks) == 0 # Clear cover cache if not smooth: Objects.art.clean_all_cache(sql) new_tracks = [] count = 0 for path in paths: for root, dirs, files in os.walk(path): for name in files: filepath = os.path.join(root, name) f = Gio.File.new_for_path(filepath) if is_audio(f): new_tracks.append(filepath) count += 1 else: debug("%s not detected as a music file" % filepath) i = 0 for filepath in new_tracks: if not self._in_thread: sql.close() self._is_locked = False return GLib.idle_add(self._update_progress, i, count) mtime = int(os.path.getmtime(filepath)) try: if filepath not in tracks: infos = Objects.player.get_infos(filepath) if infos is not None: debug("Adding file: %s" % filepath) self._add2db(filepath, mtime, infos, False, sql) else: print("Can't get infos for ", filepath) else: # Update tags by removing song and readd it if mtime != self._mtimes[filepath]: track_id = Objects.tracks.get_id_by_path(filepath, sql) album_id = Objects.tracks.get_album_id(track_id, sql) Objects.tracks.remove(filepath, sql) self._clean_compilation(album_id, sql) infos = Objects.player.get_infos(filepath) if infos is not None: debug("Adding file: %s" % filepath) self._add2db(filepath, mtime, infos, False, sql) else: print("Can't get infos for ", filepath) tracks.remove(filepath) except Exception as e: print(ascii(filepath)) print("CollectionScanner::_scan(): %s" % e) i += 1 if smooth: sleep(0.001) # Clean deleted files if i > 0: for filepath in tracks: track_id = Objects.tracks.get_id_by_path(filepath, sql) album_id = Objects.tracks.get_album_id(track_id, sql) Objects.tracks.remove(filepath, sql) self._clean_compilation(album_id, sql) Objects.tracks.clean(sql) Objects.albums.search_compilations(False, sql) self._restore_popularities(sql) self._restore_mtimes(sql) sql.commit() sql.close() GLib.idle_add(self._finish)
settings.set_property("gtk-application-prefer-dark-theme", dark) - self._parser = TotemPlParser.Parser.new() - self._parser.connect("entry-parsed", self._on_entry_parsed) - self._parser.connect("playlist-ended", self._on_playlist_ended) self._parsing = 0 self.add_action(Objects.settings.create_action('shuffle')) @@ -140,10 +137,7 @@ class Application(Gtk.Application): def do_open(self, files, hint, data): self._external_files = [] for f in files: - if self._parser.parse(f.get_uri(), False) ==\ - TotemPlParser.ParserResult.SUCCESS: - self._parsing += 1 - elif is_audio(f): + if is_audio(f): self._external_files.append(f.get_path()) if not Objects.window.is_visible(): self.do_activate() @@ -185,29 +179,6 @@ class Application(Gtk.Application): # PRIVATE # ####################### """ - Add playlist entry to external files - @param parser as TotemPlParser.Parser - @param track uri as str - @param metadata as GLib.HastTable - """ - def _on_entry_parsed(self, parser, uri, metadata): - # Check if it's really a file uri
def __on_command_line(self, app, app_cmd_line): """ Handle command line @param app as Gio.Application @param options as Gio.ApplicationCommandLine """ args = app_cmd_line.get_arguments() options = app_cmd_line.get_options_dict() if options.contains("debug"): self.debug = True if options.contains("set-rating"): value = options.lookup_value("set-rating").get_string() try: value = min(max(0, int(value)), 5) if self.player.current_track.id is not None: self.player.current_track.set_rate(value) except Exception as e: Logger.error("Application::__on_command_line(): %s", e) pass elif options.contains("play-pause"): self.player.play_pause() elif options.contains("stop"): self.player.stop() elif options.contains("play-ids"): try: value = options.lookup_value("play-ids").get_string() ids = value.split(";") tracks = [] for id in ids: if id[0:2] == "a:": album = Album(int(id[2:])) tracks += album.tracks else: tracks.append(Track(int(id[2:]))) self.player.load(tracks[0]) self.player.populate_playlist_by_tracks(tracks, [Type.SEARCH]) except Exception as e: Logger.error("Application::__on_command_line(): %s", e) pass elif options.contains("next"): self.player.next() elif options.contains("prev"): self.player.prev() elif options.contains("emulate-phone"): self.window.container.add_fake_phone() elif len(args) > 1: uris = [] pls = [] for uri in args[1:]: try: uri = GLib.filename_to_uri(uri) except: pass f = Gio.File.new_for_uri(uri) if is_audio(f): uris.append(uri) elif is_pls(f): pls.append(uri) if pls: from gi.repository import TotemPlParser parser = TotemPlParser.Parser.new() parser.connect("entry-parsed", self.__on_entry_parsed, uris) parser.parse_async(uri, True, None, self.__on_parse_finished, uris) else: self.__on_parse_finished(None, None, uris) elif self.window is not None: self.window.setup() if not self.window.is_visible(): # https://bugzilla.gnome.org/show_bug.cgi?id=766284 monotonic_time = int(GLib.get_monotonic_time() / 1000) self.window.present_with_time(monotonic_time) self.player.emit("status-changed") self.player.emit("current-changed") Gdk.notify_startup_complete() return 0