def init(self): """ Init main application """ 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.settings = Settings.new() ArtSize.BIG = self.settings.get_value('cover-size').get_int32() if LastFM is not None: self.lastfm = LastFM() self.db = Database() self.playlists = Playlists() # We store cursors for main thread SqlCursor.add(self.db) SqlCursor.add(self.playlists) self.albums = AlbumsDatabase() self.artists = ArtistsDatabase() self.genres = GenresDatabase() self.tracks = TracksDatabase() self.player = Player() self.scanner = CollectionScanner() self.art = Art() if not self.settings.get_value('disable-mpris'): MPRIS(self) if not self.settings.get_value('disable-mpd'): self.mpd = MpdServerDaemon( self.settings.get_value('mpd-eth').get_string(), self.settings.get_value('mpd-port').get_int32()) if not self.settings.get_value('disable-notifications'): self.notify = NotificationManager() settings = Gtk.Settings.get_default() dark = self.settings.get_value('dark-ui') settings.set_property('gtk-application-prefer-dark-theme', dark) self._parser = TotemPlParser.Parser.new() self._parser.connect('entry-parsed', self._on_entry_parsed) self.add_action(self.settings.create_action('shuffle')) self._is_fs = False
def _update_mpd_setting(self, widget, state): """ Update mpd setting @param widget as unused, state as widget state """ Lp().settings.set_value('disable-mpd', GLib.Variant('b', not state)) if Lp().mpd is None: Lp().mpd = MpdServerDaemon( Lp().settings.get_value('mpd-eth').get_string(), Lp().settings.get_value('mpd-port').get_int32()) else: Lp().mpd.quit() Lp().mpd = None
class Application(Gtk.Application): """ Lollypop application: - Handle appmenu - Handle command line - Create main window """ def __init__(self): """ Create application """ Gtk.Application.__init__( self, application_id='org.gnome.Lollypop', flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) self.cursors = {} self.window = None self.notify = None self.mpd = None self.lastfm = None self.debug = False self._externals_count = 0 self._init_proxy() GLib.set_application_name('lollypop') GLib.set_prgname('lollypop') # TODO: Remove this test later if Gtk.get_minor_version() > 12: 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.INT, "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("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.connect('command-line', self._on_command_line) self.register(None) if self.get_is_remote(): Gdk.notify_startup_complete() def init(self): """ Init main application """ 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.settings = Settings.new() ArtSize.BIG = self.settings.get_value('cover-size').get_int32() if LastFM is not None: self.lastfm = LastFM() self.db = Database() self.playlists = Playlists() # We store cursors for main thread SqlCursor.add(self.db) SqlCursor.add(self.playlists) self.albums = AlbumsDatabase() self.artists = ArtistsDatabase() self.genres = GenresDatabase() self.tracks = TracksDatabase() self.player = Player() self.scanner = CollectionScanner() self.art = Art() if not self.settings.get_value('disable-mpris'): MPRIS(self) if not self.settings.get_value('disable-mpd'): self.mpd = MpdServerDaemon( self.settings.get_value('mpd-eth').get_string(), self.settings.get_value('mpd-port').get_int32()) if not self.settings.get_value('disable-notifications'): self.notify = NotificationManager() settings = Gtk.Settings.get_default() dark = self.settings.get_value('dark-ui') settings.set_property('gtk-application-prefer-dark-theme', dark) self._parser = TotemPlParser.Parser.new() self._parser.connect('entry-parsed', self._on_entry_parsed) self.add_action(self.settings.create_action('shuffle')) self._is_fs = False def do_startup(self): """ Add startup notification and build gnome-shell menu after Gtk.Application startup """ Gtk.Application.do_startup(self) Notify.init("Lollypop") # Check locale, we want unicode! (code, encoding) = getlocale() if encoding is None or encoding != "UTF-8": builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/Unicode.ui') self.window = builder.get_object('unicode') self.window.set_application(self) self.window.show() elif not self.window: self.init() menu = self._setup_app_menu() # If GNOME/Unity, add appmenu if is_gnome() or is_unity(): self.set_app_menu(menu) self.window = Window(self) # If not GNOME add menu to toolbar if not is_gnome() and not is_unity(): self.window.setup_menu(menu) self.window.connect('delete-event', self._hide_on_delete) self.window.init_list_one() self.window.show() self.player.restore_state() def prepare_to_exit(self, action=None, param=None): """ Save window position and view """ if self.settings.get_value('save-state'): self.window.save_view_state() if self.player.current_track.id is None: track_id = -1 else: track_id = self.player.current_track.id self.settings.set_value('track-id', GLib.Variant('i', track_id)) self.player.stop() if self.window: self.window.stop_all() self.quit() def quit(self): """ Quit lollypop """ if self.mpd is not None: self.mpd.quit() if self.scanner.is_locked(): self.scanner.stop() GLib.idle_add(self.quit) return try: with SqlCursor(self.db) as sql: sql.execute('VACUUM') with SqlCursor(self.playlists) as sql: sql.execute('VACUUM') with SqlCursor(Radios()) as sql: sql.execute('VACUUM') except Exception as e: print("Application::quit(): ", e) self.window.destroy() Gst.deinit() def is_fullscreen(self): """ Return True if application is fullscreen """ return self._is_fs ####################### # PRIVATE # ####################### def _init_proxy(self): """ Init proxy setting env """ try: settings = Gio.Settings.new('org.gnome.system.proxy.http') h = settings.get_value('host').get_string() p = settings.get_value('port').get_int32() if h != '' and p != 0: os.environ['HTTP_PROXY'] = "%s:%s" % (h, p) except: pass def _on_command_line(self, app, app_cmd_line): """ Handle command line @param app as Gio.Application @param options as Gio.ApplicationCommandLine """ self._externals_count = 0 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_int32() if value > 0 and value < 6 and\ self.player.current_track.id is not None: self.player.current_track.set_popularity(value) if options.contains('play-pause'): self.player.play_pause() elif options.contains('next'): self.player.next() elif options.contains('prev'): self.player.prev() args = app_cmd_line.get_arguments() if len(args) > 1: self.player.clear_externals() for f in args[1:]: try: f = GLib.filename_to_uri(f) except: pass self._parser.parse_async(f, True, None, None) if self.window is not None and not self.window.is_visible(): self.window.setup_window() self.window.present() return 0 def _on_entry_parsed(self, parser, uri, metadata): """ Add playlist entry to external files @param parser as TotemPlParser.Parser @param track uri as str @param metadata as GLib.HastTable """ self.player.load_external(uri) if self._externals_count == 0: self.player.set_party(False) self.player.play_first_external() self._externals_count += 1 def _hide_on_delete(self, widget, event): """ Hide window @param widget as Gtk.Widget @param event as Gdk.Event """ if not self.settings.get_value('background-mode'): GLib.timeout_add(500, self.prepare_to_exit) self.scanner.stop() return widget.hide_on_delete() 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: t = Thread(target=self.art.clean_all_cache) t.daemon = True t.start() self.window.update_db() def _fullscreen(self, action=None, param=None): """ Show a fullscreen window with cover and artist informations @param action as Gio.SimpleAction @param param as GLib.Variant """ if self.window and not self._is_fs: fs = FullScreen(self, self.window) fs.connect("destroy", self._on_fs_destroyed) self._is_fs = True fs.show() def _on_fs_destroyed(self, widget): """ Mark fullscreen as False @param widget as Fullscreen """ self._is_fs = False def _settings_dialog(self, action=None, param=None): """ Show settings dialog @param action as Gio.SimpleAction @param param as GLib.Variant """ dialog = SettingsDialog() dialog.show() def _about(self, action, param): """ Setup about dialog @param action as Gio.SimpleAction @param param as GLib.Variant """ builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/AboutDialog.ui') builder.get_object('artists').set_text( _("%s artist(s)") % self.artists.count()) builder.get_object('albums').set_text( _("%s album(s)") % self.albums.count()) builder.get_object('tracks').set_text( _("%s track(s)") % self.tracks.count()) about = builder.get_object('about_dialog') about.set_transient_for(self.window) about.connect("response", self._about_response) about.show() def _help(self, action, param): """ Show help in yelp @param action as Gio.SimpleAction @param param as GLib.Variant """ try: Gtk.show_uri(None, "help:lollypop", Gtk.get_current_event_time()) except: print(_("Lollypop: You need to install yelp.")) def _about_response(self, dialog, response_id): """ Destroy about dialog when closed @param dialog as Gtk.Dialog @param response id as int """ dialog.destroy() def _setup_app_menu(self): """ Setup application menu @return menu as Gio.Menu """ builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/Appmenu.ui') menu = builder.get_object('app-menu') # TODO: Remove this test later if Gtk.get_minor_version() > 12: settingsAction = Gio.SimpleAction.new('settings', None) settingsAction.connect('activate', self._settings_dialog) self.set_accels_for_action('app.settings', ["<Control>s"]) self.add_action(settingsAction) updateAction = Gio.SimpleAction.new('update_db', None) updateAction.connect('activate', self._update_db) self.set_accels_for_action('app.update_db', ["<Control>u"]) self.add_action(updateAction) fsAction = Gio.SimpleAction.new('fullscreen', None) fsAction.connect('activate', self._fullscreen) self.set_accels_for_action('app.fullscreen', ["F11", "<Control>m"]) self.add_action(fsAction) aboutAction = Gio.SimpleAction.new('about', None) aboutAction.connect('activate', self._about) self.set_accels_for_action('app.about', ["F2"]) self.add_action(aboutAction) helpAction = Gio.SimpleAction.new('help', None) helpAction.connect('activate', self._help) self.set_accels_for_action('app.help', ["F1"]) self.add_action(helpAction) quitAction = Gio.SimpleAction.new('quit', None) quitAction.connect('activate', self.prepare_to_exit) self.set_accels_for_action('app.quit', ["<Control>q"]) self.add_action(quitAction) return menu
class Application(Gtk.Application): """ Lollypop application: - Handle appmenu - Handle command line - Create main window """ def __init__(self): """ Create application """ Gtk.Application.__init__( self, application_id='org.gnome.Lollypop', flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) self.cursors = {} self.window = None self.notify = None self.mpd = None self.lastfm = None self.debug = False self._externals_count = 0 self._init_proxy() GLib.set_application_name('lollypop') GLib.set_prgname('lollypop') # TODO: Remove this test later if Gtk.get_minor_version() > 12: 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.INT, "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("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 an Android Phone", None) self.connect('command-line', self._on_command_line) self.register(None) if self.get_is_remote(): Gdk.notify_startup_complete() def init(self): """ Init main application """ 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.settings = Settings.new() ArtSize.BIG = self.settings.get_value('cover-size').get_int32() if LastFM is not None: self.lastfm = LastFM() self.db = Database() self.playlists = Playlists() # We store cursors for main thread SqlCursor.add(self.db) SqlCursor.add(self.playlists) self.albums = AlbumsDatabase() self.artists = ArtistsDatabase() self.genres = GenresDatabase() self.tracks = TracksDatabase() self.player = Player() self.scanner = CollectionScanner() self.art = Art() if not self.settings.get_value('disable-mpris'): MPRIS(self) if not self.settings.get_value('disable-mpd'): self.mpd = MpdServerDaemon( self.settings.get_value('mpd-eth').get_string(), self.settings.get_value('mpd-port').get_int32()) if not self.settings.get_value('disable-notifications'): self.notify = NotificationManager() settings = Gtk.Settings.get_default() dark = self.settings.get_value('dark-ui') settings.set_property('gtk-application-prefer-dark-theme', dark) self._parser = TotemPlParser.Parser.new() self._parser.connect('entry-parsed', self._on_entry_parsed) self.add_action(self.settings.create_action('shuffle')) self._is_fs = False def do_startup(self): """ Add startup notification and build gnome-shell menu after Gtk.Application startup """ Gtk.Application.do_startup(self) Notify.init("Lollypop") # Check locale, we want unicode! (code, encoding) = getlocale() if encoding is None or encoding != "UTF-8": builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/Unicode.ui') self.window = builder.get_object('unicode') self.window.set_application(self) self.window.show() elif not self.window: self.init() menu = self._setup_app_menu() # If GNOME/Unity, add appmenu if is_gnome() or is_unity(): self.set_app_menu(menu) self.window = Window(self) # If not GNOME add menu to toolbar if not is_gnome() and not is_unity(): self.window.setup_menu(menu) self.window.connect('delete-event', self._hide_on_delete) self.window.init_list_one() self.window.show() self.player.restore_state() def prepare_to_exit(self, action=None, param=None): """ Save window position and view """ if self.settings.get_value('save-state'): self.window.save_view_state() if self.player.current_track.id is None: track_id = -1 else: track_id = self.player.current_track.id self.settings.set_value('track-id', GLib.Variant('i', track_id)) self.player.stop_all() if self.window: self.window.stop_all() self.quit() def quit(self): """ Quit lollypop """ if self.mpd is not None: self.mpd.quit() if self.scanner.is_locked(): self.scanner.stop() GLib.idle_add(self.quit) return try: with SqlCursor(self.db) as sql: sql.execute('VACUUM') with SqlCursor(self.playlists) as sql: sql.execute('VACUUM') with SqlCursor(Radios()) as sql: sql.execute('VACUUM') except Exception as e: print("Application::quit(): ", e) self.window.destroy() Gst.deinit() def is_fullscreen(self): """ Return True if application is fullscreen """ return self._is_fs ####################### # PRIVATE # ####################### def _init_proxy(self): """ Init proxy setting env """ try: settings = Gio.Settings.new('org.gnome.system.proxy.http') h = settings.get_value('host').get_string() p = settings.get_value('port').get_int32() if h != '' and p != 0: os.environ['HTTP_PROXY'] = "%s:%s" % (h, p) except: pass def _on_command_line(self, app, app_cmd_line): """ Handle command line @param app as Gio.Application @param options as Gio.ApplicationCommandLine """ self._externals_count = 0 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_int32() if value > 0 and value < 6 and\ self.player.current_track.id is not None: self.player.current_track.set_popularity(value) if options.contains('play-pause'): self.player.play_pause() elif options.contains('next'): self.player.next() elif options.contains('prev'): self.player.prev() elif options.contains('emulate-phone'): self.window.add_fake_phone() args = app_cmd_line.get_arguments() if len(args) > 1: self.player.clear_externals() for f in args[1:]: try: f = GLib.filename_to_uri(f) except: pass self._parser.parse_async(f, True, None, None) if self.window is not None and not self.window.is_visible(): self.window.setup_window() self.window.present() return 0 def _on_entry_parsed(self, parser, uri, metadata): """ Add playlist entry to external files @param parser as TotemPlParser.Parser @param track uri as str @param metadata as GLib.HastTable """ self.player.load_external(uri) if self._externals_count == 0: self.player.set_party(False) self.player.play_first_external() self._externals_count += 1 def _hide_on_delete(self, widget, event): """ Hide window @param widget as Gtk.Widget @param event as Gdk.Event """ if not self.settings.get_value('background-mode'): GLib.timeout_add(500, self.prepare_to_exit) self.scanner.stop() return widget.hide_on_delete() 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: t = Thread(target=self.art.clean_all_cache) t.daemon = True t.start() self.window.update_db() def _fullscreen(self, action=None, param=None): """ Show a fullscreen window with cover and artist informations @param action as Gio.SimpleAction @param param as GLib.Variant """ if self.window and not self._is_fs: fs = FullScreen(self, self.window) fs.connect("destroy", self._on_fs_destroyed) self._is_fs = True fs.show() def _on_fs_destroyed(self, widget): """ Mark fullscreen as False @param widget as Fullscreen """ self._is_fs = False def _settings_dialog(self, action=None, param=None): """ Show settings dialog @param action as Gio.SimpleAction @param param as GLib.Variant """ dialog = SettingsDialog() dialog.show() def _about(self, action, param): """ Setup about dialog @param action as Gio.SimpleAction @param param as GLib.Variant """ builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/AboutDialog.ui') artists = self.artists.count() albums = self.albums.count() tracks = self.tracks.count() builder.get_object('artists').set_text( ngettext("%d artist", "%d artists", artists) % artists) builder.get_object('albums').set_text( ngettext("%d album", "%d albums", albums) % albums) builder.get_object('tracks').set_text( ngettext("%d track", "%d tracks", tracks) % tracks) about = builder.get_object('about_dialog') about.set_transient_for(self.window) about.connect("response", self._about_response) about.show() def _help(self, action, param): """ Show help in yelp @param action as Gio.SimpleAction @param param as GLib.Variant """ try: Gtk.show_uri(None, "help:lollypop", Gtk.get_current_event_time()) except: print(_("Lollypop: You need to install yelp.")) def _about_response(self, dialog, response_id): """ Destroy about dialog when closed @param dialog as Gtk.Dialog @param response id as int """ dialog.destroy() def _setup_app_menu(self): """ Setup application menu @return menu as Gio.Menu """ builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/Appmenu.ui') menu = builder.get_object('app-menu') # TODO: Remove this test later if Gtk.get_minor_version() > 12: settingsAction = Gio.SimpleAction.new('settings', None) settingsAction.connect('activate', self._settings_dialog) self.set_accels_for_action('app.settings', ["<Control>s"]) self.add_action(settingsAction) updateAction = Gio.SimpleAction.new('update_db', None) updateAction.connect('activate', self._update_db) self.set_accels_for_action('app.update_db', ["<Control>u"]) self.add_action(updateAction) fsAction = Gio.SimpleAction.new('fullscreen', None) fsAction.connect('activate', self._fullscreen) self.set_accels_for_action('app.fullscreen', ["F11", "<Control>m"]) self.add_action(fsAction) aboutAction = Gio.SimpleAction.new('about', None) aboutAction.connect('activate', self._about) self.set_accels_for_action('app.about', ["F2"]) self.add_action(aboutAction) helpAction = Gio.SimpleAction.new('help', None) helpAction.connect('activate', self._help) self.set_accels_for_action('app.help', ["F1"]) self.add_action(helpAction) quitAction = Gio.SimpleAction.new('quit', None) quitAction.connect('activate', self.prepare_to_exit) self.set_accels_for_action('app.quit', ["<Control>q"]) self.add_action(quitAction) return menu