def _on_delete_confirm(self, button): """ Delete tracks after confirmation @param button as Gtk.Button """ selection = self.__view.get_selection() selected = selection.get_selected_rows()[1] rows = [] for item in selected: rows.append(Gtk.TreeRowReference.new(self.__model, item)) tracks = [] for row in rows: iterator = self.__model.get_iter(row.get_path()) track = Track(self.__model.get_value(iterator, 3)) tracks.append(track) if self.__playlist_id == Type.LOVED and Lp().lastfm is not None: if track.album.artist_id == Type.COMPILATIONS: artist_name = ", ".join(track.artists) else: artist_name = ", ".join(track.album.artists) helper = TaskHelper() helper.run(Lp().lastfm.unlove, artist_name, track.name) self.__model.remove(iterator) Lp().playlists.remove_tracks(self.__playlist_id, tracks) self.__infobar.hide() self.__unselectall()
def _on_new_btn_clicked(self, button): """ Create a new playlist based on search @param button as Gtk.Button """ helper = TaskHelper() helper.run(self.__new_playlist)
def populate(self): """ populate view if needed """ if len(self.__model) == 0: helper = TaskHelper() helper.run(self.__append_tracks, callback=(self.__append_track,))
def __on_track_moved(self, widget, dst, src, up): """ Move track from src to row Recalculate track position @param widget as TracksWidget @param dst as int @param src as int @param up as bool """ def update_playlist(): # Save playlist in db only if one playlist visible if len(self.__playlist_ids) == 1 and self.__playlist_ids[0] >= 0: Lp().playlists.clear(self.__playlist_ids[0], False) tracks = [] for track_id in self.__tracks_left + self.__tracks_right: tracks.append(Track(track_id)) Lp().playlists.add_tracks(self.__playlist_ids[0], tracks, False) if not (set(self.__playlist_ids) - set(Lp().player.get_user_playlist_ids())): Lp().player.update_user_playlist(self.__tracks_left + self.__tracks_right) (src_widget, dst_widget, src_index, dst_index) = \ self.__move_track(dst, src, up) self.__update_tracks() self.__update_position() self.__update_headers() self.__tracks_widget_left.update_indexes(1) self.__tracks_widget_right.update_indexes(len(self.__tracks_left) + 1) helper = TaskHelper() helper.run(update_playlist)
def __connect(self, full_sync=False): """ Connect service @param full_sync as bool @thread safe """ if self.__goa is not None or (self.__password != "" and self.__login != ""): self.__is_auth = True else: self.__is_auth = False try: self.session_key = "" self.__check_for_proxy() if self.__goa is not None: self.session_key = self.__goa.call_get_access_token_sync( None)[0] else: skg = SessionKeyGenerator(self) self.session_key = skg.get_session_key(username=self.__login, password_hash=md5( self.__password)) if full_sync: helper = TaskHelper() helper.run(self.__populate_loved_tracks) except Exception as e: debug("LastFM::__connect(): %s" % e) self.__is_auth = False
def __remove_from_playlist(self, action, variant, playlist_id): """ Del from playlist @param SimpleAction @param GVariant @param object id as int @param is album as bool @param playlist id as int """ def remove(playlist_id): tracks = [] if isinstance(self._object, Album): track_ids = Lp().albums.get_track_ids(self._object.id, self._object.genre_ids, self._object.artist_ids) for track_id in track_ids: tracks.append(Track(track_id)) else: tracks = [Track(self._object.id)] Lp().playlists.remove_tracks(playlist_id, tracks) if playlist_id in Lp().player.get_user_playlist_ids(): Lp().player.update_user_playlist( Lp().playlists.get_track_ids(playlist_id)) helper = TaskHelper() helper.run(remove, playlist_id)
def get(self, search_items, cancellable, callback): """ Get track for name @param search_items as [str] @param cancellable as Gio.Cancellable @param callback as callback """ helper = TaskHelper() helper.run(self.__get, search_items, cancellable, callback=callback)
def cache_artists_info(self): """ Cache info for all artists """ if self.__cache_artists_running: return self.__cache_artists_running = True helper = TaskHelper() helper.run(self.__cache_artists_info)
def __update_db(self, action=None, param=None): """ Search for new music @param action as Gio.SimpleAction @param param as GLib.Variant """ if self.window: helper = TaskHelper() helper.run(self.art.clean_all_cache) self.window.update_db()
def populate(self): """ Populate view with tracks from playlist """ Lp().player.set_radios(self.__radios_manager.get()) if Lp().player.current_track.id == Type.RADIOS: Lp().player.set_next() # We force next update Lp().player.set_prev() # We force prev update helper = TaskHelper() helper.run(self.__get_radios, callback=(self.__on_get_radios, ))
def now_playing(self, artist, album, title, duration): """ Now playing track @param artist as str @param title as str @param album as str @param duration as int """ if get_network_available() and\ self.__is_auth and Secret is not None: helper = TaskHelper() helper.run(self.__now_playing, artist, album, title, duration)
def cache_album_art(self, album_id): """ Download album artwork @param album id as int """ if album_id in self.__albums_history: return if get_network_available(): self.__albums_queue.append(album_id) if not self.__in_albums_download: helper = TaskHelper() helper.run(self.__cache_albums_art)
def connect(self, full_sync=False, callback=None, *args): """ Connect service @param full_sync as bool @param callback as function """ if self.__goa is not None: helper = TaskHelper() helper.run(self.__connect, (full_sync, )) elif get_network_available(): from lollypop.helper_passwords import PasswordsHelper helper = PasswordsHelper() helper.get(self.__name, self.__on_get_password, callback, *args)
def do_scrobble(self, artist, album, title, timestamp): """ Scrobble track @param artist as str @param title as str @param album as str @param timestamp as int @param duration as int """ if get_network_available() and\ self.__is_auth and Secret is not None: helper = TaskHelper() helper.run(self.__scrobble, artist, album, title, timestamp)
def save_album_artwork(self, data, album_id): """ Save data for album id @param data as bytes @param album id as int """ try: album = Album(album_id) arturi = None save_to_tags = Lp().settings.get_value("save-to-tags") uri_count = Lp().albums.get_uri_count(album.uri) filename = self.get_album_cache_name(album) + ".jpg" if save_to_tags: helper = TaskHelper() helper.run(self.__save_artwork_tags, data, album) store_path = self._STORE_PATH + "/" + filename if album.uri == "" or is_readonly(album.uri): arturi = GLib.filename_to_uri(store_path) # Many albums with same path, suffix with artist_album name elif uri_count > 1: arturi = album.uri + "/" + filename favorite_uri = album.uri + "/" + self.__favorite favorite = Gio.File.new_for_uri(favorite_uri) if favorite.query_exists(): favorite.trash() else: arturi = album.uri + "/" + self.__favorite # Save cover to uri dst = Gio.File.new_for_uri(arturi) if not save_to_tags or dst.query_exists(): bytes = GLib.Bytes(data) stream = Gio.MemoryInputStream.new_from_bytes(bytes) bytes.unref() pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale( stream, ArtSize.MONSTER, ArtSize.MONSTER, True, None) stream.close() pixbuf.savev(store_path, "jpeg", ["quality"], [str(Lp().settings.get_value( "cover-quality").get_int32())]) dst = Gio.File.new_for_uri(arturi) src = Gio.File.new_for_path(store_path) src.move(dst, Gio.FileCopyFlags.OVERWRITE, None, None) self.clean_album_cache(album) GLib.idle_add(self.album_artwork_update, album.id) except Exception as e: print("Art::save_album_artwork(): %s" % e)
def __on_get_password(self, attributes, password, name, callback, *args): """ Set password label @param attributes as {} @param password as str @param name as str @param callback as function """ if attributes is None: return self.__login = attributes["login"] self.__password = password if get_network_available(): helper = TaskHelper() helper.run(self.__connect, (), callback, *args)
def __on_drag_data_received(self, widget, context, x, y, data, info, time): """ Import values @param widget as Gtk.Widget @param context as Gdk.DragContext @param x as int @param y as int @param data as Gtk.SelectionData @param info as int @param time as int """ from lollypop.collectionimporter import CollectionImporter importer = CollectionImporter() uris = data.get_text().strip("\n").split("\r") task_helper = TaskHelper() task_helper.run(importer.add, uris, callback=(self.update_db,))
def _on_map_albums(self, widget): """ Load on map @param widget as Gtk.Grid """ self.__jump_button.show() if self.__current_track.id is None: self.__current_track = Lp().player.current_track Lp().settings.set_value("infoswitch", GLib.Variant("s", "albums")) view = widget.get_child_at(0, 0) if view is None: view = CurrentArtistAlbumsView() view.set_property("expand", True) view.show() widget.add(view) helper = TaskHelper() helper.run(view.populate, self.__current_track)
def reset_history(self): """ Reset history """ # Tracks already played self.__history = [] # Used by shuffle albums to restore playlist before shuffle self._albums_backup = [] # Albums already played self.__already_played_albums = [] # Tracks already played for albums self.__already_played_tracks = {} # If we have tracks/albums to ignore in party mode, add them helper = TaskHelper() helper.run(self.__init_party_blacklist) # Reset user playlist self._user_playlist = [] self._user_playlist_ids = []
def sync(self): """ Start synchronisation """ self._syncing = True Lp().window.progress.add(self) self.__menu.set_sensitive(False) playlists = [] if not Lp().settings.get_value("sync-albums"): self.__view.set_sensitive(False) for item in self.__model: if item[0]: playlists.append(item[2]) else: playlists.append(Type.NONE) helper = TaskHelper() helper.run(self._sync, playlists, self.__switch_mp3.get_active(), self.__switch_normalize.get_active())
def set_loved(track_id, loved): """ Add or remove track from loved playlist @param track_id @param loved Add to loved playlist if `True`; remove if `False` """ if not is_loved(track_id): if loved: Lp().playlists.add_tracks(Type.LOVED, [Track(track_id)]) if Lp().lastfm is not None: helper = TaskHelper() helper.run(_set_loved_on_lastfm, track_id, True) else: if not loved: Lp().playlists.remove_tracks(Type.LOVED, [Track(track_id)]) if Lp().lastfm is not None: helper = TaskHelper() helper.run(_set_loved_on_lastfm, track_id, False)
def __set_current_object(self, playlist_id, add): """ Add/Remove current object to playlist @param playlist id as int @param add as bool """ def set(playlist_id, add): tracks = [] if self.__is_album: track_ids = Lp().albums.get_track_ids(self.__object_id, self.__genre_ids, self.__artist_ids) for track_id in track_ids: tracks.append(Track(track_id)) else: tracks = [Track(self.__object_id)] if add: Lp().playlists.add_tracks(playlist_id, tracks) else: Lp().playlists.remove_tracks(playlist_id, tracks) helper = TaskHelper() helper.run(set, playlist_id, add)
def __save_album_artwork(self, album, data): """ Save artwork for an album @param album as Album @param data as bytes """ store_path = "%s/%s.jpg" % (ALBUMS_PATH, album.lp_album_id) save_to_tags = App().settings.get_value("save-to-tags") # Multiple albums at same path uri_count = App().albums.get_uri_count(album.uri) art_uri = album.uri + "/" + self.__favorite # Save cover to tags if save_to_tags: helper = TaskHelper() helper.run(self.__save_album_artwork_to_tags, album, data) # We need to remove favorite if exists if uri_count > 1 or save_to_tags: f = Gio.File.new_for_uri(art_uri) if f.query_exists(): f.trash() # Name file with album information if uri_count > 1: art_uri = "%s/%s.jpg" % (album.uri, album.lp_album_id) if data is None: f = Gio.File.new_for_path(store_path) fstream = f.replace(None, False, Gio.FileCreateFlags.REPLACE_DESTINATION, None) fstream.close() else: self.save_pixbuf_from_data(store_path, data) dst = Gio.File.new_for_uri(art_uri) src = Gio.File.new_for_path(store_path) src.move(dst, Gio.FileCopyFlags.OVERWRITE, None, None) self.clean_album_cache(album) self.album_artwork_update(album.id)
def __add_to_playlist(self, action, variant, playlist_id): """ Add to playlist @param SimpleAction @param GVariant @param playlist id as int """ def add(playlist_id): tracks = [] if isinstance(self._object, Album): track_ids = Lp().albums.get_track_ids(self._object.id, self._object.genre_ids, self._object.artist_ids) for track_id in track_ids: tracks.append(Track(track_id)) else: tracks = [Track(self._object.id)] Lp().playlists.add_tracks(playlist_id, tracks) if playlist_id in Lp().player.get_user_playlist_ids(): Lp().player.update_user_playlist( Lp().playlists.get_track_ids(playlist_id)) helper = TaskHelper() helper.run(add, playlist_id)
def __on_activate_link(self, link, item): """ Open new uri or just play stream @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self.__scrolled.get_vadjustment().set_value(0.0) self.populate(item.URL) elif item.TYPE == "audio": if get_network_available(): helper = TaskHelper() # Cache for toolbar helper.run(Lp().art.copy_uri_to_cache, item.LOGO, item.TEXT, Lp().window.toolbar.artsize) # Cache for MPRIS helper.run(Lp().art.copy_uri_to_cache, item.LOGO, item.TEXT, ArtSize.BIG) # Cache for miniplayer helper.run(Lp().art.copy_uri_to_cache, item.LOGO, item.TEXT, WindowSize.SMALL) Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True
def __upgrade_46(self, db): """ Populate lp_album_id/lp_track_id """ queue = LOLLYPOP_DATA_PATH + "/queue.bin" try: f = Gio.File.new_for_path(queue) f.delete(None) except: pass from lollypop.database_albums import AlbumsDatabase from lollypop.database_tracks import TracksDatabase from lollypop.utils import get_lollypop_album_id, get_lollypop_track_id albums = AlbumsDatabase(db) tracks = TracksDatabase(db) def do_migration(dialog, label, progress): GLib.idle_add(label.set_text, _("Please wait while Lollypop is updating albums")) album_ids = albums.get_ids([], [], StorageType.ALL, True) album_ids += albums.get_compilation_ids([], StorageType.ALL, True) count = len(album_ids) i = 0 for album_id in album_ids: if i % 10 == 0: GLib.idle_add(progress.set_fraction, i / count) name = albums.get_name(album_id) artists = ";".join(albums.get_artists(album_id)) lp_album_id = get_lollypop_album_id(name, artists) albums.set_lp_album_id(album_id, lp_album_id) i += 1 track_ids = tracks.get_ids(StorageType.ALL, True) count = len(track_ids) i = 0 GLib.idle_add(label.set_text, _("Please wait while Lollypop is updating tracks")) for track_id in track_ids: if i % 10 == 0: GLib.idle_add(progress.set_fraction, i / count) name = tracks.get_name(track_id) artists = ";".join(tracks.get_artists(track_id)) album_name = tracks.get_album_name(track_id) lp_track_id = get_lollypop_track_id(name, artists, album_name) tracks.set_lp_track_id(track_id, lp_track_id) i += 1 GLib.idle_add(dialog.destroy) dialog = Gtk.MessageDialog(buttons=Gtk.ButtonsType.NONE) progress = Gtk.ProgressBar.new() progress.show() label = Gtk.Label.new() label.show() grid = Gtk.Grid.new() grid.set_orientation(Gtk.Orientation.VERTICAL) grid.set_row_spacing(10) grid.show() grid.add(label) grid.add(progress) dialog.set_image(grid) helper = TaskHelper() helper.run(do_migration, dialog, label, progress) dialog.run()
class Application(Gtk.Application, ApplicationActions): """ Lollypop application: - Handle appmenu - Handle command line - Create main window """ def __init__(self, version, data_dir): """ Create application @param version as str @param data_dir as str """ Gtk.Application.__init__( self, application_id="org.gnome.Lollypop", flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) self.__version = version self.__data_dir = data_dir self.set_property("register-session", True) signal(SIGINT, lambda a, b: self.quit()) signal(SIGTERM, lambda a, b: self.quit()) # Set main thread name # We force it to current python 3.6 name, to be sure in case of # change in python current_thread().setName("MainThread") (self.__proxy_host, self.__proxy_port) = init_proxy_from_gnome() GLib.setenv("PULSE_PROP_media.role", "music", True) GLib.setenv("PULSE_PROP_application.icon_name", "org.gnome.Lollypop", True) # Ideally, we will be able to delete this once Flatpak has a solution # for SSL certificate management inside of applications. if GLib.file_test("/app", GLib.FileTest.EXISTS): paths = [ "/etc/ssl/certs/ca-certificates.crt", "/etc/pki/tls/cert.pem", "/etc/ssl/cert.pem" ] for path in paths: if GLib.file_test(path, GLib.FileTest.EXISTS): GLib.setenv("SSL_CERT_FILE", path, True) break self.cursors = {} self.debug = False self.shown_sidebar_tooltip = False self.__window = None self.__fs_window = None settings = Gio.Settings.new("org.gnome.desktop.interface") self.animations = settings.get_value("enable-animations").get_boolean() GLib.set_application_name("Lollypop") GLib.set_prgname("lollypop") self.add_main_option("play-ids", b"a", GLib.OptionFlags.NONE, GLib.OptionArg.STRING, "Play ids", None) self.add_main_option("debug", b"d", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Debug Lollypop", None) self.add_main_option("set-rating", b"r", GLib.OptionFlags.NONE, GLib.OptionArg.STRING, "Rate the current track", None) self.add_main_option("play-pause", b"t", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Toggle playback", None) self.add_main_option("stop", b"s", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Stop playback", None) self.add_main_option("next", b"n", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Go to next track", None) self.add_main_option("prev", b"p", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Go to prev track", None) self.add_main_option("emulate-phone", b"e", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Emulate a Librem phone", None) self.add_main_option("version", b"v", GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Lollypop version", None) self.connect("command-line", self.__on_command_line) self.connect("handle-local-options", self.__on_handle_local_options) self.connect("activate", self.__on_activate) self.connect("shutdown", lambda a: self.__save_state()) self.register(None) if self.get_is_remote(): Gdk.notify_startup_complete() if GLib.environ_getenv(GLib.get_environ(), "DEBUG_LEAK") is not None: import gc gc.set_debug(gc.DEBUG_LEAK) def init(self): """ Init main application """ self.settings = Settings.new() # Mount enclosing volume as soon as possible uris = self.settings.get_music_uris() try: for uri in uris: if uri.startswith("file:/"): continue f = Gio.File.new_for_uri(uri) f.mount_enclosing_volume(Gio.MountMountFlags.NONE, None, None, None) except Exception as e: Logger.error("Application::init(): %s" % e) cssProviderFile = Gio.File.new_for_uri( "resource:///org/gnome/Lollypop/application.css") cssProvider = Gtk.CssProvider() cssProvider.load_from_file(cssProviderFile) screen = Gdk.Screen.get_default() styleContext = Gtk.StyleContext() styleContext.add_provider_for_screen(screen, cssProvider, Gtk.STYLE_PROVIDER_PRIORITY_USER) self.db = Database() self.cache = CacheDatabase() self.playlists = Playlists() self.albums = AlbumsDatabase(self.db) self.artists = ArtistsDatabase(self.db) self.genres = GenresDatabase(self.db) self.tracks = TracksDatabase(self.db) self.player = Player() self.inhibitor = Inhibitor() self.scanner = CollectionScanner() self.notify = NotificationManager() self.task_helper = TaskHelper() self.art_helper = ArtHelper() self.art = Art() self.art.update_art_size() self.ws_director = DirectorWebService() self.ws_director.start() if not self.settings.get_value("disable-mpris"): from lollypop.mpris import MPRIS MPRIS(self) settings = Gtk.Settings.get_default() self.__gtk_dark = settings.get_property( "gtk-application-prefer-dark-theme") if not self.__gtk_dark: dark = self.settings.get_value("dark-ui") settings.set_property("gtk-application-prefer-dark-theme", dark) ApplicationActions.__init__(self) monitor = Gio.NetworkMonitor.get_default() if monitor.get_network_available() and\ not monitor.get_network_metered() and\ self.settings.get_value("recent-youtube-dl"): self.task_helper.run(install_youtube_dl) def do_startup(self): """ Init application """ Gtk.Application.do_startup(self) Handy.init() if self.__window is None: from lollypop.window import Window self.init() self.__window = Window() self.__window.connect("delete-event", self.__hide_on_delete) self.__window.setup() self.__window.show() self.player.restore_state() def quit(self, vacuum=False): """ Quit Lollypop @param vacuum as bool """ self.__window.container.stop() self.__window.hide() if not self.ws_director.stop(): GLib.timeout_add(100, self.quit, vacuum) return if self.settings.get_value("save-state"): self.__window.container.stack.save_history() # Then vacuum db if vacuum: self.__vacuum() self.art.clean_artwork() Gio.Application.quit(self) if GLib.environ_getenv(GLib.get_environ(), "DEBUG_LEAK") is not None: import gc gc.collect() for x in gc.garbage: s = str(x) print(type(x), "\n ", s) def fullscreen(self): """ Go fullscreen """ def on_destroy(window): self.__fs_window = None self.__window.show() if self.__fs_window is None: from lollypop.fullscreen import FullScreen self.__fs_window = FullScreen() self.__fs_window.delayed_init() self.__fs_window.show() self.__fs_window.connect("destroy", on_destroy) self.__window.hide() else: self.__fs_window.destroy() @property def proxy_host(self): """ Get proxy host @return str """ return self.__proxy_host @property def proxy_port(self): """ Get proxy port @return int """ return self.__proxy_port @property def devices(self): """ Get available devices Merge connected and known @return [str] """ devices = self.__window.toolbar.end.devices_popover.devices devices += list(self.settings.get_value("devices")) result = [] # Do not use set() + filter() because we want to keep order for device in devices: if device not in result and device != "": result.append(device) return result @property def is_fullscreen(self): """ Return True if application is fullscreen """ return self.__fs_window is not None @property def main_window(self): """ Get main window """ return self.__window @property def window(self): """ Get current application window @return Gtk.Window """ if self.__fs_window is not None: return self.__fs_window else: return self.__window @property def data_dir(self): """ Get data dir @return str """ return self.__data_dir @property def gtk_application_prefer_dark_theme(self): """ Return default gtk value @return bool """ return self.__gtk_dark @property def version(self): """ Get Lollypop version @return srt """ return self.__version ####################### # PRIVATE # ####################### def __save_state(self): """ Save player state """ if self.settings.get_value("save-state"): if self.player.current_track.id is None or\ self.player.current_track.storage_type &\ StorageType.EPHEMERAL: track_id = None else: track_id = self.player.current_track.id # Save albums context try: with open(LOLLYPOP_DATA_PATH + "/Albums.bin", "wb") as f: dump(self.player.albums, f) except Exception as e: Logger.error("Application::__save_state(): %s" % e) dump(track_id, open(LOLLYPOP_DATA_PATH + "/track_id.bin", "wb")) dump([self.player.is_playing, self.player.is_party], open(LOLLYPOP_DATA_PATH + "/player.bin", "wb")) dump(self.player.queue, open(LOLLYPOP_DATA_PATH + "/queue.bin", "wb")) if self.player.current_track.id is not None: position = self.player.position else: position = 0 dump(position, open(LOLLYPOP_DATA_PATH + "/position.bin", "wb")) self.player.stop_all() def __vacuum(self): """ VACUUM DB """ try: if self.scanner.is_locked(): self.scanner.stop() GLib.idle_add(self.__vacuum) return SqlCursor.add(self.db) self.tracks.del_non_persistent(False) self.tracks.clean(False) self.albums.clean(False) self.artists.clean(False) self.genres.clean(False) SqlCursor.remove(self.db) self.cache.clean(True) with SqlCursor(self.db) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" with SqlCursor(self.playlists) as sql: sql.isolation_level = None sql.execute("VACUUM") sql.isolation_level = "" except Exception as e: Logger.error("Application::__vacuum(): %s" % e) def __parse_uris(self, playlist_uris, audio_uris): """ Parse playlist uris @param playlist_uris as [str] @param audio_uris as [str] """ from gi.repository import TotemPlParser playlist_uri = playlist_uris.pop(0) parser = TotemPlParser.Parser.new() parser.connect("entry-parsed", self.__on_entry_parsed, audio_uris) parser.parse_async(playlist_uri, True, None, self.__on_parse_finished, playlist_uris, audio_uris) def __on_handle_local_options(self, app, options): """ Handle local options @param app as Gio.Application @param options as GLib.VariantDict """ if options.contains("version"): print("Lollypop %s" % self.__version) exit(0) return -1 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 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(";") albums = [] for id in ids: if id[0:2] == "a:": album = Album(int(id[2:])) self.player.add_album(album) albums.append(album) else: track = Track(int(id[2:])) self.player.add_album(track.album) albums.append(track.album) if albums and albums[0].tracks: self.player.load(albums[0].tracks[0]) 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: audio_uris = [] playlist_uris = [] for uri in args[1:]: parsed = urlparse(uri) if parsed.scheme not in ["http", "https"]: try: uri = GLib.filename_to_uri(uri) except: pass f = Gio.File.new_for_uri(uri) # Try ./filename if not f.query_exists(): uri = GLib.filename_to_uri( "%s/%s" % (GLib.get_current_dir(), uri)) print(uri) f = Gio.File.new_for_uri(uri) file_type = get_file_type(uri) if file_type == FileType.PLS: playlist_uris.append(uri) else: audio_uris.append(uri) if playlist_uris: self.__parse_uris(playlist_uris, audio_uris) else: self.__on_parse_finished(None, None, [], audio_uris) elif self.__window is not None: if not self.__window.is_visible(): self.__window.present() emit_signal(self.player, "status-changed") emit_signal(self.player, "current-changed") Gdk.notify_startup_complete() except Exception as e: Logger.error("Application::__on_command_line(): %s", e) return 0 def __on_parse_finished(self, source, result, playlist_uris, audio_uris): """ Play stream @param source as None @param result as Gio.AsyncResult @param uris as ([str], [str]) """ if playlist_uris: self.__parse_uris(playlist_uris, audio_uris) else: self.scanner.update(ScanType.EXTERNAL, audio_uris) def __on_entry_parsed(self, parser, uri, metadata, audio_uris): """ Add playlist entry to external files @param parser as TotemPlParser.Parser @param uri as str @param metadata as GLib.HastTable @param audio_uris as str """ audio_uris.append(uri) def __hide_on_delete(self, widget, event): """ Hide window @param widget as Gtk.Widget @param event as Gdk.Event """ # Quit if background mode is on but player is off if not self.settings.get_value("background-mode") or\ not self.player.is_playing: GLib.idle_add(self.quit, True) return widget.hide_on_delete() def __on_activate(self, application): """ Call default handler @param application as Gio.Application """ self.__window.present()