def get_userinfo(self): dict = {} dict['username'] = GLib.get_user_name() dict['homedir'] = GLib.get_home_dir() dict['shell'] = GLib.getenv('SHELL') dict['lang'] = GLib.getenv('LANG') return dict['username'], dict['homedir'],dict['shell'],dict['lang']
def get_base_style_scheme() -> GtkSource.StyleScheme: global base_style_scheme if base_style_scheme: return base_style_scheme env_theme = GLib.getenv('GTK_THEME') if env_theme: use_dark = env_theme.endswith(':dark') else: gtk_settings = Gtk.Settings.get_default() use_dark = gtk_settings.props.gtk_application_prefer_dark_theme # As of 3.28, the global dark theme switch is going away. if not use_dark: from meld.sourceview import MeldSourceView stylecontext = MeldSourceView().get_style_context() background_set, rgba = ( stylecontext.lookup_color('theme_bg_color')) # This heuristic is absolutely dire. I made it up. There's # literally no basis to this. if background_set and rgba.red + rgba.green + rgba.blue < 1.0: use_dark = True base_scheme_name = ( MELD_STYLE_SCHEME_DARK if use_dark else MELD_STYLE_SCHEME) manager = GtkSource.StyleSchemeManager.get_default() base_style_scheme = manager.get_scheme(base_scheme_name) return base_style_scheme
def __init__ (self, bus, engine, datadir, gettext_package): self.__config = Config(bus, engine, self.on_value_changed) ui_file = GLib.build_filenamev([datadir, "setup.ui"]) self.__builder = Gtk.Builder() self.__builder.set_translation_domain(gettext_package) self.__builder.add_from_file(ui_file) for option in options: prepare_func = getattr(self, "prepare_%s" % option["widget"]) prepare_func(option) self.__window = self.__builder.get_object("setup_dialog") self.__window.set_title(titles[engine]) try: # Pretend to be a GNOME Control Center dialog if appropriate self.gnome_cc_xid = int(GLib.getenv('GNOME_CONTROL_CENTER_XID')) self.__window.set_wmclass('gnome-control-center', 'Gnome-control-center') self.__window.set_modal(True) self.__window.connect('notify::window', self.set_transient) self.__window.set_type_hint(Gdk.WindowTypeHint.DIALOG) except: # No problem here, we're just a normal dialog pass self.__window.show()
def __init__( self, connmgr, datadir=GLib.getenv("HOME") + "/.config/chromium", namespace="org.chromium.Policies"): logging.debug( "Initializing chromium logger with namespace %s at %s" % ( namespace, datadir)) self.connmgr = connmgr self.datadir = datadir self.namespace = namespace self.local_state_path = self.datadir + "/Local State"; self.monitored_preferences = {} self.initial_bookmarks = {} self.monitored_bookmarks = {} self.bookmarks_paths = [] self.file_monitors = {} # Load policy map self.policy_map = self.load_policy_map() if self.policy_map is None: logging.error( "WARNING: ChromiumLogger can't locate policies file. Chromium/Chrome disabled.") return # Setup file monitoring for local state self._setup_local_state_file_monitor()
def __init__(self): Clip.__init__(self) self.table = EasyTable(items=( (Gtk.Label(label=_('Current user:'******'Home directory:')), Gtk.Label(label=GLib.get_home_dir())), (Gtk.Label(label=_('Shell:')), Gtk.Label(label=GLib.getenv('SHELL'))), (Gtk.Label(label=_('Language:')), Gtk.Label(label=GLib.getenv('LANG')))), xpadding=12, ypadding=2) self.add_content(self.table) self.show_all()
def __init__ (self, engine, datadir, gettext_package): self.settings = Gio.Settings("org.cangjians.ibus.%s" % engine) self.settings.connect("changed", self.on_value_changed) ui_file = GLib.build_filenamev([datadir, "setup.ui"]) self.__builder = Gtk.Builder() self.__builder.set_translation_domain(gettext_package) self.__builder.add_from_file(ui_file) for key in ("version", ): combo = self.__builder.get_object(key) active = self.__get_active_combo_index(combo.get_model(), self.settings.get_int(key)) combo.set_active(active) combo.connect("changed", self.on_combo_changed, key) setattr(self, "widget_%s" % key, combo) for key in ("include-allzh", "include-jp", "include-zhuyin", "include-symbols"): switch = self.__builder.get_object(key) switch.set_active(self.settings.get_boolean(key)) switch.connect("notify::active", self.on_switch_toggled, key) setattr(self, "widget_%s" % key, switch) self.__window = self.__builder.get_object("setup_dialog") if engine == "cangjie": title = dgettext(gettext_package, "Cangjie Preferences") elif engine == "quick": title = dgettext(gettext_package, "Quick Preferences") self.__window.set_title(title) try: # Pretend to be a GNOME Control Center dialog if appropriate self.gnome_cc_xid = int(GLib.getenv('GNOME_CONTROL_CENTER_XID')) self.__window.set_wmclass('gnome-control-center', 'Gnome-control-center') self.__window.set_modal(True) self.__window.connect('notify::window', self.set_transient) self.__window.set_type_hint(Gdk.WindowTypeHint.DIALOG) except: # No problem here, we're just a normal dialog pass self.__window.show()
def __init__(self): super(BinFileMonitor, self).__init__() self.changed_id = 0 env = GLib.getenv("PATH") if env == None: env = "/bin:/usr/bin:." self.paths = env.split(":") self.monitors = [] for path in self.paths: file = Gio.File.new_for_path(path) mon = file.monitor_directory(Gio.FileMonitorFlags.SEND_MOVED, None) mon.connect("changed", self.queue_emit_changed) self.monitors.append(mon)
def get_base_style_scheme(): global base_style_scheme if base_style_scheme: return base_style_scheme env_theme = GLib.getenv('GTK_THEME') if env_theme: use_dark = env_theme.endswith(':dark') else: gtk_settings = Gtk.Settings.get_default() use_dark = gtk_settings.props.gtk_application_prefer_dark_theme try: from Foundation import NSUserDefaults standardUserDefaults = NSUserDefaults.standardUserDefaults() if standardUserDefaults.stringForKey_("AppleInterfaceStyle") == 'Dark': use_dark = True except: pass # As of 3.28, the global dark theme switch is going away. if not use_dark: from meld.sourceview import MeldSourceView stylecontext = MeldSourceView().get_style_context() background_set, rgba = ( stylecontext.lookup_color('theme_bg_color')) # This heuristic is absolutely dire. I made it up. There's # literally no basis to this. if background_set and rgba.red + rgba.green + rgba.blue < 1.0: use_dark = True base_scheme_name = ( MELD_STYLE_SCHEME_DARK if use_dark else MELD_STYLE_SCHEME) manager = GtkSource.StyleSchemeManager.get_default() base_style_scheme = manager.get_scheme(base_scheme_name) return base_style_scheme
def get_base_style_scheme(): MELD_STYLE_SCHEME = "meld-base" MELD_STYLE_SCHEME_DARK = "meld-dark" global base_style_scheme if base_style_scheme: return base_style_scheme env_theme = GLib.getenv('GTK_THEME') if env_theme: use_dark = env_theme.endswith(':dark') else: gtk_settings = Gtk.Settings.get_default() use_dark = gtk_settings.props.gtk_application_prefer_dark_theme base_scheme_name = ( MELD_STYLE_SCHEME_DARK if use_dark else MELD_STYLE_SCHEME) manager = GtkSource.StyleSchemeManager.get_default() base_style_scheme = manager.get_scheme(base_scheme_name) return base_style_scheme
def get_base_style_scheme(): MELD_STYLE_SCHEME = "meld-base" MELD_STYLE_SCHEME_DARK = "meld-dark" global base_style_scheme if base_style_scheme: return base_style_scheme env_theme = GLib.getenv('GTK_THEME') if env_theme: use_dark = env_theme.endswith(':dark') else: gtk_settings = Gtk.Settings.get_default() use_dark = gtk_settings.props.gtk_application_prefer_dark_theme base_scheme_name = (MELD_STYLE_SCHEME_DARK if use_dark else MELD_STYLE_SCHEME) manager = GtkSource.StyleSchemeManager.get_default() base_style_scheme = manager.get_scheme(base_scheme_name) return base_style_scheme
def __on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if self.__context is not None: self.__on_button_clicked(self.__menu_button) if event.button == 3: if GLib.getenv("WAYLAND_DISPLAY") != "" and\ self.get_ancestor(Gtk.Popover) is not None: print("https://bugzilla.gnome.org/show_bug.cgi?id=774148") window = widget.get_window() if window == event.window: self.__popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self.__on_button_clicked(self.__menu_button) return True elif event.button == 2: if self._track.id in Lp().player.get_queue(): Lp().player.del_from_queue(self._track.id) else: Lp().player.append_to_queue(self._track.id)
def main(args=None): '''Entry point function. Since we're often running from within flatpak, make sure to override XDG_DATA_DIRS to include the flatpak exports too, since they don't get included by default. We use GLib.setenv here, since os.environ is only visible to Python code, but setting a variable in os.environ does not actually update the 'environ' global variable on the C side. ''' flatpak_export_share_dirs = [ os.path.join(d, 'exports', 'share') for d in EosCompanionAppService.flatpak_install_dirs() ] GLib.setenv( 'XDG_DATA_DIRS', os.pathsep.join([GLib.getenv('XDG_DATA_DIRS') or ''] + flatpak_export_share_dirs), True) logging.basicConfig( format='CompanionAppService %(levelname)s: %(message)s', level=get_log_level()) CompanionAppApplication().run(args or sys.argv)
def __on_button_press(self, widget, event): """ Popup menu for track relative to track row @param widget as Gtk.Widget @param event as Gdk.Event """ if self.__context is not None: self.__on_button_clicked(self.__menu_button) if event.button == 3: if GLib.getenv("WAYLAND_DISPLAY") != "" and\ self.get_ancestor(Gtk.Popover) is not None: print("https://bugzilla.gnome.org/show_bug.cgi?id=774148") window = widget.get_window() if window == event.window: self.__popup_menu(widget, event.x, event.y) # Happens when pressing button over menu btn else: self.__on_button_clicked(self.__menu_button) return True elif event.button == 2: if self._track.id in Lp().player.queue: Lp().player.del_from_queue(self._track.id) else: Lp().player.append_to_queue(self._track.id)
class Application(Gtk.Application): """ Eolie application: """ if GLib.getenv("XDG_DATA_HOME") is None: LOCAL_PATH = GLib.get_home_dir() + "/.local/share/eolie" else: LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") + "/eolie" __COOKIES_PATH = "%s/cookies.db" % LOCAL_PATH __FAVICONS_PATH = "%s/favicons" % LOCAL_PATH def __init__(self, extension_dir): """ Create application @param extension_dir as str """ # First check WebKit2 version if WebKit2.MINOR_VERSION < 16: exit("You need WebKit2GTK >= 2.16") Gtk.Application.__init__( self, application_id='org.gnome.Eolie', flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) self.set_property('register-session', 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.__extension_dir = extension_dir self.__windows = [] self.__pages_menu = PagesMenu(self) self.debug = False try: self.zoom_levels = load( open(self.LOCAL_PATH + "/zoom_levels.bin", "rb")) except: self.zoom_levels = {} self.cursors = {} GLib.set_application_name('Eolie') GLib.set_prgname('eolie') self.add_main_option("debug", b'd', GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Debug Eolie", None) self.add_main_option("private", b'p', GLib.OptionFlags.NONE, GLib.OptionArg.NONE, "Add a private page", None) self.connect('activate', self.__on_activate) 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 """ self.__is_fs = False self.__setup_app_menu() self.set_app_menu(self.__menu) cssProviderFile = Gio.File.new_for_uri( 'resource:///org/gnome/Eolie/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() self.history = DatabaseHistory() self.bookmarks = DatabaseBookmarks() # We store cursors for main thread SqlCursor.add(self.history) SqlCursor.add(self.bookmarks) try: from eolie.mozilla_sync import SyncWorker self.sync_worker = SyncWorker() self.sync_worker.sync() GLib.timeout_add_seconds(3600, self.sync_worker.sync) except Exception as e: print("Application::init():", e) self.sync_worker = None self.adblock = DatabaseAdblock() self.adblock.update() self.art = Art() self.search = Search() self.download_manager = DownloadManager() shortcut_action = Gio.SimpleAction.new('shortcut', GLib.VariantType.new('s')) shortcut_action.connect('activate', self.__on_shortcut_action) self.add_action(shortcut_action) self.set_accels_for_action("win.shortcut::uri", ["<Control>l"]) self.set_accels_for_action("win.shortcut::new_page", ["<Control>t"]) self.set_accels_for_action("app.shortcut::new_window", ["<Control>n"]) self.set_accels_for_action("win.shortcut::private", ["<Control><Shift>p"]) self.set_accels_for_action("win.shortcut::close_page", ["<Control>w"]) self.set_accels_for_action("win.shortcut::save", ["<Control>s"]) self.set_accels_for_action("win.shortcut::filter", ["<Control>i"]) self.set_accels_for_action("win.shortcut::reload", ["<Control>r"]) self.set_accels_for_action("win.shortcut::find", ["<Control>f"]) self.set_accels_for_action("win.shortcut::print", ["<Control>p"]) self.set_accels_for_action("win.shortcut::settings", ["<Control>e"]) self.set_accels_for_action("win.shortcut::backward", ["<Alt>Left"]) self.set_accels_for_action("win.shortcut::forward", ["<Alt>Right"]) self.set_accels_for_action("win.shortcut::next", ["<Control>Tab"]) self.set_accels_for_action("win.shortcut::previous", ["<Control><Shift>Tab"]) # Set some WebKit defaults context = WebKit2.WebContext.get_default() GLib.setenv('PYTHONPATH', self.__extension_dir, True) context.set_web_extensions_directory(self.__extension_dir) data_manager = WebKit2.WebsiteDataManager() context.new_with_website_data_manager(data_manager) context.set_process_model( WebKit2.ProcessModel.MULTIPLE_SECONDARY_PROCESSES) context.set_cache_model(WebKit2.CacheModel.WEB_BROWSER) d = Gio.File.new_for_path(self.__FAVICONS_PATH) if not d.query_exists(): d.make_directory_with_parents() context.set_favicon_database_directory(self.__FAVICONS_PATH) cookie_manager = context.get_cookie_manager() cookie_manager.set_accept_policy( self.settings.get_enum("cookie-storage")) cookie_manager.set_persistent_storage( self.__COOKIES_PATH, WebKit2.CookiePersistentStorage.SQLITE) helper = DBusHelper() helper.connect(self.__on_extension_signal, None) def do_startup(self): """ Init application """ Gtk.Application.do_startup(self) if not self.__windows: self.init() self.__get_new_window() def set_setting(self, key, value): """ Set setting for all view @param key as str @param value as GLib.Variant """ for window in self.__windows: for view in window.container.views: view.webview.set_setting(key, value) @property def pages_menu(self): """ Get pages menu @return PagesMenu """ return self.__pages_menu @property def start_page(self): """ Get start page @return uri as str """ value = self.settings.get_value("start-page").get_string() if value == "search": value = self.search.uri elif value == "popular": value = "populars://" return value @property def active_window(self): """ Get active window @return Window """ for window in self.__windows: if window.is_active(): return window # Fallback if self.__windows: return self.__windows[0] return None @property def windows(self): """ Get windows @return [Window] """ return self.__windows @property def cookies_path(self): """ Cookies sqlite db path """ return self.__COOKIES_PATH @property def favicons_path(self): """ Cookies sqlite db path """ return self.__FAVICONS_PATH + "/WebpageIcons.db" ####################### # PRIVATE # ####################### def __save_state(self): """ Save window position and view """ self.download_manager.cancel() self.adblock.stop() if self.sync_worker is not None: self.sync_worker.stop() try: remember_session = self.settings.get_value("remember-session") session_states = [] for window in self.__windows: window.container.stop() if not remember_session: continue for view in window.container.views: uri = view.webview.get_uri() private = view.webview.private state = view.webview.get_session_state().serialize() session_states.append((uri, private, state.get_data())) if remember_session: dump(session_states, open(self.LOCAL_PATH + "/session_states.bin", "wb")) dump(self.zoom_levels, open(self.LOCAL_PATH + "/zoom_levels.bin", "wb")) except Exception as e: print("Application::save_state()", e) def __get_new_window(self): """ Return a new window @return Window """ window = Window(self) window.connect('delete-event', self.__on_delete_event) window.show() self.__windows.append(window) return window def __show_plugins(self): """ Show available plugins on stdout """ if self.debug: self.active_window.container.current.webview.get_context( ).get_plugins(None, self.__on_get_plugins, None) def __restore_state(self): """ Restore saved state @return restored pages count as int """ count = 0 try: session_states = load( open(self.LOCAL_PATH + "/session_states.bin", "rb")) for (uri, private, state) in session_states: webkit_state = WebKit2.WebViewSessionState( GLib.Bytes.new(state)) GLib.idle_add(self.active_window.container.add_web_view, uri, count == 0, private, None, None, webkit_state) count += 1 except Exception as e: print("Application::restore_state()", e) return count 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 args = app_cmd_line.get_arguments() options = app_cmd_line.get_options_dict() if options.contains("debug"): GLib.setenv("LIBGL_DEBUG", "verbose", True) GLib.setenv("WEBKIT_DEBUG", "network", True) GLib.setenv("GST_DEBUG", "webkit*:5", True) self.debug = True private_browsing = options.contains("private") if self.settings.get_value("remember-session"): count = self.__restore_state() else: count = 0 active_window = self.active_window if len(args) > 1: for uri in args[1:]: active_window.container.add_web_view(uri, True, private_browsing) active_window.present() elif count == 0: # We already have a window, open a new one if active_window.container.current: window = self.__get_new_window() window.container.add_web_view(self.start_page, True, private_browsing) else: active_window.container.add_web_view(self.start_page, True, private_browsing) GLib.idle_add(self.__show_plugins) return 0 def __on_get_plugins(self, source, result, data): """ Print plugins on command line @param source as GObject.Object @param result as Gio.AsyncResult @param data as None """ plugins = source.get_plugins_finish(result) for plugin in plugins: print(plugin.get_name(), plugin.get_description(), plugin.get_path()) def __on_delete_event(self, window, event): """ Exit application @param window as Window @param event as Gdk.Event """ self.__save_state() self.__windows.remove(window) window.destroy() if not self.__windows: self.quit() def __on_settings_activate(self, action, param): """ Show settings dialog @param action as Gio.SimpleAction @param param as GLib.Variant """ dialog = SettingsDialog(self.active_window) dialog.show() def __on_about_activate(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/Eolie/AboutDialog.ui') about = builder.get_object('about_dialog') window = self.active_window if window is not None: about.set_transient_for(window) about.connect("response", self.__on_about_activate_response) about.show() def __on_shortcuts_activate(self, action, param): """ Show help in yelp @param action as Gio.SimpleAction @param param as GLib.Variant """ try: builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/Shortcuts.ui') shortcuts = builder.get_object('shortcuts') window = self.active_window if window is not None: shortcuts.set_transient_for(window) shortcuts.show() except: # GTK < 3.20 self.__on_help_activate(action, param) def __on_help_activate(self, action, param): """ Show help in yelp @param action as Gio.SimpleAction @param param as GLib.Variant """ try: Gtk.show_uri(None, "help:eolie", Gtk.get_current_event_time()) except: print(_("Eolie: You need to install yelp.")) def __on_about_activate_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/Eolie/Appmenu.ui') self.__menu = builder.get_object('app-menu') settings_action = Gio.SimpleAction.new('settings', None) settings_action.connect('activate', self.__on_settings_activate) self.add_action(settings_action) about_action = Gio.SimpleAction.new('about', None) about_action.connect('activate', self.__on_about_activate) self.add_action(about_action) shortcuts_action = Gio.SimpleAction.new('shortcuts', None) shortcuts_action.connect('activate', self.__on_shortcuts_activate) self.add_action(shortcuts_action) help_action = Gio.SimpleAction.new('help', None) help_action.connect('activate', self.__on_help_activate) self.add_action(help_action) quit_action = Gio.SimpleAction.new('quit', None) quit_action.connect('activate', lambda x, y: self.__save_state()) self.add_action(quit_action) def __on_activate(self, application): """ Call default handler, raise last window @param application as Gio.Application """ if self.__windows: self.__windows[-1].present() def __on_shortcut_action(self, action, param): """ Global shortcuts handler @param action as Gio.SimpleAction @param param as GLib.Variant """ string = param.get_string() if string == "new_window": window = self.__get_new_window() window.container.add_web_view(self.start_page, True) def __on_extension_signal(self, connection, sender, path, interface, signal, params, data): """ Show message on wanted window @param connection as Gio.DBusConnection @param sender as str @param path as str @param interface as str @param signal as str @param parameters as GLib.Variant @param data """ webview = self.active_window.container.current.webview self.active_window.toolbar.title.show_input_warning(webview)
class RadioArt(BaseArt): """ Manage radio artwork """ if GLib.getenv("XDG_DATA_HOME") is None: _RADIOS_PATH = GLib.get_home_dir() + "/.local/share/lollypop/radios" else: _RADIOS_PATH = GLib.getenv("XDG_DATA_HOME") + "/lollypop/radios" def __init__(self): """ Init radio art """ BaseArt.__init__(self) d = Gio.File.new_for_path(self._RADIOS_PATH) if not d.query_exists(): try: d.make_directory_with_parents() except Exception as e: print("RadioArt.__init__(): %s" % e) def get_radio_cache_path(self, name, size): """ get cover cache path for radio @param name as string @return cover path as string or None if no cover """ filename = "" try: filename = self.__get_radio_cache_name(name) cache_path_png = "%s/%s_%s.png" % (self._CACHE_PATH, filename, size) f = Gio.File.new_for_path(cache_path_png) if f.query_exists(): return cache_path_png else: self.get_radio_artwork(name, size, 1) if f.query_exists(): return cache_path_png else: return self._get_default_icon_path( size, "audio-input-microphone-symbolic") except Exception as e: print("Art::get_radio_cache_path(): %s" % e, ascii(filename)) return None def get_radio_artwork(self, name, size, scale): """ Return a cairo surface for radio name @param radio name as string @param pixbuf size as int @param scale factor as int @return cairo surface """ size *= scale filename = self.__get_radio_cache_name(name) cache_path_png = "%s/%s_%s.png" % (self._CACHE_PATH, filename, size) pixbuf = None try: # Look in cache f = Gio.File.new_for_path(cache_path_png) if f.query_exists(): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(cache_path_png, size, size) else: path = self.__get_radio_art_path(name) if path is not None: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, size, size) if pixbuf is None: return self.get_default_icon( "audio-input-microphone-symbolic", size, scale) pixbuf.savev(cache_path_png, "png", [None], [None]) surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, None) return surface except Exception as e: print(e) return self.get_default_icon("audio-input-microphone-symbolic", size, scale) def copy_uri_to_cache(self, params): """ Copy uri to cache at size @param params as (str, str, int) @thread safe """ uri, name, size = params filename = self.__get_radio_cache_name(name) cache_path_png = "%s/%s_%s.png" % (self._CACHE_PATH, filename, size) s = Gio.File.new_for_uri(uri) d = Gio.File.new_for_path(cache_path_png) s.copy(d, Gio.FileCopyFlags.OVERWRITE, None, None) GLib.idle_add(self.emit, "radio-artwork-changed", name) def rename_radio(self, old_name, new_name): """ Rename artwork @param old name as str @param new name as str """ old = "%s/%s.png" % (self._RADIOS_PATH, old_name) new = "%s/%s.png" % (self._RADIOS_PATH, new_name) try: src = Gio.File.new_for_path(old) dst = Gio.File.new_for_path(new) src.move(dst, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: print("RadioArt::rename_radio(): %s" % e) def save_radio_artwork(self, pixbuf, radio): """ Save pixbuf for radio @param pixbuf as Gdk.Pixbuf @param radio name as string """ try: artpath = self._RADIOS_PATH + "/" +\ radio.replace("/", "-") + ".png" pixbuf.savev(artpath, "png", [None], [None]) except Exception as e: print("RadioArt::save_radio_artwork(): %s" % e) def radio_artwork_update(self, name): """ Announce radio logo update @param radio name as string """ self.emit("radio-artwork-changed", name) def clean_radio_cache(self, name): """ Remove logo from cache for radio @param radio name as string """ cache_name = self.__get_radio_cache_name(name) try: f = Gio.File.new_for_path(self._CACHE_PATH) infos = f.enumerate_children( "standard::name", Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, None) for info in infos: f = infos.get_child(info) basename = f.get_basename() if re.search("%s_.*\.png" % re.escape(cache_name), basename): f.delete() except Exception as e: print("RadioArt::clean_radio_cache(): ", e, cache_name) ####################### # PRIVATE # ####################### def __get_radio_art_path(self, name): """ Look for radio covers @param radio name as string @return cover file path as string """ try: name = name.replace("/", "-") f = Gio.File.new_for_path(self._RADIOS_PATH + "/" + name + ".png") if f.query_exists(): return self._RADIOS_PATH + "/" + name + ".png" return None except Exception as e: print("Art::__get_radio_art_path(): %s" % e) def __get_radio_cache_name(self, name): """ Get a uniq string for radio @param album id as int @param sql as sqlite cursor """ return "@@"+name.replace("/", "-")+"@@radio@@"
class DatabaseBookmarks: """ Eolie bookmarks db """ if GLib.getenv("XDG_DATA_HOME") is None: __LOCAL_PATH = GLib.get_home_dir() + "/.local/share/eolie" else: __LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") + "/eolie" DB_PATH = "%s/bookmarks.db" % __LOCAL_PATH # SQLite documentation: # In SQLite, a column with type INTEGER PRIMARY KEY # is an alias for the ROWID. # Here, we define an id INT PRIMARY KEY but never feed it, # this make VACUUM not destroy rowids... __create_bookmarks = '''CREATE TABLE bookmarks ( id INTEGER PRIMARY KEY, title TEXT NOT NULL, uri TEXT NOT NULL )''' __create_tags = '''CREATE TABLE tags (id INTEGER PRIMARY KEY, title TEXT NOT NULL)''' __create_bookmarks_tags = '''CREATE TABLE bookmarks_tags ( id INTEGER PRIMARY KEY, bookmark_id INT NOT NULL, tag_id INT NOT NULL)''' def __init__(self): """ Create database tables or manage update if needed """ f = Gio.File.new_for_path(self.DB_PATH) if not f.query_exists(): try: d = Gio.File.new_for_path(self.__LOCAL_PATH) if not d.query_exists(): d.make_directory_with_parents() # Create db schema with SqlCursor(self) as sql: sql.execute(self.__create_bookmarks) sql.execute(self.__create_tags) sql.execute(self.__create_bookmarks_tags) sql.commit() except Exception as e: print("DatabaseBookmarks::__init__(): %s" % e) def add(self, title, uri, tags): """ Add a new bookmark @param title as str @param uri as str @param tags as [str] """ if not uri or not title: return with SqlCursor(self) as sql: result = sql.execute( "INSERT INTO bookmarks\ (title, uri)\ VALUES (?, ?)", (title, uri)) bookmarks_id = result.lastrowid for tag in tags: if not tag: continue tag_id = self.get_tag_id(tag) if tag_id is None: result = sql.execute( "INSERT INTO tags\ (title) VALUES (?)", (tag, )) tag_id = result.lastrowid sql.execute( "INSERT INTO bookmarks_tags\ (bookmark_id, tag_id) VALUES (?, ?)", (bookmarks_id, tag_id)) sql.commit() # We need this as current db is attached to history El().history.add(title, uri, 0) def get_id(self, uri): """ Get id for uri @param uri as str """ with SqlCursor(self) as sql: result = sql.execute( "SELECT rowid\ FROM bookmarks\ WHERE uri=?", (uri, )) v = result.fetchone() if v is not None: return v[0] return None def get_tag_id(self, title): """ Get tag id @param title as str """ with SqlCursor(self) as sql: result = sql.execute( "SELECT rowid\ FROM tags\ WHERE title=?", (title, )) v = result.fetchone() if v is not None: return v[0] return None def get_tags(self): """ Get all tags @return [rowid, str] """ with SqlCursor(self) as sql: result = sql.execute("SELECT rowid, title\ FROM tags\ ORDER BY title COLLATE LOCALIZED") return list(result) def get_bookmarks(self, tag_id): """ Get all bookmarks @param tag id as int @return [(id, title, uri)] """ with SqlCursor(self) as sql: result = sql.execute( "\ SELECT bookmarks.rowid,\ bookmarks.title,\ bookmarks.uri\ FROM bookmarks, bookmarks_tags, history.history\ WHERE bookmarks.rowid=bookmarks_tags.bookmark_id\ AND bookmarks_tags.tag_id=?\ AND history.uri=bookmarks.uri\ ORDER BY history.popularity DESC", (tag_id, )) return list(result) def get_populars(self): """ Get popular bookmarks """ with SqlCursor(self) as sql: result = sql.execute("\ SELECT bookmarks.rowid,\ bookmarks.title,\ bookmarks.uri\ FROM bookmarks, history.history\ WHERE history.uri=bookmarks.uri\ AND history.popularity!=0\ ORDER BY history.popularity DESC") return list(result) def get_recents(self): """ Get recents bookmarks """ with SqlCursor(self) as sql: result = sql.execute("\ SELECT bookmarks.rowid,\ bookmarks.title,\ bookmarks.uri\ FROM bookmarks, history.history\ WHERE history.uri=bookmarks.uri\ AND history.mtime != 0\ ORDER BY history.mtime DESC") return list(result) def import_firefox(self): """ Mozilla Firefox importer """ firefox_path = GLib.get_home_dir() + "/.mozilla/firefox/" d = Gio.File.new_for_path(firefox_path) infos = d.enumerate_children('standard::name,standard::type', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, None) sqlite_path = None for info in infos: if info.get_file_type() == Gio.FileType.DIRECTORY: f = Gio.File.new_for_path(firefox_path + info.get_name() + "/places.sqlite") if f.query_exists(): sqlite_path = f.get_path() break if sqlite_path is not None: c = sqlite3.connect(sqlite_path, 600.0) result = c.execute("SELECT bookmarks.title,\ moz_places.url,\ tag.title\ FROM moz_bookmarks AS bookmarks,\ moz_bookmarks AS tag,\ moz_places\ WHERE bookmarks.fk=moz_places.id\ AND bookmarks.type=1\ AND tag.id=bookmarks.parent") for (title, uri, tag) in list(result): rowid = self.get_id(uri) if rowid is None: self.add(title, uri, [tag]) def search(self, search): """ Search string in db (uri and title) @param search as str """ with SqlCursor(self) as sql: filter = '%' + search + '%' result = sql.execute( "SELECT title, uri\ FROM bookmarks\ WHERE title LIKE ?\ OR uri LIKE ?\ ORDER BY popularity DESC, mtime DESC", (filter, filter)) return list(result) def get_cursor(self): """ Return a new sqlite cursor """ try: c = sqlite3.connect(self.DB_PATH, 600.0) c.create_collation('LOCALIZED', LocalizedCollation()) c.execute("ATTACH DATABASE '%s' AS history" % DatabaseHistory.DB_PATH) c.create_function("noaccents", 1, noaccents) return c except: exit(-1) def drop_db(self): """ Drop database """ try: f = Gio.File.new_for_path(self.DB_PATH) f.trash() except Exception as e: print("DatabaseBookmarks::drop_db():", e)
from datetime import datetime from gi._gi import variant_type_from_string from gi.repository import GLib from gi.repository import Gio gettext.bindtextdomain("{{UUID}}", os.path.expanduser("~") + "/.local/share/locale") gettext.textdomain("{{UUID}}") _ = gettext.gettext # The application ID is also used as the daemon name. APPLICATION_ID = "org.cinnamon.Applets.WallpaperChangerApplet.Daemon" SCHEMA_NAME = "org.cinnamon.applets.{{UUID}}" SCHEMA_PATH = "/org/cinnamon/applets/{{UUID}}/" CINNAMON_VERSION = GLib.getenv("CINNAMON_VERSION") XLET_DIR = os.path.dirname(os.path.abspath(__file__)) __daemon_path__ = XLET_DIR __version__ = "2.2.0" WallChangerDaemonDBusInterface = Gio.DBusNodeInfo.new_for_xml("""<node>\ <interface name="%s">\ <method name="LoadProfile">\ <arg direction="in" name="profile" type="s" />\ </method>\ <method name="Next">\ <arg direction="out" name="uri" type="s" />\ </method>\ <method name="Prev">\
def __init__( self, connmgr, datadir=GLib.getenv("HOME") + "/.config/google-chrome", namespace="com.google.chrome.Policies"): super(ChromeLogger, self).__init__(connmgr, datadir, namespace)
def is_gnome(): """ Return True if desktop is Gnome """ return GLib.getenv("XDG_CURRENT_DESKTOP") == "GNOME"
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from gi.repository import Gio, GLib El = Gio.Application.get_default PROXY_BUS = 'org.gnome.Eolie.Proxy.Page%s' PROXY_PATH = '/org/gnome/EolieProxy' PROXY_INTERFACE = 'org.gnome.Eolie.Proxy' # Setup common paths if GLib.getenv("XDG_DATA_HOME") is None: LOCAL_PATH = GLib.get_home_dir() + "/.local/share" else: LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") EOLIE_LOCAL_PATH = LOCAL_PATH + "/eolie" ADBLOCK_JS = "%s/adblock_js" % EOLIE_LOCAL_PATH if GLib.getenv("XDG_CONFIG_HOME") is None: CONFIG_PATH = GLib.get_home_dir() + "/.config" else: CONFIG_PATH = GLib.getenv("XDG_CONFIG_HOME") if GLib.getenv("XDG_CACHE_HOME") is None: CACHE_PATH = GLib.get_home_dir() + "/.cache/eolie" else:
def session_is_cinnamon(): if "cinnamon" in GLib.getenv("DESKTOP_SESSION"): if GLib.find_program_in_path("cinnamon-session-quit"): return True return False
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from gi.repository import Gio, GLib El = Gio.Application.get_default PROXY_BUS = 'org.gnome.Eolie.Proxy' PROXY_PATH = '/org/gnome/EolieProxy' # Setup common paths if GLib.getenv("XDG_DATA_HOME") is None: LOCAL_PATH = GLib.get_home_dir() + "/.local/share" else: LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") EOLIE_LOCAL_PATH = LOCAL_PATH + "/eolie" if GLib.getenv("XDG_CONFIG_HOME") is None: CONFIG_PATH = GLib.get_home_dir() + "/.config" else: CONFIG_PATH = GLib.getenv("XDG_CONFIG_HOME") class TimeSpan: HOUR = "0" DAY = "1"
def __init__(self, window): """ Init dialog @param window as Window """ builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Eolie/SettingsDialog.ui") self.__settings_dialog = builder.get_object("settings_dialog") self.__settings_dialog.set_transient_for(window) # self.__settings_dialog.connect("destroy", self.__on_destroy) if False: self.__settings_dialog.set_title(_("Preferences")) else: headerbar = builder.get_object("header_bar") headerbar.set_title(_("Preferences")) self.__settings_dialog.set_titlebar(headerbar) download_chooser = builder.get_object("download_chooser") dir_uri = El().settings.get_value("download-uri").get_string() if not dir_uri: directory = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_DOWNLOAD) if directory is not None: dir_uri = GLib.filename_to_uri(directory, None) if dir_uri: download_chooser.set_uri(dir_uri) else: download_chooser.set_uri("file://" + GLib.getenv("HOME")) open_downloads = builder.get_object("open_downloads_check") open_downloads.set_active(El().settings.get_value("open-downloads")) self.__start_page_uri = builder.get_object("start_page_uri") combo_start = builder.get_object("combo_start") start_page = El().settings.get_value("start-page").get_string() if start_page.startswith("http"): combo_start.set_active_id("address") self.__start_page_uri.set_text(start_page) self.__start_page_uri.show() else: combo_start.set_active_id(start_page) combo_engine = builder.get_object("combo_engine") combo_engine.set_active_id( El().settings.get_value("search-engine").get_string()) remember_session = builder.get_object("remember_sessions_check") remember_session.set_active( El().settings.get_value("remember-session")) enable_plugins = builder.get_object("plugins_check") enable_plugins.set_active(El().settings.get_value("enable-plugins")) self.__fonts_grid = builder.get_object("fonts_grid") use_system_fonts = builder.get_object("system_fonts_check") use_system_fonts.set_active( El().settings.get_value("use-system-fonts")) self.__fonts_grid.set_sensitive( not El().settings.get_value("use-system-fonts")) sans_serif_button = builder.get_object("sans_serif_button") sans_serif_button.set_font_name( El().settings.get_value("font-sans-serif").get_string()) serif_button = builder.get_object("serif_button") serif_button.set_font_name( El().settings.get_value("font-serif").get_string()) monospace_button = builder.get_object("monospace_button") monospace_button.set_font_name( El().settings.get_value("font-monospace").get_string()) min_font_size_spin = builder.get_object("min_font_size_spin") min_font_size_spin.set_value( El().settings.get_value("min-font-size").get_int32()) monitor_model = get_current_monitor_model(window) zoom_levels = El().settings.get_value("default-zoom-level") wanted_zoom_level = 1.0 try: for zoom_level in zoom_levels: zoom_splited = zoom_level.split('@') if zoom_splited[0] == monitor_model: wanted_zoom_level = float(zoom_splited[1]) except: pass default_zoom_level = builder.get_object("default_zoom_level") default_zoom_level.set_value(float(wanted_zoom_level)) cookies_combo = builder.get_object("cookies_combo") storage = El().settings.get_enum("cookie-storage") cookies_combo.set_active_id(str(storage)) if GLib.find_program_in_path("seahorse") is None: button = builder.get_object("manage_passwords_button") button.set_sensitive(False) button.set_label( _("Installing seahorse will allow you\n" "managing your passwords")) remember_passwords = builder.get_object("remember_passwords_check") remember_passwords.set_active( El().settings.get_value("remember-passwords")) tracking_check = builder.get_object("tracking_check") tracking_check.set_active(El().settings.get_value("do-not-track")) label = builder.get_object("result_label") sync_button = builder.get_object("sync_button") if El().sync_worker is not None: login_entry = builder.get_object("login_entry") login_entry.set_text(El().sync_worker.username) password_entry = builder.get_object("password_entry") image = builder.get_object("result_image") if El().sync_worker.status: label.set_text(_("Synchronization is working")) image.set_from_icon_name("network-transmit-receive-symbolic", Gtk.IconSize.MENU) sync_button.connect("clicked", self.__on_sync_button_clicked, login_entry, password_entry, label, image) else: try: from eolie.mozilla_sync import SyncWorker SyncWorker # Just make PEP8 happy except Exception as e: label.set_text( _("Synchronization is not available" " on your computer:\n %s") % e) sync_button.set_sensitive(False) builder.connect_signals(self)
class Application(Gtk.Application): """ Eolie application: """ if GLib.getenv("XDG_DATA_HOME") is None: __LOCAL_PATH = GLib.get_home_dir() + "/.local/share/eolie" else: __LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") + "/eolie" __COOKIES_PATH = "%s/cookies.db" % __LOCAL_PATH __FAVICONS_PATH = "%s/favicons" % __LOCAL_PATH def __init__(self, extension_dir): """ Create application @param extension_dir as str """ Gtk.Application.__init__( self, application_id='org.gnome.Eolie', flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE) self.set_property('register-session', 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.__extension_dir = extension_dir self.__windows = [] self.debug = False self.cursors = {} GLib.set_application_name('Eolie') GLib.set_prgname('eolie') self.connect('activate', self.__on_activate) 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 """ self.__is_fs = False if Gtk.get_minor_version() > 18: cssProviderFile = Gio.File.new_for_uri( 'resource:///org/gnome/Eolie/application.css') else: cssProviderFile = Gio.File.new_for_uri( 'resource:///org/gnome/Eolie/application-legacy.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() self.history = DatabaseHistory() self.bookmarks = DatabaseBookmarks() # We store cursors for main thread SqlCursor.add(self.history) SqlCursor.add(self.bookmarks) self.bookmarks.import_firefox() adblock = DatabaseAdblock() adblock.update() self.art = Art() self.search = Search() self.downloads_manager = DownloadsManager() shortcut_action = Gio.SimpleAction.new('shortcut', GLib.VariantType.new('s')) shortcut_action.connect('activate', self.__on_shortcut_action) self.add_action(shortcut_action) self.set_accels_for_action("app.shortcut::uri", ["<Control>l"]) self.set_accels_for_action("app.shortcut::new_page", ["<Control>t"]) self.set_accels_for_action("app.shortcut::close_page", ["<Control>w"]) # Set some WebKit defaults context = WebKit2.WebContext.get_default() GLib.setenv('PYTHONPATH', self.__extension_dir, True) context.set_web_extensions_directory(self.__extension_dir) data_manager = WebKit2.WebsiteDataManager() context.new_with_website_data_manager(data_manager) context.set_process_model( WebKit2.ProcessModel.MULTIPLE_SECONDARY_PROCESSES) context.set_cache_model(WebKit2.CacheModel.WEB_BROWSER) d = Gio.File.new_for_path(self.__FAVICONS_PATH) if not d.query_exists(): d.make_directory_with_parents() context.set_favicon_database_directory(self.__FAVICONS_PATH) cookie_manager = context.get_cookie_manager() cookie_manager.set_accept_policy( WebKit2.CookieAcceptPolicy.NO_THIRD_PARTY) cookie_manager.set_persistent_storage( self.__COOKIES_PATH, WebKit2.CookiePersistentStorage.SQLITE) def do_startup(self): """ Init application """ Gtk.Application.do_startup(self) if not self.__windows: self.init() menu = self.__setup_app_menu() if self.prefers_app_menu(): self.set_app_menu(menu) window = Window() else: window = Window() window.setup_menu(menu) window.connect('delete-event', self.__on_delete_event) window.show() self.__windows.append(window) def prepare_to_exit(self, action=None, param=None, exit=True): """ Save window position and view """ self.downloads_manager.cancel() if exit: self.quit() def quit(self): """ Quit Eolie """ for window in self.__windows: window.destroy() @property def active_window(self): """ Get active window @return Window """ for window in self.__windows: if window.is_active(): return window # Fallback if self.__windows: return self.__windows[0] return None @property def windows(self): """ Get windows @return [Window] """ return self.__windows ####################### # PRIVATE # ####################### 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 args = app_cmd_line.get_arguments() options = app_cmd_line.get_options_dict() if options.contains('debug'): pass else: active_window = self.active_window if len(args) > 1: for uri in args[1:]: active_window.container.add_web_view(uri, True) active_window.present() else: active_window.container.add_web_view("google.fr", True) return 0 def __on_delete_event(self, widget, event): """ Exit application @param widget as Gtk.Widget @param event as Gdk.Event """ self.prepare_to_exit() def __on_settings_activate(self, action, param): """ Show settings dialog @param action as Gio.SimpleAction @param param as GLib.Variant """ dialog = SettingsDialog() dialog.show() def __on_about_activate(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/Eolie/AboutDialog.ui') about = builder.get_object('about_dialog') window = self.active_window if window is not None: about.set_transient_for(window) about.connect("response", self.__on_about_activate_response) about.show() def __on_shortcuts_activate(self, action, param): """ Show help in yelp @param action as Gio.SimpleAction @param param as GLib.Variant """ try: builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Eolie/Shortcuts.ui') shortcuts = builder.get_object('shortcuts') window = self.active_window if window is not None: shortcuts.set_transient_for(window) shortcuts.show() except: # GTK < 3.20 self.__on_help_activate(action, param) def __on_help_activate(self, action, param): """ Show help in yelp @param action as Gio.SimpleAction @param param as GLib.Variant """ try: Gtk.show_uri(None, "help:eolie", Gtk.get_current_event_time()) except: print(_("Eolie: You need to install yelp.")) def __on_about_activate_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/Eolie/Appmenu.ui') menu = builder.get_object('app-menu') settings_action = Gio.SimpleAction.new('settings', None) settings_action.connect('activate', self.__on_settings_activate) self.add_action(settings_action) about_action = Gio.SimpleAction.new('about', None) about_action.connect('activate', self.__on_about_activate) self.add_action(about_action) shortcuts_action = Gio.SimpleAction.new('shortcuts', None) shortcuts_action.connect('activate', self.__on_shortcuts_activate) self.add_action(shortcuts_action) help_action = Gio.SimpleAction.new('help', None) help_action.connect('activate', self.__on_help_activate) self.add_action(help_action) quit_action = Gio.SimpleAction.new('quit', None) quit_action.connect('activate', self.prepare_to_exit) self.add_action(quit_action) return menu def __on_activate(self, application): """ Call default handler, raise last window @param application as Gio.Application """ if self.__windows: self.__windows[-1].present() def __on_shortcut_action(self, action, param): """ Global shortcuts handler @param action as Gio.SimpleAction @param param as GLib.Variant """ window = self.active_window if window is None: return string = param.get_string() if string == "uri": window.toolbar.title.focus_entry() elif string == "new_page": window.container.add_web_view("google.fr", True) elif string == "close_page": window.container.sidebar.current.destroy()
def is_unity(): """ Return True if desktop is Gnome """ return GLib.getenv("XDG_CURRENT_DESKTOP") == "ubuntu:GNOME"
class History: """ Playlists manager """ if GLib.getenv("XDG_DATA_HOME") is None: __LOCAL_PATH = GLib.get_home_dir() + "/.local/share/lollypop" else: __LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") + "/lollypop" __DB_PATH = "%s/history.db" % __LOCAL_PATH __LIMIT = 1000000 # Delete when limit is reached __DELETE = 100 # How many elements to delete __create_history = """CREATE TABLE history ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, duration INT NOT NULL, ltime INT NOT NULL, popularity INT NOT NULL, rate INT NOT NULL, mtime INT NOT NULL, album_rate INT NOT NULL, loved_album INT NOT NULL, album_popularity INT NOT NULL)""" def __init__(self): """ Init playlists manager """ # Create db schema try: with SqlCursor(self) as sql: sql.execute(self.__create_history) sql.commit() except: pass with SqlCursor(self) as sql: result = sql.execute("SELECT COUNT(*)\ FROM history") v = result.fetchone() if v is not None and v[0] > self.__LIMIT: sql.execute("DELETE FROM history\ WHERE rowid IN (SELECT rowid\ FROM history\ LIMIT %s)" % self.__DELETE) sql.commit() sql.execute("VACUUM") def add(self, name, duration, popularity, rate, ltime, mtime, loved_album, album_popularity, album_rate): """ Add a radio, update url if radio already exists in db @param name as str @param duration as int @param popularity as int @param rate as int @param ltime as int @param mtime as int @param loved album as bool @param album_popularity as int @param album_rate as int @thread safe """ with SqlCursor(self) as sql: if self.exists(name, duration): sql.execute("UPDATE history\ SET popularity=?,rate=?,ltime=?,mtime=?,\ loved_album=?,album_popularity=?,album_rate=?\ WHERE name=? AND duration=?", (popularity, rate, ltime, mtime, loved_album, album_popularity, album_rate, name, duration)) else: sql.execute("INSERT INTO history\ (name, duration, popularity, rate, ltime, mtime,\ loved_album, album_popularity, album_rate)\ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", (name, duration, popularity, rate, ltime, mtime, loved_album, album_popularity, album_rate)) sql.commit() def get(self, name, duration): """ Get stats for track with filename and duration @param path as str @param duration as int @return (popularity, ltime, mtime, loved album, album_popularity) as (int, int, int, int, int, int) """ with SqlCursor(self) as sql: result = sql.execute("SELECT popularity, rate, ltime, mtime,\ loved_album, album_popularity, album_rate\ FROM history\ WHERE name=?\ AND duration=?", (name, duration)) v = result.fetchone() if v is not None: return v return (0, 0, 0, 0, 0, 0, 0) def exists(self, name, duration): """ Return True if entry exists @param name as str @parma duration as int @return bool """ with SqlCursor(self) as sql: result = sql.execute("SELECT rowid\ FROM history\ WHERE name=?\ AND duration=?", (name, duration)) v = result.fetchone() if v is not None: return True else: return False def get_cursor(self): """ Return a new sqlite cursor """ try: return sqlite3.connect(self.__DB_PATH, 600.0) except: exit(-1)
class DatabaseAdblock: """ Eolie adblock db """ if GLib.getenv("XDG_DATA_HOME") is None: __LOCAL_PATH = GLib.get_home_dir() + "/.local/share/eolie" else: __LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") + "/eolie" DB_PATH = "%s/adblock.db" % __LOCAL_PATH __URIS = [ "https://adaway.org/hosts.txt", "http://winhelp2002.mvps.org/hosts.txt", "http://hosts-file.net/ad_servers.txt", "https://pgl.yoyo.org/adservers/serverlist.php?" "hostformat=hosts&showintro=0&mimetype=plaintext" ] # SQLite documentation: # In SQLite, a column with type INTEGER PRIMARY KEY # is an alias for the ROWID. # Here, we define an id INT PRIMARY KEY but never feed it, # this make VACUUM not destroy rowids... __create_adblock = '''CREATE TABLE adblock ( id INTEGER PRIMARY KEY, dns TEXT NOT NULL, mtime INT NOT NULL )''' def __init__(self): """ Create database tables or manage update if needed """ self.__exceptions = DatabaseExceptions() self.__cancellable = Gio.Cancellable.new() f = Gio.File.new_for_path(self.DB_PATH) # Lazy loading if not empty if not f.query_exists(): try: d = Gio.File.new_for_path(self.__LOCAL_PATH) if not d.query_exists(): d.make_directory_with_parents() # Create db schema with SqlCursor(self) as sql: sql.execute(self.__create_adblock) sql.commit() except Exception as e: print("DatabaseAdblock::__init__(): %s" % e) def add_exception(self, uri): """ Add an exception @param uri as str """ try: with SqlCursor(self.__exceptions) as sql: sql.execute("INSERT INTO exceptions (uri) VALUES (?)", (uri, )) sql.commit() except: pass def remove_exception(self, uri): """ Remove an exception @param uri as str """ try: with SqlCursor(self.__exceptions) as sql: sql.execute("DELETE FROM exceptions WHERE uri=?", (uri, )) sql.commit() except: pass def is_an_exception(self, uri): """ True if uri not in exceptions @param uri as str @return bool """ with SqlCursor(self.__exceptions) as sql: result = sql.execute( "SELECT rowid FROM exceptions\ WHERE uri=?", (uri, )) v = result.fetchone() return v is not None def update(self): """ Update database """ # Get in db mtime # Only update if filters older than one week mtime = 0 with SqlCursor(self) as sql: result = sql.execute("SELECT mtime FROM adblock LIMIT 1") v = result.fetchone() if v is not None: mtime = v[0] self.__mtime = int(time()) if self.__mtime - mtime < 604800: return self.__stop = False if Gio.NetworkMonitor.get_default().get_network_available(): thread = Thread(target=self.__update) thread.daemon = True thread.start() def stop(self): """ Stop update """ self.__cancellable.cancel() self.__cancellable.reset() self.__stop = True def is_blocked(self, uri): """ Return True if uri is blocked @param uri as str @return bool """ try: parse = urlparse(uri) with SqlCursor(self) as sql: result = sql.execute( "SELECT mtime FROM adblock\ WHERE dns=?", (parse.netloc, )) v = result.fetchone() return v is not None except Exception as e: print("DatabaseAdblock::is_blocked():", e) return False def get_cursor(self): """ Return a new sqlite cursor """ try: c = sqlite3.connect(self.DB_PATH, 600.0) return c except Exception as e: print(e) exit(-1) ####################### # PRIVATE # ####################### def __update(self): """ Update database """ SqlCursor.add(self) result = "" try: for uri in self.__URIS: session = Soup.Session.new() request = session.request(uri) stream = request.send(self.__cancellable) bytes = bytearray(0) buf = stream.read_bytes(1024, self.__cancellable).get_data() while buf: bytes += buf buf = stream.read_bytes(1024, self.__cancellable).get_data() result = bytes.decode('utf-8') count = 0 for line in result.split('\n'): if self.__cancellable.is_cancelled() or self.__stop: raise IOError("Cancelled") if line.startswith('#'): continue array = line.replace(' ', '\t', 1).replace('\t', '@', 1).split('@') if len(array) <= 1: continue dns = array[1].replace(' ', '').replace('\r', '').split('#')[0] # Update entry if exists, create else with SqlCursor(self) as sql: sql.execute( "INSERT INTO adblock\ (dns, mtime)\ VALUES (?, ?)", (dns, self.__mtime)) count += 1 if count == 1000: sql.commit() count = 0 # Delete removed entries with SqlCursor(self) as sql: sql.execute( "DELETE FROM adblock\ WHERE mtime!=?", (self.__mtime, )) except Exception as e: print("DatabaseAdlbock:__update():", e) with SqlCursor(self) as sql: sql.commit() SqlCursor.remove(self)
class InfoCache: """ Generic class to cache text and images """ if GLib.getenv("XDG_DATA_HOME") is None: _INFO_PATH = GLib.get_home_dir() + "/.local/share/lollypop/info" else: _INFO_PATH = GLib.getenv("XDG_DATA_HOME") + "/lollypop/info" if GLib.getenv("XDG_CACHE_HOME") is None: _CACHE_PATH = GLib.get_home_dir() + "/.cache/lollypop_info" else: _CACHE_PATH = GLib.getenv("XDG_CACHE_HOME") + "/lollypop_info" WEBSERVICES = [ ("lastfm", "_get_lastfm_artist_info", "_get_lastfm_album_artwork"), ("spotify", "_get_spotify_artist_info", "_get_spotify_album_artwork"), ("deezer", "_get_deezer_artist_info", "_get_deezer_album_artwork"), ("wikipedia", "_get_wp_artist_info", None) ] def init(): """ Init cache """ if not path.exists(InfoCache._INFO_PATH): try: mkdir(InfoCache._INFO_PATH) except: print("Can't create %s" % InfoCache._INFO_PATH) if not path.exists(InfoCache._CACHE_PATH): try: mkdir(InfoCache._CACHE_PATH) except: print("Can't create %s" % InfoCache._CACHE_PATH) def exists(prefix): """ Return True if an info is cached @param prefix as string """ exists = False for (suffix, helper1, helper2) in InfoCache.WEBSERVICES: filepath = "%s/%s_%s.jpg" % (InfoCache._INFO_PATH, escape(prefix), suffix) if path.exists(filepath): exists = True return exists def get_artwork(prefix, suffix, size): """ Return path for artwork @param prefix as string @param suffix as string @param size as int @return path as string/None """ try: for (suffix, helper1, helper2) in InfoCache.WEBSERVICES: extract = None filepath = "%s/%s_%s.jpg" % (InfoCache._INFO_PATH, escape(prefix), suffix) filepath_at_size = "%s/%s_%s_%s.jpg" % ( InfoCache._CACHE_PATH, escape(prefix), suffix, size) if not path.exists(filepath) or path.getsize(filepath) == 0: filepath_at_size = None continue # Make cache for this size if not path.exists(filepath_at_size): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( filepath, size, size) if pixbuf.get_height() > pixbuf.get_width(): vertical = True elif pixbuf.get_height() < pixbuf.get_width(): vertical = False else: extract = pixbuf if extract is None: extract = GdkPixbuf.Pixbuf.new( GdkPixbuf.Colorspace.RGB, True, 8, size, size) if vertical: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( filepath, size, -1, True) diff = pixbuf.get_height() - size pixbuf.copy_area(0, diff / 2, pixbuf.get_width(), size, extract, 0, 0) else: pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( filepath, -1, size, True) diff = pixbuf.get_width() - size pixbuf.copy_area(diff / 2, 0, size, pixbuf.get_height(), extract, 0, 0) extract.savev(filepath_at_size, "jpeg", ["quality"], [ str(Lp().settings.get_value( "cover-quality").get_int32()) ]) return filepath_at_size except Exception as e: print("InfoCache::get_artwork():", e) return None def get(prefix, suffix): """ Get content from cache @param prefix as str @param suffix as str @return (content as string, data as bytes) """ filepath = "%s/%s_%s" % (InfoCache._INFO_PATH, escape(prefix), suffix) content = None data = None if path.exists(filepath + ".txt"): f = Gio.File.new_for_path(filepath + ".txt") (status, content, tag) = f.load_contents() if not status: content = None image_path = filepath + ".jpg" if path.exists(image_path): f = Gio.File.new_for_path(image_path) (status, data, tag) = f.load_contents() if not status: data = None return (content, data) def add(prefix, content, data, suffix): """ Add info to store @param prefix as str @param content as str @param data as bytes @param suffix as str """ filepath = "%s/%s_%s" % (InfoCache._INFO_PATH, escape(prefix), suffix) if content is not None: f = Gio.File.new_for_path(filepath + ".txt") fstream = f.replace(None, False, Gio.FileCreateFlags.REPLACE_DESTINATION, None) if fstream is not None: fstream.write(content, None) fstream.close() if data is None: f = Gio.File.new_for_path(filepath + ".jpg") fstream = f.replace(None, False, Gio.FileCreateFlags.REPLACE_DESTINATION, None) fstream.close() else: bytes = GLib.Bytes(data) stream = Gio.MemoryInputStream.new_from_bytes(bytes) bytes.unref() pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale( stream, ArtSize.ARTIST, -1, True, None) stream.close() pixbuf.savev( filepath + ".jpg", "jpeg", ["quality"], [str(Lp().settings.get_value("cover-quality").get_int32())]) def remove(prefix, suffix): """ Remove info from store @param prefix as str @param suffix as str """ filepath = "%s/%s_%s.txt" % (InfoCache._INFO_PATH, escape(prefix), suffix) f = Gio.File.new_for_path(filepath) try: f.delete(None) except: pass filepath = "%s/%s_%s.jpg" % (InfoCache._INFO_PATH, escape(prefix), suffix) f = Gio.File.new_for_path(filepath) try: f.delete(None) except: pass def uncache_artwork(prefix, suffix, scale): """ Remove artwork from cache @param prefix as str @param suffix as str @param scale factor as int """ for i in [1, 2]: filepath = "%s/%s_%s_%s.jpg" % (InfoCache._CACHE_PATH, escape(prefix), suffix, ArtSize.ARTIST_SMALL * scale * i) f = Gio.File.new_for_path(filepath) try: f.delete(None) except: pass
def is_unity(): """ Return True if desktop is Unity """ return GLib.getenv("XDG_CURRENT_DESKTOP") == "Unity"
class Art: """ Base art manager """ if GLib.getenv("XDG_CACHE_HOME") is None: __CACHE_PATH = GLib.get_home_dir() + "/.cache/eolie" else: __CACHE_PATH = GLib.getenv("XDG_CACHE_HOME") + "/eolie" def __init__(self): """ Init base art """ self.__create_cache() def save_artwork(self, uri, surface, suffix): """ Save artwork for uri with suffix @param uri as str @param surface as cairo.surface @param suffix as str """ encoded = sha256(self.__strip_uri(uri)).hexdigest() filepath = "%s/%s_%s.png" % (self.__CACHE_PATH, encoded, suffix) pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height()) pixbuf.savev(filepath, "png", [None], [None]) del pixbuf def get_artwork(self, uri, suffix, scale_factor, width, heigth): """ @param uri as str @param suffix as str @param scale factor as int @param width as int @param height as int @return cairo.surface """ encoded = sha256(self.__strip_uri(uri)).hexdigest() filepath = "%s/%s_%s.png" % (self.__CACHE_PATH, encoded, suffix) f = Gio.File.new_for_path(filepath) if f.query_exists(): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(filepath, width, heigth, True) surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale_factor, None) del pixbuf return surface return None ####################### # PROTECTED # ####################### ####################### # PRIVATE # ####################### def __strip_uri(self, uri): """ Remove prefix from uri @param uri as str @return bytes """ parsed = urlparse(uri) scheme = "%s://" % parsed.scheme return parsed.geturl().replace(scheme, '', 1).encode("utf-8") def __create_cache(self): """ Create cache dir """ d = Gio.File.new_for_path(self.__CACHE_PATH) if not d.query_exists(): try: d.make_directory_with_parents() except: print("Can't create %s" % self.__CACHE_PATH)
class BaseArt(GObject.GObject): """ Base art manager """ if GLib.getenv("XDG_CACHE_HOME") is None: _CACHE_PATH = GLib.get_home_dir() + "/.cache/lollypop" else: _CACHE_PATH = GLib.getenv("XDG_CACHE_HOME") + "/lollypop" # Fallback when album dir is readonly if GLib.getenv("XDG_DATA_HOME") is None: _STORE_PATH = GLib.get_home_dir() + "/.local/share/lollypop/store" else: _STORE_PATH = GLib.getenv("XDG_DATA_HOME") + "/lollypop/store" __gsignals__ = { "album-artwork-changed": (GObject.SignalFlags.RUN_FIRST, None, (int, )), "artist-artwork-changed": (GObject.SignalFlags.RUN_FIRST, None, (str, )), "radio-artwork-changed": (GObject.SignalFlags.RUN_FIRST, None, (str, )) } def __init__(self): """ Init base art """ GObject.GObject.__init__(self) def update_art_size(self): """ Update value with some check """ value = Lp().settings.get_value("cover-size").get_int32() # Check value as user can enter bad value via dconf if value < ArtSize.SMALL or value > ArtSize.MAX: value = 200 ArtSize.BIG = value # For a 200 album artwork, we want a 60 artist artwork ArtSize.ARTIST_SMALL = ArtSize.BIG * 60 / 200 def clean_store(self, filename): """ @param filename as str """ try: filepath = self._STORE_PATH + "/" + filename + ".jpg" f = Lio.File.new_for_path(filepath) if f.query_exists(): f.delete() except Exception as e: print("Art::clean_store()", e) def get_default_icon(self, icon_name, size, scale): """ Construct an empty cover album, code forked Gnome Music, see copyright header @param icon_name as str @param size as int @param scale factor as int @return pixbuf as cairo.Surface """ try: # First look in cache cache_path_jpg = self._get_default_icon_path(size, icon_name) f = Lio.File.new_for_path(cache_path_jpg) if f.query_exists(): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( cache_path_jpg, size, size, False) else: # get a small pixbuf with the given path icon_size = size / 4 icon = Gtk.IconTheme.get_default().load_icon( icon_name, icon_size, 0) # create an empty pixbuf with the requested size pixbuf = GdkPixbuf.Pixbuf.new(icon.get_colorspace(), True, icon.get_bits_per_sample(), size, size) pixbuf.fill(0xffffffff) icon.composite(pixbuf, icon_size * 3 / 2, icon_size * 3 / 2, icon_size, icon_size, icon_size * 3 / 2, icon_size * 3 / 2, 1, 1, GdkPixbuf.InterpType.NEAREST, 255) # Gdk < 3.15 was missing save method # > 3.15 is missing savev method try: pixbuf.save(cache_path_jpg, "jpeg", ["quality"], [ str(Lp().settings.get_value( "cover-quality").get_int32()) ]) except: pixbuf.savev(cache_path_jpg, "jpeg", ["quality"], [ str(Lp().settings.get_value( "cover-quality").get_int32()) ]) surface = Gdk.cairo_surface_create_from_pixbuf(pixbuf, scale, None) return surface except: return self.get_default_icon("computer-fail-symbolic", ArtSize.MEDIUM, scale) ####################### # PROTECTED # ####################### def _respect_ratio(self, uri): """ Check for aspect ratio based on size @param uri as str @return respect aspect ratio as bool """ f = Lio.File.new_for_uri(uri) (status, data, tag) = f.load_contents(None) bytes = GLib.Bytes(data) stream = Gio.MemoryInputStream.new_from_bytes(bytes) bytes.unref() cover = GdkPixbuf.Pixbuf.new_from_stream(stream, None) stream.close() cover_width = cover.get_width() cover_height = cover.get_height() del cover if cover_width == cover_height: return True elif cover_width < cover_height: cut = cover_height / 5 return cover_width < cover_height - cut else: cut = cover_width / 5 return cover_height < cover_width - cut def _create_store(self): """ Create store dir """ d = Lio.File.new_for_path(self._STORE_PATH) if not d.query_exists(): try: d.make_directory_with_parents() except: print("Can't create %s" % self._STORE_PATH) def _create_cache(self): """ Create cache dir """ d = Lio.File.new_for_path(self._CACHE_PATH) if not d.query_exists(): try: d.make_directory_with_parents() except: print("Can't create %s" % self._CACHE_PATH) def _get_default_icon_path(self, size, icon_name): """ Return default icon path @return path as string """ return "%s/%s_%s.jpg" % (self._CACHE_PATH, icon_name, size)
class Art: """ Base art manager """ if GLib.getenv("XDG_CACHE_HOME") is None: __CACHE_PATH = GLib.get_home_dir() + "/.cache/eolie" else: __CACHE_PATH = GLib.getenv("XDG_CACHE_HOME") + "/eolie" def __init__(self): """ Init base art """ self.__create_cache() def save_artwork(self, uri, surface, suffix): """ Save artwork for uri with suffix @param uri as str @param surface as cairo.surface @param suffix as str """ filepath = self.get_path(uri, suffix) pixbuf = Gdk.pixbuf_get_from_surface(surface, 0, 0, surface.get_width(), surface.get_height()) pixbuf.savev(filepath, "png", [None], [None]) del pixbuf def get_artwork(self, uri, suffix, scale_factor, width, heigth): """ @param uri as str @param suffix as str @param scale factor as int @param width as int @param height as int @return cairo.surface """ filepath = self.get_path(uri, suffix) f = Gio.File.new_for_path(filepath) if f.query_exists(): pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale( filepath, width, heigth, True) surface = Gdk.cairo_surface_create_from_pixbuf( pixbuf, scale_factor, None) del pixbuf return surface return None def get_path(self, uri, suffix): """ Return cache image path @return str """ strip = strip_uri(uri, False, True) strip = strip.replace("www.", "") encoded = sha256(strip.encode("utf-8")).hexdigest() filepath = "%s/%s_%s.png" % (self.__CACHE_PATH, encoded, suffix) return filepath def exists(self, uri, suffix): """ True if exists in cache and not older than one day @return bool """ f = Gio.File.new_for_path(self.get_path(uri, suffix)) exists = f.query_exists() if exists: info = f.query_info('time::modified', Gio.FileQueryInfoFlags.NONE, None) mtime = int(info.get_attribute_as_string('time::modified')) if time() - mtime > 86400: exists = False return exists @property def base_uri(self): """ Get cache base uri @return str """ return GLib.filename_to_uri(self.__CACHE_PATH) ####################### # PROTECTED # ####################### ####################### # PRIVATE # ####################### def __create_cache(self): """ Create cache dir """ d = Gio.File.new_for_path(self.__CACHE_PATH) if not d.query_exists(): try: d.make_directory_with_parents() except: print("Can't create %s" % self.__CACHE_PATH)
def is_gnome(): """ Return True if desktop is Gnome """ return GLib.getenv("XDG_CURRENT_DESKTOP") in ["ubuntu:GNOME", "GNOME"]
import os import argparse import gettext import gi import re import json import sys gi.require_version("Gtk", "3.0") from gi.repository import Gio, Gtk, GLib home = os.path.expanduser("~") APPLICATION_ID = "org.cinnamon.applets.odyseus.extensions-manager-debugger" SETTING_TYPE_NONE = 0 SETTING_TYPE_INTERNAL = 1 SETTING_TYPE_EXTERNAL = 2 curr_ver = GLib.getenv("CINNAMON_VERSION") debug = False translations = {} XLET_DIR = os.path.dirname(os.path.abspath(__file__)) XLET_UUID = str(os.path.basename(XLET_DIR)) def cmp(x, y): """ cmp(x, y) -> integer Return negative if x<y, zero if x==y, positive if x>y. """ return (x > y) - (x < y)
import sys import gettext import gi gi.require_version("Gio", "2.0") from datetime import datetime from gi.repository import GLib, Gio from gi._gi import variant_type_from_string # The application ID is also used as the daemon name. APPLICATION_ID = "org.cinnamon.Applets.WallpaperChangerApplet.Daemon" SCHEMA_NAME = "org.cinnamon.applets.0dyseus@WallpaperChangerApplet" SCHEMA_PATH = "/org/cinnamon/applets/0dyseus@WallpaperChangerApplet/" CINNAMON_VERSION = GLib.getenv("CINNAMON_VERSION") HOME = os.path.expanduser("~") EXTENSION_DIR = os.path.dirname(os.path.abspath(__file__)) EXTENSION_UUID = str(os.path.basename(EXTENSION_DIR)) TRANSLATIONS = {} __daemon_path__ = EXTENSION_DIR __version__ = "2.2.0" def _(string): # check for a translation for this xlet if EXTENSION_UUID not in TRANSLATIONS: try: TRANSLATIONS[EXTENSION_UUID] = gettext.translation( EXTENSION_UUID, HOME + "/.local/share/locale").gettext
def __init__(self, window): """ Init dialog @param window as Window """ self.__helper = PasswordsHelper() builder = Gtk.Builder() builder.add_from_resource("/org/gnome/Eolie/SettingsDialog.ui") self.__settings_dialog = builder.get_object("settings_dialog") self.__settings_dialog.set_transient_for(window) # self.__settings_dialog.connect("destroy", self.__on_destroy) if False: self.__settings_dialog.set_title(_("Preferences")) else: headerbar = builder.get_object("header_bar") headerbar.set_title(_("Preferences")) self.__settings_dialog.set_titlebar(headerbar) download_chooser = builder.get_object("download_chooser") dir_uri = El().settings.get_value("download-uri").get_string() if not dir_uri: directory = GLib.get_user_special_dir( GLib.UserDirectory.DIRECTORY_DOWNLOAD) if directory is not None: dir_uri = GLib.filename_to_uri(directory, None) if dir_uri: download_chooser.set_uri(dir_uri) else: download_chooser.set_uri("file://" + GLib.getenv("HOME")) open_downloads = builder.get_object("open_downloads_check") open_downloads.set_active( El().settings.get_value("open-downloads")) self.__start_page_uri = builder.get_object("start_page_uri") combo_start = builder.get_object("combo_start") start_page = El().settings.get_value("start-page").get_string() if start_page.startswith("http"): combo_start.set_active_id("address") self.__start_page_uri.set_text(start_page) self.__start_page_uri.show() else: combo_start.set_active_id(start_page) remember_session = builder.get_object("remember_sessions_check") remember_session.set_active( El().settings.get_value("remember-session")) enable_plugins = builder.get_object("plugins_check") enable_plugins.set_active( El().settings.get_value("enable-plugins")) self.__fonts_grid = builder.get_object("fonts_grid") use_system_fonts = builder.get_object("system_fonts_check") use_system_fonts.set_active( El().settings.get_value("use-system-fonts")) self.__fonts_grid.set_sensitive( not El().settings.get_value("use-system-fonts")) sans_serif_button = builder.get_object("sans_serif_button") sans_serif_button.set_font_name( El().settings.get_value("font-sans-serif").get_string()) serif_button = builder.get_object("serif_button") serif_button.set_font_name( El().settings.get_value("font-serif").get_string()) monospace_button = builder.get_object("monospace_button") monospace_button.set_font_name( El().settings.get_value("font-monospace").get_string()) min_font_size_spin = builder.get_object("min_font_size_spin") min_font_size_spin.set_value( El().settings.get_value("min-font-size").get_int32()) monitor_model = get_current_monitor_model(window) zoom_levels = El().settings.get_value("default-zoom-level") wanted_zoom_level = 1.0 try: for zoom_level in zoom_levels: zoom_splited = zoom_level.split('@') if zoom_splited[0] == monitor_model: wanted_zoom_level = float(zoom_splited[1]) except: pass default_zoom_level = builder.get_object("default_zoom_level") default_zoom_level.set_value(float(wanted_zoom_level)) cookies_combo = builder.get_object("cookies_combo") storage = El().settings.get_enum("cookie-storage") cookies_combo.set_active_id(str(storage)) history_combo = builder.get_object("history_combo") storage = El().settings.get_enum("history-storage") history_combo.set_active_id(str(storage)) self.__populars_count = builder.get_object("populars_count") if start_page == "popular": self.__populars_count.show() max_popular_items = El().settings.get_value( "max-popular-items").get_int32() builder.get_object("popular_spin_button").set_value(max_popular_items) remember_passwords = builder.get_object("remember_passwords_check") remember_passwords.set_active( El().settings.get_value("remember-passwords")) tracking_check = builder.get_object("tracking_check") tracking_check.set_active( El().settings.get_value("do-not-track")) self.__result_label = builder.get_object("result_label") self.__sync_button = builder.get_object("sync_button") self.__login_entry = builder.get_object("login_entry") self.__password_entry = builder.get_object("password_entry") self.__result_image = builder.get_object("result_image") builder.connect_signals(self) self.__helper.get_sync(self.__on_get_sync) thread = Thread(target=self.__get_sync_status) thread.daemon = True thread.start()
class Playlists(GObject.GObject): """ Playlists manager """ if GLib.getenv("XDG_DATA_HOME") is None: __LOCAL_PATH = GLib.get_home_dir() + "/.local/share/lollypop" else: __LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") + "/lollypop" _DB_PATH = "%s/playlists.db" % __LOCAL_PATH __gsignals__ = { # Add or remove a playlist 'playlists-changed': (GObject.SignalFlags.RUN_FIRST, None, (int,)), # Objects added/removed to/from playlist 'playlist-add': (GObject.SignalFlags.RUN_FIRST, None, (int, int, int)), 'playlist-del': (GObject.SignalFlags.RUN_FIRST, None, (int, int)) } __create_playlists = '''CREATE TABLE playlists ( id INTEGER PRIMARY KEY, name TEXT NOT NULL, mtime BIGINT NOT NULL)''' __create_tracks = '''CREATE TABLE tracks ( playlist_id INT NOT NULL, uri TEXT NOT NULL)''' def __init__(self): """ Init playlists manager """ GObject.GObject.__init__(self) self.LOVED = _("Loved tracks") # Create db schema try: with SqlCursor(self) as sql: sql.execute(self.__create_playlists) sql.execute(self.__create_tracks) sql.commit() except: pass def add(self, name): """ Add a playlist @param playlist name as str @thread safe """ with SqlCursor(self) as sql: result = sql.execute("INSERT INTO playlists (name, mtime)" " VALUES (?, ?)", (name, datetime.now().strftime('%s'))) sql.commit() GLib.idle_add(self.emit, 'playlists-changed', result.lastrowid) def exists(self, playlist_id): """ Return True if playlist exists @param playlist id as int @param bool """ with SqlCursor(self) as sql: result = sql.execute("SELECT rowid\ FROM playlists\ WHERE rowid=?", (playlist_id,)) v = result.fetchone() if v is not None: return True else: return False def rename(self, new_name, old_name): """ Rename playlist @param new playlist name as str @param old playlist name as str """ with SqlCursor(self) as sql: playlist_id = self.get_id(old_name) sql.execute("UPDATE playlists\ SET name=?\ WHERE name=?", (new_name, old_name)) sql.commit() GLib.idle_add(self.emit, 'playlists-changed', playlist_id) def delete(self, name): """ delete playlist @param playlist name as str """ with SqlCursor(self) as sql: playlist_id = self.get_id(name) sql.execute("DELETE FROM playlists\ WHERE name=?", (name,)) sql.execute("DELETE FROM tracks\ WHERE playlist_id=?", (playlist_id,)) sql.commit() GLib.idle_add(self.emit, 'playlists-changed', playlist_id) def remove(self, uri): """ Remove track from playlists @param uri as str """ with SqlCursor(self) as sql: sql.execute("DELETE FROM tracks\ WHERE uri=?", (uri,)) sql.commit() def get(self): """ Return availables playlists @return array of (id, string) """ with SqlCursor(self) as sql: result = sql.execute("SELECT rowid, name\ FROM playlists\ ORDER BY name\ COLLATE NOCASE COLLATE LOCALIZED") return list(result) def get_last(self): """ Return 6 last modified playlist @return [string] """ with SqlCursor(self) as sql: result = sql.execute("SELECT rowid, name\ FROM playlists\ ORDER BY mtime DESC\ LIMIT 6") return list(result) def get_tracks(self, playlist_id): """ Return availables tracks for playlist If playlist name == Type.ALL, then return all tracks from db @param playlist name as str @return array of paths as [str] """ with SqlCursor(self) as sql: result = sql.execute("SELECT uri\ FROM tracks\ WHERE playlist_id=?", (playlist_id,)) return list(itertools.chain(*result)) def get_track_ids(self, playlist_id): """ Return availables track ids for playlist If playlist name == Type.ALL, then return all tracks from db @param playlist id as int @return array of track id as int """ with SqlCursor(self) as sql: result = sql.execute("SELECT music.tracks.rowid\ FROM tracks, music.tracks\ WHERE tracks.playlist_id=?\ AND music.tracks.uri=\ main.tracks.uri", (playlist_id,)) return list(itertools.chain(*result)) def get_track_ids_sorted(self, playlist_id): """ Return availables track ids for playlist sorted by artist/album If playlist name == Type.ALL, then return all tracks from db @param playlist id as int @return array of track id as int """ with SqlCursor(self) as sql: result = sql.execute("SELECT music.tracks.rowid\ FROM tracks, music.tracks,\ music.track_artists, music.artists\ WHERE tracks.playlist_id=?\ AND music.track_artists.track_id=\ music.tracks.rowid\ AND music.artists.id=\ music.track_artists.artist_id\ AND music.tracks.uri=\ main.tracks.uri\ ORDER BY\ music.artists.sortname, album_id", (playlist_id,)) return list(itertools.chain(*result)) def get_id(self, playlist_name): """ Get playlist id @param playlist name as str @return playlst id as int """ if playlist_name == self.LOVED: return Type.LOVED with SqlCursor(self) as sql: result = sql.execute("SELECT rowid\ FROM playlists\ WHERE name=?", (playlist_name,)) v = result.fetchone() if v is not None: return v[0] return Type.NONE def get_name(self, playlist_id): """ Get playlist name @param playlist id as int @return playlist name as str """ if playlist_id == Type.LOVED: return self.LOVED with SqlCursor(self) as sql: result = sql.execute("SELECT name\ FROM playlists\ WHERE rowid=?", (playlist_id,)) v = result.fetchone() if v is not None: return v[0] return '' def get_names(self, playlist_ids): """ Return playlist names @param playlist_ids as [int] @return names as [str] """ names = [] for playlist_id in playlist_ids: if playlist_id == Type.POPULARS: names.append(_("Popular tracks")) elif playlist_id == Type.RECENTS: names.append(_("Recently played")) elif playlist_id == Type.NEVER: names.append(_("Never played")) elif playlist_id == Type.RANDOMS: names.append(_("Random tracks")) elif playlist_id == Type.SEARCH: names.append(_("Search")) else: names.append(self.get_name(playlist_id)) return names def clear(self, playlist_id, notify=True): """ Clear playlsit @param playlist id as int @param notify as bool """ with SqlCursor(self) as sql: sql.execute("DELETE FROM tracks\ WHERE playlist_id=?", (playlist_id,)) sql.commit() if notify: GLib.idle_add(self.emit, 'playlist-del', playlist_id, None) def add_tracks(self, playlist_id, tracks, notify=True): """ Add tracks to playlist if not already present @param playlist id as int @param tracks as [Track] @param notify as bool """ with SqlCursor(self) as sql: changed = False for track in tracks: if not self.exists_track(playlist_id, track.id): changed = True sql.execute("INSERT INTO tracks" " VALUES (?, ?)", (playlist_id, track.uri)) if notify: GLib.idle_add(self.emit, 'playlist-add', playlist_id, track.id, -1) if changed: sql.execute("UPDATE playlists SET mtime=?\ WHERE rowid=?", (datetime.now().strftime('%s'), playlist_id)) sql.commit() def remove_tracks(self, playlist_id, tracks, notify=True): """ Remove tracks from playlist @param playlist id as int @param tracks as [Track] """ with SqlCursor(self) as sql: for track in tracks: sql.execute("DELETE FROM tracks\ WHERE uri=?\ AND playlist_id=?", (track.uri, playlist_id)) if notify: GLib.idle_add(self.emit, 'playlist-del', playlist_id, track.id) sql.commit() def import_uri(self, playlist_id, uri, start=None, up=False): """ Import uri in playlist @param playlist id as int @param uri as str @param start track id as int @param up as bool """ try: uri = uri.strip('\n\r') f = Lio.File.new_for_uri(uri) if f.query_exists(): if f.query_file_type(Gio.FileQueryInfoFlags.NONE, None) == Gio.FileType.DIRECTORY: walk_uris = [uri] track_ids = [] while walk_uris: uri = walk_uris.pop(0) try: d = Lio.File.new_for_uri(uri) infos = d.enumerate_children( 'standard::name,standard::type', Gio.FileQueryInfoFlags.NONE, None) except Exception as e: print("Playlists::import_uri():", e) continue for info in infos: f = infos.get_child(info) if info.get_file_type() == Gio.FileType.DIRECTORY: walk_uris.append(f.get_uri()) else: track_id = Lp().tracks.get_id_by_uri( f.get_uri()) if track_id is not None: track_ids.append(track_id) else: track_id = Lp().tracks.get_id_by_uri(uri) track_ids = [track_id] tracks = [] if start is None: for track_id in track_ids: tracks.append(Track(track_id)) self.add_tracks(playlist_id, tracks) else: # Insert at wanted position playlist_track_ids = self.get_track_ids(playlist_id) start_idx = playlist_track_ids.index(start) if not up: start_idx += 1 for track_id in track_ids: playlist_track_ids.insert(start_idx, track_id) GLib.idle_add(self.emit, 'playlist-add', playlist_id, track_id, start_idx) start_idx += 1 self.clear(playlist_id, False) tracks = [] for track_id in playlist_track_ids: tracks.append(Track(track_id)) self.add_tracks(playlist_id, tracks, False) except: pass def get_position(self, playlist_id, track_id): """ Get track position in playlist @param playlist id as int @param track id as int @return position as int """ i = 0 for tid in self.get_track_ids(playlist_id): if track_id == tid: break i += 1 return i def exists_track(self, playlist_id, track_id): """ Check if track id exist in playlist @param playlist id as int @param track as Track @return bool """ with SqlCursor(self) as sql: result = sql.execute("SELECT main.tracks.uri\ FROM tracks, music.tracks\ WHERE music.tracks.rowid=?\ AND playlist_id=?\ AND music.tracks.uri=\ main.tracks.uri", (track_id, playlist_id)) v = result.fetchone() if v is not None: return True return False def exists_album(self, playlist_id, album_id, genre_ids, artist_ids): """ Return True if object_id is already present in playlist @param playlist id as int @param album id as int @param genre ids as [int] @param artist ids as [int] @param sql as sqlite cursor @return bool """ # We do not use Album object for performance reasons playlist_uris = self.get_tracks(playlist_id) track_uris = Lp().albums.get_track_uris(album_id, genre_ids, artist_ids) return len(set(playlist_uris) & set(track_uris)) == len(track_uris) def get_cursor(self): """ Return a new sqlite cursor """ try: sql = sqlite3.connect(self._DB_PATH, 600.0) sql.execute("ATTACH DATABASE '%s' AS music" % Database.DB_PATH) sql.create_collation('LOCALIZED', LocalizedCollation()) return sql except: exit(-1)
def is_gnome(): """ Check if the current desktop env is gnome """ return GLib.getenv("XDG_CURRENT_DESKTOP").lower() == "gnome"
class Database: """ Base database object """ if GLib.getenv("XDG_DATA_HOME") is None: __LOCAL_PATH = GLib.get_home_dir() + "/.local/share/lollypop" else: __LOCAL_PATH = GLib.getenv("XDG_DATA_HOME") + "/lollypop" DB_PATH = "%s/lollypop.db" % __LOCAL_PATH # SQLite documentation: # In SQLite, a column with type INTEGER PRIMARY KEY # is an alias for the ROWID. # Here, we define an id INT PRIMARY KEY but never feed it, # this make VACUUM not destroy rowids... __create_albums = '''CREATE TABLE albums (id INTEGER PRIMARY KEY, name TEXT NOT NULL, no_album_artist BOOLEAN NOT NULL, year INT, uri TEXT NOT NULL, popularity INT NOT NULL, rate INT NOT NULL, loved INT NOT NULL, synced INT NOT NULL, mtime INT NOT NULL)''' __create_artists = '''CREATE TABLE artists (id INTEGER PRIMARY KEY, name TEXT NOT NULL, sortname TEXT NOT NULL)''' __create_genres = '''CREATE TABLE genres (id INTEGER PRIMARY KEY, name TEXT NOT NULL)''' __create_album_artists = '''CREATE TABLE album_artists ( album_id INT NOT NULL, artist_id INT NOT NULL)''' __create_album_genres = '''CREATE TABLE album_genres ( album_id INT NOT NULL, genre_id INT NOT NULL)''' __create_tracks = '''CREATE TABLE tracks (id INTEGER PRIMARY KEY, name TEXT NOT NULL, uri TEXT NOT NULL, duration INT, tracknumber INT, discnumber INT, discname TEXT, album_id INT NOT NULL, year INT, popularity INT NOT NULL, rate INT NOT NULL, ltime INT NOT NULL, mtime INT NOT NULL, persistent INT NOT NULL DEFAULT 1)''' __create_track_artists = '''CREATE TABLE track_artists ( track_id INT NOT NULL, artist_id INT NOT NULL)''' __create_track_genres = '''CREATE TABLE track_genres ( track_id INT NOT NULL, genre_id INT NOT NULL)''' __create_album_artists_idx = '''CREATE index idx_aa ON album_artists( album_id)''' __create_track_artists_idx = '''CREATE index idx_ta ON track_artists( track_id)''' __create_album_genres_idx = '''CREATE index idx_ag ON album_genres( album_id)''' __create_track_genres_idx = '''CREATE index idx_tg ON track_genres( track_id)''' def __init__(self): """ Create database tables or manage update if needed """ f = Lio.File.new_for_path(self.DB_PATH) if not f.query_exists(): db_version = Lp().settings.get_value('db-version').get_int32() upgrade = DatabaseUpgrade(db_version) try: d = Lio.File.new_for_path(self.__LOCAL_PATH) if not d.query_exists(): d.make_directory_with_parents() # Create db schema with SqlCursor(self) as sql: sql.execute(self.__create_albums) sql.execute(self.__create_artists) sql.execute(self.__create_genres) sql.execute(self.__create_album_genres) sql.execute(self.__create_album_artists) sql.execute(self.__create_tracks) sql.execute(self.__create_track_artists) sql.execute(self.__create_track_genres) sql.execute(self.__create_album_artists_idx) sql.execute(self.__create_track_artists_idx) sql.execute(self.__create_album_genres_idx) sql.execute(self.__create_track_genres_idx) sql.commit() Lp().settings.set_value('db-version', GLib.Variant('i', upgrade.count())) except Exception as e: print("Database::__init__(): %s" % e) def upgrade(self): """ Upgrade database """ db_version = Lp().settings.get_value('db-version').get_int32() upgrade = DatabaseUpgrade(db_version) f = Lio.File.new_for_path(self.DB_PATH) if f.query_exists(): upgrade.do_db_upgrade() Lp().settings.set_value('db-version', GLib.Variant('i', upgrade.count())) def get_cursor(self): """ Return a new sqlite cursor """ try: c = sqlite3.connect(self.DB_PATH, 600.0) c.create_collation('LOCALIZED', LocalizedCollation()) c.create_function("noaccents", 1, noaccents) return c except: exit(-1) def drop_db(self): """ Drop database """ try: f = Gio.File.new_for_path(self.DB_PATH) f.trash() except Exception as e: print("Database::drop_db():", e) def del_tracks(self, track_ids): """ Delete tracks from db @param track_ids as [int] """ with SqlCursor(self) as sql: all_album_ids = [] all_artist_ids = [] all_genre_ids = [] for track_id in track_ids: album_id = Lp().tracks.get_album_id(track_id) art_file = Lp().art.get_album_cache_name(Album(album_id)) genre_ids = Lp().tracks.get_genre_ids(track_id) album_artist_ids = Lp().albums.get_artist_ids(album_id) artist_ids = Lp().tracks.get_artist_ids(track_id) uri = Lp().tracks.get_uri(track_id) Lp().playlists.remove(uri) Lp().tracks.remove(track_id) Lp().tracks.clean(track_id) all_album_ids.append(album_id) all_artist_ids += album_artist_ids + artist_ids all_genre_ids += genre_ids for album_id in list(set(all_album_ids)): if Lp().albums.clean(album_id): Lp().art.clean_store(art_file) for artist_id in list(set(all_artist_ids)): Lp().artists.clean(artist_id) for genre_id in list(set(all_genre_ids)): Lp().genres.clean(genre_id) sql.commit()