def _go(): self.thumbs_window = ThumbsWindow(screen=self.screen, position=options.position, breadth=options.breadth) try: icon = varietyconfig.get_data_file("media", "variety.svg") self.thumbs_window.set_icon_from_file(icon) except Exception: logger.exception(lambda: "Could not set thumbs window icon") if self.type == "history": title = _("Variety History") elif self.type == "downloads": title = _("Variety Recent Downloads") else: title = _("Variety Images") self.thumbs_window.set_title(title) self.thumbs_window.connect("clicked", self.on_click) def _on_close(window, event): self.hide(force=True) self.thumbs_window.connect("delete-event", _on_close) self.mark_active(self.active_file, self.active_position) self.thumbs_window.start(self.images)
def browse(self, widget=None): try: self.chooser = Gtk.FileChooserDialog( _("Choose a folder"), parent=self.button.get_toplevel(), action=Gtk.FileChooserAction.SELECT_FOLDER, buttons=[ _("Cancel"), Gtk.ResponseType.CANCEL, _("OK"), Gtk.ResponseType.OK ]) self.chooser.set_filename(self.folder) self.chooser.set_select_multiple(False) self.chooser.set_local_only(False) if self.chooser.run() == Gtk.ResponseType.OK: self.set_folder(_u(self.chooser.get_filename())) try: if self.on_change: self.on_change() except Exception: logger.exception( lambda: "Exception during FolderChooser on_change:") finally: if self.chooser: try: self.chooser.destroy() self.chooser = None except Exception: logger.exception( lambda: "Exception during FolderChooser destroying:")
def initialize_thumbs_window(self, gdk_thread=False): try: if not gdk_thread: Gdk.threads_enter() options = self.load_options() self.thumbs_window = ThumbsWindow( screen=self.screen, position=options.position, breadth=options.breadth) try: icon = varietyconfig.get_data_file("media", "variety.svg") self.thumbs_window.set_icon_from_file(icon) except Exception: logger.exception(lambda: "Could not set thumbs window icon") if self.type == "history": title = _("Variety History") elif self.type == "downloads": title = _("Variety Recent Downloads") else: title = _("Variety Images") self.thumbs_window.set_title(title) self.thumbs_window.connect("clicked", self.on_click) def _on_close(window, event): self.hide(gdk_thread=True, force=True) self.thumbs_window.connect("delete-event", _on_close) self.mark_active(self.active_file, self.active_position) self.thumbs_window.start(self.images) finally: if not gdk_thread: Gdk.threads_leave()
def validate(self, query): if not '/' in query: query = 'https://www.reddit.com/r/%s' % query else: if not query.startswith("http://") and not query.startswith("https://"): query = "https://" + query if not '//reddit.com' in query and not '//www.reddit.com' in query: return query, False, _("This does not seem to be a valid Reddit URL") valid = RedditDownloader.validate(query, self.parent) return query, None if valid else _("We could not find any image submissions there.")
def validate(self, query): if not "/" in query: query = "https://www.reddit.com/r/%s" % query else: if not query.startswith("http://") and not query.startswith( "https://"): query = "https://" + query if not "//reddit.com" in query and not "//www.reddit.com" in query: return query, False, _( "This does not seem to be a valid Reddit URL") valid = RedditDownloader.validate(query, self.parent) return (query, None if valid else _("We could not find any image submissions there."))
def validate(self, url): if not url.startswith("http://") and not url.startswith("https://"): url = "https://" + url valid = MediaRssDownloader.validate(url) return url, None if valid else _( "This does not seem to be a valid Media RSS feed URL or there is no content there." )
def get_info(cls): return { "name": "Goodreads", "description": _("Fetches quotes from Goodreads.com"), "author": "Peter Levi", "version": "0.1" }
def get_info(cls): return { "name": "TheQuotationsPage.com", "description": _("Fetches quotes from TheQuotationsPage.com"), "author": "Peter Levi", "version": "0.1" }
def _set_rating(widget, rating=rating): try: Util.set_rating(file, rating) main_window.on_rating_changed(file) except Exception: logger.exception(lambda: "Could not set EXIF rating") main_window.show_notification(_("Could not set EXIF rating"))
class BingDownloader(SimpleDownloader): DESCRIPTION = _("Bing Photo of the Day") BING_JSON_URL = ( "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=100&mkt=en-US" ) # n=100, but max 8 images are actually returned... Pity. @classmethod def get_info(cls): return { "name": "BingDownloader", "description": BingDownloader.DESCRIPTION, "author": "Peter Levi", "version": "0.1", } def get_source_type(self): return "bing" def get_description(self): return BingDownloader.DESCRIPTION def get_source_name(self): return "Bing" def get_source_location(self): return "https://www.bing.com/gallery/" def get_local_filename(self, url): return parse_qs(urlparse(url).query)["id"][0] def fill_queue(self): queue = [] s = Util.fetch_json(BingDownloader.BING_JSON_URL) for item in s["images"]: try: if not item["wp"]: # not marked as a wallpaper continue image_url = "https://www.bing.com" + item["url"] filename = item["url"].split("/")[-1] name = filename[0 : filename.find("_EN")] src_url = "https://www.bing.com/gallery/#images/%s" % name try: date = datetime.strptime(item["startdate"], "%Y%m%d").strftime("%Y-%m-%d") except: date = item["startdate"] extra_metadata = { "sourceType": "bing", "sfwRating": 100, "headline": "Bing Photo of the Day, %s" % date, "description": item["copyright"], } queue.append((src_url, image_url, extra_metadata)) except: logger.exception(lambda: "Could not process an item in the Bing json result") random.shuffle(queue) return queue
def get_info(cls): return { "name": "LDSInspiration", "description": _("Fetches quotes from lds.org quotes RSS feed.\n" "Does not support searching by tags, authors, or topics...yet."), "author": "Andrew Payne", "version": "0.2" }
def get_info(cls): return { "name": "QuotesDaddy", "description": _("Fetches quotes from QuotesDaddy's daily quotes RSS feed.\n" "Does not support searching by tags or authors."), "author": "Peter Levi", "version": "0.1" }
def create_rating_menu(file, main_window): def _set_rating_maker(rating): def _set_rating(widget, rating=rating): try: Util.set_rating(file, rating) main_window.on_rating_changed(file) except Exception: logger.exception(lambda: "Could not set EXIF rating") main_window.show_notification( _("Could not set EXIF rating")) return _set_rating try: actual_rating = Util.get_rating(file) except Exception: actual_rating = None rating_menu = Gtk.Menu() for rating in range(5, 0, -1): item = Gtk.CheckMenuItem("\u2605" * rating) item.set_draw_as_radio(True) item.set_active(actual_rating == rating) item.set_sensitive(not item.get_active()) item.connect("activate", _set_rating_maker(rating)) rating_menu.append(item) rating_menu.append(Gtk.SeparatorMenuItem.new()) unrated_item = Gtk.CheckMenuItem(_("Unrated")) unrated_item.set_draw_as_radio(True) unrated_item.set_active(actual_rating is None or actual_rating == 0) unrated_item.set_sensitive(not unrated_item.get_active()) unrated_item.connect("activate", _set_rating_maker(None)) rating_menu.append(unrated_item) rejected_item = Gtk.CheckMenuItem(_("Rejected")) rejected_item.set_draw_as_radio(True) rejected_item.set_active(actual_rating is not None and actual_rating < 0) rejected_item.set_sensitive(not rejected_item.get_active()) rejected_item.connect("activate", _set_rating_maker(-1)) rating_menu.append(rejected_item) rating_menu.show_all() return rating_menu
def _set_rating(widget, rating=rating): try: Util.set_rating(file, rating) main_window.on_rating_changed(file) except Exception: logger.exception(lambda: "Could not set EXIF rating") main_window.show_notification( _("Could not set EXIF rating"))
def ok_thread(self): search = "" if len(self.ui.tags.get_text().strip()): search += "tags:" + ','.join([urllib.quote_plus(t.strip()) for t in self.ui.tags.get_text().split(',')]) + ";" if len(self.ui.text.get_text().strip()): search += "text:" + urllib.quote_plus(self.ui.text.get_text().strip()) +";" self.error = "" user_url = self.ui.user_url.get_text().strip() if len(user_url) > 0: self.show_spinner() u = FlickrDownloader.obtain_userid(user_url) if u[0]: search += "user:"******"http://", "").replace("https://", "") + ";" search += "user_id:" + u[2] + ";" else: self.error = self.error + "\n" + u[1] group_url = self.ui.group_url.get_text().strip() if len(group_url) > 0: self.show_spinner() g = FlickrDownloader.obtain_groupid(group_url) if g[0]: search += "group:" + self.ui.group_url.get_text().replace("http://", "").replace("https://", "") + ";" search += "group_id:" + g[2] else: self.error = self.error + "\n" + g[1] if not len(self.error) and len(search) > 0: self.show_spinner() if FlickrDownloader.count_search_results(search) <= 0: self.error = _("No images found") try: Gdk.threads_enter() self.ui.buttonbox.set_sensitive(True) self.ui.spinner.stop() self.ui.spinner.set_visible(False) self.ui.message.set_visible(False) if len(self.error) > 0: self.ui.error.set_label(self.error) for entry in (self.ui.text, self.ui.tags, self.ui.user_url, self.ui.group_url): if entry.get_text().strip(): entry.grab_focus() break else: if len(search): self.parent.on_add_dialog_okay(Options.SourceType.FLICKR, search, self.edited_row) self.destroy() finally: Gdk.threads_leave()
def get_info(cls): return { "name": "Local text files", "description": _("Displays quotes, defined in local text files.\n" "Put your own txt files in: ~/.config/variety/pluginconfig/quotes/.\n" "The file format is:\n\nquote -- author\n.\nsecond quote -- another author\n.\netc...\n\n" "Example: http://rvelthuis.de/zips/quotes.txt"), "author": "Peter Levi", "version": "0.1" }
def create_rating_menu(file, main_window): def _set_rating_maker(rating): def _set_rating(widget, rating=rating): try: Util.set_rating(file, rating) main_window.on_rating_changed(file) except Exception: logger.exception(lambda: "Could not set EXIF rating") main_window.show_notification(_("Could not set EXIF rating")) return _set_rating try: actual_rating = Util.get_rating(file) except Exception: actual_rating = None rating_menu = Gtk.Menu() for rating in xrange(5, 0, -1): item = Gtk.CheckMenuItem(u"\u2605" * rating) item.set_draw_as_radio(True) item.set_active(actual_rating == rating) item.set_sensitive(not item.get_active()) item.connect("activate", _set_rating_maker(rating)) rating_menu.append(item) rating_menu.append(Gtk.SeparatorMenuItem.new()) unrated_item = Gtk.CheckMenuItem(_("Unrated")) unrated_item.set_draw_as_radio(True) unrated_item.set_active(actual_rating is None or actual_rating == 0) unrated_item.set_sensitive(not unrated_item.get_active()) unrated_item.connect("activate", _set_rating_maker(None)) rating_menu.append(unrated_item) rejected_item = Gtk.CheckMenuItem(_("Rejected")) rejected_item.set_draw_as_radio(True) rejected_item.set_active(actual_rating is not None and actual_rating < 0) rejected_item.set_sensitive(not rejected_item.get_active()) rejected_item.connect("activate", _set_rating_maker(-1)) rating_menu.append(rejected_item) rating_menu.show_all() return rating_menu
def on_facebook_auth(self, params): try: if self.hash != params["hash"][0]: return # user has reloaded an old redirect page, ignore it self.token = params['access_token'][0] self.token_expire = params['expires_in'][0] # Should be equal to 0, don't expire # Save token to file with open(self.token_file, 'w') as token_file: token_file.write(self.token) token_file.close() if self.on_success: self.parent.show_notification(_("Authorization successful"), _("Publishing...")) self.on_success(self, self.token) except Exception: logger.exception(lambda: "Facebook auth failed") if self.on_failure: self.on_failure(self, "authorize", _("Authorization failed"))
class EarthDownloader(SimpleDownloader): DESCRIPTION = _("World Sunlight Map - live wallpaper from Die.net") @classmethod def get_info(cls): return { "name": "EarthDownloader", "description": EarthDownloader.DESCRIPTION, "author": "Peter Levi", "version": "0.1" } def get_source_type(self): return "earth" def get_description(self): return EarthDownloader.DESCRIPTION def get_source_name(self): return "Die.net" def get_folder_name(self): return "Earth" def get_source_location(self): return EARTH_ORIGIN_URL def get_refresh_interval_seconds(self): return 15 * 60 def download_one(self): logger.info( lambda: "Downloading world sunlight map from " + EARTH_ORIGIN_URL) downloaded = self.save_locally( EARTH_ORIGIN_URL, EARTH_IMAGE_URL, force_download=True, extra_metadata={'headline': 'World Sunlight Map'}, ) final_path = os.path.join(self.target_folder, EARTH_FILENAME) shutil.move(downloaded, final_path) for f in os.listdir(self.target_folder): if f != EARTH_FILENAME and f.lower().endswith(".jpg"): os.unlink(os.path.join(self.target_folder, f)) return final_path def fill_queue(self): """ Not needed here """ return [] def on_variety_start_complete(self): if not os.path.exists(os.path.join(self.target_folder, EARTH_FILENAME)): self.download_one()
def browse(self, widget=None): try: self.chooser = Gtk.FileChooserDialog( _("Choose a folder"), parent=self.button.get_toplevel(), action=Gtk.FileChooserAction.SELECT_FOLDER, buttons=[_("Cancel"), Gtk.ResponseType.CANCEL, _("OK"), Gtk.ResponseType.OK]) self.chooser.set_filename(self.folder) self.chooser.set_select_multiple(False) self.chooser.set_local_only(False) if self.chooser.run() == Gtk.ResponseType.OK: self.set_folder(_u(self.chooser.get_filename())) try: if self.on_change: self.on_change() except Exception: logger.exception(lambda: "Exception during FolderChooser on_change:") finally: if self.chooser: try: self.chooser.destroy() self.chooser = None except Exception: logger.exception(lambda: "Exception during FolderChooser destroying:")
class ChromeOSWallpapersDownloader(SimpleDownloader): DESCRIPTION = _("Chrome OS Wallpapers") @classmethod def get_info(cls): return { "name": "ChromeOsWallpapersDownloader", "description": ChromeOSWallpapersDownloader.DESCRIPTION, "author": "Peter Levi", "version": "0.1", } def get_description(self): return ChromeOSWallpapersDownloader.DESCRIPTION def get_source_type(self): return "chromeos" def get_source_name(self): return "Chrome OS Wallpapers" def get_source_location(self): return self.get_source_name() def fill_queue(self): manifest = Util.fetch_json(MANIFEST_URL) queue = manifest["wallpaper_list"] self.tags = manifest["tags"] random.shuffle(queue) return queue def download_queue_item(self, item): image_url = item["base_url"] + "_high_resolution.jpg" origin_url = item["dynamic_url"] extra_metadata = {} if "tags" in item: extra_metadata["keywords"] = [ self.tags[str(tag)] for tag in item["tags"] if str(tag) in self.tags ] if "author" in item: extra_metadata["author"] = item["author"] if "author_website" in item: extra_metadata["authorURL"] = item["author_website"] return self.save_locally(origin_url, image_url, extra_metadata=extra_metadata)
class EarthviewDownloader(SimpleDownloader): DESCRIPTION = _("Google Earth View Wallpapers") ROOT_URL = "https://earthview.withgoogle.com/" @classmethod def get_info(cls): return { "name": "EarthviewDownloader", "description": EarthviewDownloader.DESCRIPTION, "author": "Peter Levi", "version": "0.1", } def get_description(self): return EarthviewDownloader.DESCRIPTION def get_source_type(self): return "earthview" def get_source_name(self): return "Earth View" def get_source_location(self): return self.ROOT_URL def fill_queue(self): queue = Util.fetch_json(DATA_URL) random.shuffle(queue) return queue def get_default_throttling(self): # throttle this source, as otherwise maps "overpower" all other types of images # with Variety's default settings, and we have no other way to control source "weights" return Throttling(min_download_interval=600, min_fill_queue_interval=0) def download_queue_item(self, item): region = item["Region"] filename = "{}{} (ID-{}).jpg".format( region + ", " if region and region != "-" else "", item["Country"], item["ID"]) origin_url = EarthviewDownloader.ROOT_URL + str(item["ID"]) image_url = item["Image URL"] if not image_url.startswith("http"): image_url = "https://" + image_url return self.save_locally(origin_url, image_url, local_filename=filename)
def _add_menuitem(rating): menuitem = Gtk.ImageMenuItem(_(rating['label_long'])) menuitem.set_visible(True) def _rate(*args, **kwargs): self.parent.report_sfw_rating(file=None, rating=rating['rating']) menuitem.connect("activate", _rate) try: menuitem.set_always_show_image(True) image = Gtk.Image() image.set_from_file(varietyconfig.get_data_file("media", "sfw-%s.svg" % rating['rating'])) menuitem.set_image(image) except: logger.exception(lambda: "Could not set image to NSFW menuitem:") self.sfw_menu.append(menuitem) self.rating_items.append(menuitem)
def _gui_update(sfw_ratings): try: def _add_menuitem(rating): menuitem = Gtk.ImageMenuItem(_(rating['label_long'])) menuitem.set_visible(True) def _rate(*args, **kwargs): self.parent.report_sfw_rating(file=None, rating=rating['rating']) menuitem.connect("activate", _rate) try: menuitem.set_always_show_image(True) image = Gtk.Image() image.set_from_file(varietyconfig.get_data_file("media", "sfw-%s.svg" % rating['rating'])) menuitem.set_image(image) except: logger.exception(lambda: "Could not set image to NSFW menuitem:") self.sfw_menu.append(menuitem) self.rating_items.append(menuitem) map(_add_menuitem, reversed(sfw_ratings)) separator = Gtk.SeparatorMenuItem.new() separator.set_visible(True) self.sfw_menu.append(separator) self.safe_mode = Gtk.CheckMenuItem(_("_Safe mode")) self.safe_mode.set_visible(True) self.safe_mode.set_active(self.parent.options.safe_mode) self.safe_mode.set_use_underline(True) self.safe_mode_handler_id = self.safe_mode.connect("toggled", self.parent.on_safe_mode_toggled) self.sfw_menu.append(self.safe_mode) self.sfw_menu_item.set_sensitive(True) self.parent.update_indicator() except Exception: logger.exception(lambda: 'Oops, could not populate NSFW menu:')
class DesktopprDownloader(SimpleDownloader): DESCRIPTION = _("Random wallpapers from Desktoppr.co") @classmethod def get_info(cls): return { "name": "DesktopprDownloader", "description": DesktopprDownloader.DESCRIPTION, "author": "Peter Levi", "version": "0.1", } def get_source_type(self): return "desktoppr" def get_description(self): return DesktopprDownloader.DESCRIPTION def get_source_name(self): return "Desktoppr.co" def get_folder_name(self): return "Desktoppr" def fill_queue(self): response = Util.fetch_json( "https://api.desktoppr.co/1/wallpapers/random") if response["response"]["review_state"] != "safe": logger.info( lambda: "Non-safe image returned by Desktoppr, skipping") return None origin_url = response["response"]["url"] image_url = response["response"]["image"]["url"] return [QueueItem(origin_url, image_url, {})]
def fetch( url, to_folder, origin_url=None, source_type=None, source_location=None, source_name=None, extra_metadata=None, progress_reporter=lambda a, b: None, verbose=True, ): reported = verbose try: logger.info(lambda: "Trying to fetch URL %s to %s " % (url, to_folder)) if verbose: progress_reporter(_("Fetching"), url) if url.startswith("javascript:"): if verbose: progress_reporter(_("Not an image"), url) return None if url.find("://") < 0: url = "file://" + url r = Util.request(url, stream=True) if not "content-type" in r.headers: logger.info(lambda: "Unknown content-type for url " + url) if verbose: progress_reporter(_("Not an image"), url) return None ct = r.headers["content-type"] if not ct.startswith("image/"): logger.info(lambda: "Unsupported content-type for url " + url + ": " + ct) if verbose: progress_reporter(_("Not an image"), url) return None local_name = Util.get_local_name(r.url) if "content-disposition" in r.headers: cd = r.headers["content-disposition"] cd_name = ImageFetcher.extract_filename_from_content_disposition(cd) if cd_name: local_name = cd_name filename = os.path.join(to_folder, local_name) if os.path.exists(filename): m = Util.read_metadata(filename) if m and m.get("imageURL") == url: logger.info(lambda: "Local file already exists (%s)" % filename) return filename else: logger.info( lambda: "File with same name already exists, but from different imageURL; renaming new download" ) filename = Util.find_unique_name(filename) logger.info(lambda: "Fetching to " + filename) if not reported: reported = True progress_reporter(_("Fetching"), url) local_filepath_partial = filename + ".partial" with open(local_filepath_partial, "wb") as f: Util.request_write_to(r, f) try: img = Image.open(local_filepath_partial) except Exception: progress_reporter(_("Not an image"), url) Util.safe_unlink(local_filepath_partial) return None if img.size[0] < 400 or img.size[1] < 400: # too small - delete and do not use progress_reporter(_("Image too small, ignoring it"), url) Util.safe_unlink(local_filepath_partial) return None metadata = { "sourceType": source_type or "fetched", "sourceName": source_name or "Fetched", "sourceURL": origin_url or url, "imageURL": url, } if source_location: metadata["sourceLocation"] = source_location metadata.update(extra_metadata or {}) Util.write_metadata(local_filepath_partial, metadata) os.rename(local_filepath_partial, filename) logger.info(lambda: "Fetched %s to %s." % (url, filename)) return filename except Exception as e: # pylint: disable=no-member logger.exception(lambda: "Fetch failed for URL " + url) if reported: if isinstance(e, HTTPError) and e.response.status_code in (403, 404): progress_reporter( _("Sorry, got %s error...") % str(e.response.status_code), _("This means the link is no longer valid"), ) else: progress_reporter( _("Fetch failed for some reason"), _( "To get more information, please run Variety from terminal with -v option and retry the action" ), ) return None
def get_one_quote(self): keywords = [] if self.parent.options.quotes_tags.strip(): keywords = self.parent.options.quotes_tags.split(",") authors = [] if self.parent.options.quotes_authors.strip(): authors = self.parent.options.quotes_authors.split(",") category, search = ("random", "") if keywords or authors: category, search = random.choice( [("keyword", k) for k in keywords] + [("author", a) for a in authors] ) plugins = list(self.plugins) if not plugins: self.parent.show_notification( _("No quote plugins"), _("There are no quote plugins installed") ) raise Exception("No quote plugins") if keywords or authors: plugins = [p for p in self.plugins if p["plugin"].supports_search()] if not plugins: self.parent.show_notification( _("No suitable quote plugins"), _("You have no quote plugins which support searching by keywords and authors"), ) raise Exception("No quote plugins") error_plugins = [] count_plugins = len(plugins) while self.running and self.parent.options.quotes_enabled: if not plugins: if ( time.time() - self.last_error_notification_time > 3600 and len(self.prepared) + len(self.used) < 5 ): self.last_error_notification_time = time.time() if len(error_plugins) == count_plugins: self.parent.show_notification( _("Could not fetch quotes"), _("Quotes services may be down, we will continue trying"), ) else: self.parent.show_notification( _("Could not find quotes"), _("Maybe you are searching for something very obscure?"), ) return None plugin = random.choice(plugins) plugin_name = plugin["info"]["name"] self.cache.setdefault(plugin_name, {"random": {}, "keyword": {}, "author": {}}) self.cache[plugin_name][category].setdefault(search, {}) cached = self.cache[plugin_name][category][search] if not cached: try: if category == "random": quotes = plugin["plugin"].get_random() elif category == "keyword": quotes = plugin["plugin"].get_for_keyword(search) elif category == "author": quotes = plugin["plugin"].get_for_author(search) else: raise RuntimeError("Unknown category") if quotes: for q in quotes: if len(q["quote"]) < self.parent.options.quotes_max_length: cached[q["quote"]] = q except Exception: logger.exception(lambda: "Exception in quote plugin") plugins.remove(plugin) error_plugins.append(plugin) continue if not cached: logger.warning(lambda: "No quotes for '%s' for plugin %s" % (search, plugin_name)) plugins.remove(plugin) continue quote = random.choice(list(cached.values())) del cached[quote["quote"]] if not cached: del cached return quote
def get_one_quote(self): keywords = [] if self.parent.options.quotes_tags.strip(): keywords = self.parent.options.quotes_tags.split(",") authors = [] if self.parent.options.quotes_authors.strip(): authors = self.parent.options.quotes_authors.split(",") category, search = ("random", "") if keywords or authors: category, search = random.choice( map(lambda k: ("keyword", k), keywords) + map(lambda a: ("author", a), authors)) plugins = list(self.plugins) if not plugins: self.parent.show_notification(_("No quote plugins"), _("There are no quote plugins installed")) raise Exception("No quote plugins") if keywords or authors: plugins = [p for p in self.plugins if p["plugin"].supports_search()] if not plugins: self.parent.show_notification( _("No suitable quote plugins"), _("You have no quote plugins which support searching by keywords and authors")) raise Exception("No quote plugins") error_plugins = [] count_plugins = len(plugins) while self.running and self.parent.options.quotes_enabled: if not plugins: if time.time() - self.last_error_notification_time > 3600 and len(self.prepared) + len( self.used) < 5: self.last_error_notification_time = time.time() if len(error_plugins) == count_plugins: self.parent.show_notification( _("Could not fetch quotes"), _("Quotes services may be down, we will continue trying")) else: self.parent.show_notification( _("Could not find quotes"), _("Maybe you are searching for something very obscure?")) return None plugin = random.choice(plugins) plugin_name = plugin["info"]["name"] self.cache.setdefault(plugin_name, {"random": {}, "keyword": {}, "author": {}}) self.cache[plugin_name][category].setdefault(search, {}) cached = self.cache[plugin_name][category][search] if not cached: try: if category == "random": quotes = plugin["plugin"].get_random() elif category == "keyword": quotes = plugin["plugin"].get_for_keyword(search) elif category == "author": quotes = plugin["plugin"].get_for_author(search) else: raise RuntimeError("Unknown category") for q in quotes: if len(q["quote"]) < 250: cached[q["quote"]] = q except Exception: logger.exception(lambda: "Exception in quote plugin") plugins.remove(plugin) error_plugins.append(plugin) continue if not cached: logger.warning(lambda: "No quotes for '%s' for plugin %s" % (search, plugin_name)) plugins.remove(plugin) continue quote = random.choice(cached.values()) del cached[quote["quote"]] if not cached: del cached return quote
def validate(self, query): valid = WallhavenDownloader.validate(query) return query, None if valid else _("No images found")
def validate(self, url): if not url.startswith("http://") and not url.startswith("https://"): url = "https://" + url valid = MediaRssDownloader.validate(url) return url, None if valid else _("This does not seem to be a valid Media RSS feed URL or there is no content there.")
# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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/>. ### END LICENSE from variety import _ FILTERS = { "Keep original": _("Keep original"), "Grayscale": _("Grayscale"), "Heavy blur": _("Heavy blur"), "Soft blur": _("Soft blur"), "Oil painting": _("Oil painting"), "Pointilism": _("Pointilism"), "Pixellate": _("Pixellate"), } SOURCES = { "favorites": ("The Favorites folder", _("The Favorites folder")), "fetched": ("The Fetched folder", _("The Fetched folder")), "recommended": ( "Recommended by Variety. Adapts to your taste as you mark images as favorite or trash.", _("Recommended by Variety. Adapts to your taste as you mark images as favorite or trash." ),
def fetch(url, to_folder, origin_url=None, source_type=None, source_location=None, source_name=None, extra_metadata={}, progress_reporter=lambda a, b: None, verbose=True): reported = verbose try: logger.info(lambda: "Trying to fetch URL %s to %s " % (url, to_folder)) if verbose: progress_reporter(_("Fetching"), url) if url.startswith('javascript:'): if verbose: progress_reporter(_("Not an image"), url) return None if url.find('://') < 0: url = "file://" + url r = Util.request(url, stream=True) if not "content-type" in r.headers: logger.info(lambda: "Unknown content-type for url " + url) if verbose: progress_reporter(_("Not an image"), url) return None ct = r.headers["content-type"] if not ct.startswith("image/"): logger.info(lambda: "Unsupported content-type for url " + url + ": " + ct) if verbose: progress_reporter(_("Not an image"), url) return None local_name = Util.get_local_name(r.url) if "content-disposition" in r.headers: cd = r.headers["content-disposition"] cd_name = ImageFetcher.extract_filename_from_content_disposition(cd) if cd_name: local_name = cd_name filename = os.path.join(to_folder, local_name) if os.path.exists(filename): m = Util.read_metadata(filename) if m and m.get("imageURL") == url: logger.info(lambda: "Local file already exists (%s)" % filename) return filename else: logger.info(lambda: "File with same name already exists, but from different imageURL; renaming new download") filename = Util.find_unique_name(filename) local_name = os.path.basename(filename) logger.info(lambda: "Fetching to " + filename) if not reported: reported = True progress_reporter(_("Fetching"), url) with open(filename, 'wb') as f: Util.request_write_to(r, f) try: img = Image.open(filename) except Exception: progress_reporter(_("Not an image"), url) os.unlink(filename) return None if img.size[0] < 400 or img.size[1] < 400: # too small - delete and do not use progress_reporter(_("Image too small, ignoring it"), url) os.unlink(filename) return None metadata = {"sourceType": source_type or 'fetched', "sourceName": source_name or "Fetched", "sourceURL": origin_url or url, "imageURL": url} if source_location: metadata["sourceLocation"] = source_location metadata.update(extra_metadata) Util.write_metadata(filename, metadata) logger.info(lambda: "Fetched %s to %s." % (url, filename)) return filename except Exception, e: logger.exception(lambda: "Fetch failed for URL " + url) if reported: if isinstance(e, HTTPError) and e.response.status_code in (403, 404): progress_reporter( _("Sorry, got %s error...") % str(e.response.status_code), _("This means the link is no longer valid")) else: progress_reporter( _("Fetch failed for some reason"), _("To get more information, please run Variety from terminal with -v option and retry the action")) return None
class APODDownloader(SimpleDownloader): DESCRIPTION = _("NASA Astro Pic of the Day") ROOT_URL = "http://apod.nasa.gov/apod/" @classmethod def get_info(cls): return { "name": "APODDownloader", "description": APODDownloader.DESCRIPTION, "author": "Peter Levi", "version": "0.1" } def get_description(self): return APODDownloader.DESCRIPTION def get_source_type(self): return "apod" def get_source_name(self): return "NASA Astro Pic of the Day" def get_folder_name(self): return "nasa_apod" def get_source_location(self): return self.ROOT_URL def fill_queue(self): logger.info(lambda: "Filling APOD queue from Archive") s = Util.html_soup(self.ROOT_URL + "archivepix.html") urls = [ self.ROOT_URL + x["href"] for x in s.findAll("a") if x["href"].startswith("ap") and x["href"].endswith(".html") ] urls = urls[:730] # leave only last 2 years' pics urls = [x for x in urls if not self.is_in_banned(x)] queue = urls[:3] # always put the latest 3 first urls = urls[3:] random.shuffle(urls) # shuffle the rest queue.extend(urls) return queue def download_queue_item(self, queue_item): origin_url = queue_item logger.info(lambda: "APOD URL: " + origin_url) s = Util.html_soup(origin_url) img_url = None try: link = s.find("img").parent["href"] if link.startswith("image/"): img_url = self.ROOT_URL + link logger.info(lambda: "Image URL: " + img_url) except Exception: pass if img_url: return self.save_locally(origin_url, img_url, source_location=self.ROOT_URL) else: logger.info(lambda: "No image url found for this APOD URL") return None
def create_menu(self, window): self.menu = Gtk.Menu() if not Util.is_unity(): self.next_main = Gtk.MenuItem(_("_Next")) self.next_main.set_use_underline(True) self.next_main.connect("activate", window.next_wallpaper) self.menu.append(self.next_main) self.prev_main = Gtk.MenuItem(_("_Previous")) self.prev_main.set_use_underline(True) self.prev_main.connect("activate", window.prev_wallpaper) self.menu.append(self.prev_main) self.menu.append(Gtk.SeparatorMenuItem.new()) self.file_label = Gtk.MenuItem(_("Current desktop wallpaper")) self.file_label.connect("activate", window.open_file) self.menu.append(self.file_label) self.show_origin = Gtk.MenuItem(_("Show origin")) self.show_origin.connect("activate", window.on_show_origin) self.show_origin.set_sensitive(False) self.menu.append(self.show_origin) self.show_author = Gtk.MenuItem("Show author") self.show_author.connect("activate", window.on_show_author) self.show_author.set_sensitive(False) self.show_author.set_visible(False) self.menu.append(self.show_author) self.copy_to_favorites = Gtk.MenuItem(_("Copy to _Favorites")) self.copy_to_favorites.set_use_underline(True) self.copy_to_favorites.connect("activate", window.copy_to_favorites) self.menu.append(self.copy_to_favorites) self.move_to_favorites = Gtk.MenuItem(_("Move to Favorites")) self.move_to_favorites.set_use_underline(True) self.move_to_favorites.connect("activate", window.move_to_favorites) self.move_to_favorites.set_visible(False) self.menu.append(self.move_to_favorites) self.trash = Gtk.MenuItem(_("Delete to _Trash")) self.trash.set_use_underline(True) self.trash.connect("activate", window.move_to_trash) self.menu.append(self.trash) self.menu.append(Gtk.SeparatorMenuItem.new()) self.image_menu = Gtk.Menu() self.next = Gtk.MenuItem(_("_Next")) self.next.set_use_underline(True) self.next.connect("activate", window.next_wallpaper) self.image_menu.append(self.next) self.prev = Gtk.MenuItem(_("_Previous")) self.prev.set_use_underline(True) self.prev.connect("activate", window.prev_wallpaper) self.image_menu.append(self.prev) self.fast_forward = Gtk.MenuItem(_("_Next, skipping forward history")) self.fast_forward.set_use_underline(True) def _fast_forward(widget): window.next_wallpaper(widget, bypass_history=True) self.fast_forward.connect("activate", _fast_forward) self.image_menu.append(self.fast_forward) self.image_menu.append(Gtk.SeparatorMenuItem.new()) self.scroll_tip = Gtk.MenuItem(_("Tip: Scroll wheel over icon\nfor Next and Previous")) self.scroll_tip.set_sensitive(False) self.image_menu.append(self.scroll_tip) self.image_menu.append(Gtk.SeparatorMenuItem.new()) self.pause_resume = Gtk.MenuItem(_("Pause on current")) self.pause_resume.connect("activate", window.on_pause_resume) self.image_menu.append(self.pause_resume) self.image_item = Gtk.MenuItem(_("_Image")) self.image_item.set_use_underline(True) self.image_item.set_submenu(self.image_menu) self.menu.append(self.image_item) self.image_menu.append(Gtk.SeparatorMenuItem.new()) self.focus = Gtk.MenuItem(_("Where is it from?")) self.focus.connect("activate", window.focus_in_preferences) self.image_menu.append(self.focus) self.no_effects = Gtk.CheckMenuItem(_("Show without effects")) self.no_effects.set_active(False) self.no_effects.set_use_underline(True) def _toggle_no_effects(widget=None): window.toggle_no_effects(self.no_effects.get_active()) self.no_effects_handler_id = self.no_effects.connect("toggled", _toggle_no_effects) self.image_menu.append(self.no_effects) self.google_image = Gtk.MenuItem(_("Google Image Search")) self.google_image.connect("activate", window.google_image_search) self.image_menu.append(self.google_image) self.image_menu.append(Gtk.SeparatorMenuItem.new()) self.publish_fb = Gtk.MenuItem(_("Share on Facebook")) self.publish_fb.connect("activate", window.publish_on_facebook) self.image_menu.append(self.publish_fb) self.rating_separator = Gtk.SeparatorMenuItem.new() self.image_menu.append(self.rating_separator) self.rating = Gtk.MenuItem(_("Set EXIF Rating")) self.image_menu.append(self.rating) # self.image_item = Gtk.MenuItem(_("_Image")) # self.image_item.set_use_underline(True) # self.image_item.set_submenu(self.image_menu) # self.menu.append(self.image_item) # self.sfw_menu = Gtk.Menu() threading.Timer(2, self.populate_sfw_menu).start() self.sfw_menu_item = Gtk.MenuItem(_("Report NSFW image")) self.sfw_menu_item.set_sensitive(False) self.sfw_menu_item.set_use_underline(True) self.sfw_menu_item.set_submenu(self.sfw_menu) self.menu.append(self.sfw_menu_item) self.quotes_menu = Gtk.Menu() self.next_quote = Gtk.MenuItem(_("_Next")) self.next_quote.set_use_underline(True) self.next_quote.connect("activate", window.next_quote) self.quotes_menu.append(self.next_quote) self.prev_quote = Gtk.MenuItem(_("_Previous")) self.prev_quote.set_use_underline(True) self.prev_quote.connect("activate", window.prev_quote) self.quotes_menu.append(self.prev_quote) self.fast_forward_quote = Gtk.MenuItem(_("_Next, skipping forward history")) self.fast_forward_quote.set_use_underline(True) def _fast_forward_quote(widget): window.next_quote(widget, bypass_history=True) self.fast_forward_quote.connect("activate", _fast_forward_quote) self.quotes_menu.append(self.fast_forward_quote) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quotes_pause_resume = Gtk.MenuItem(_("Pause on current")) self.quotes_pause_resume.connect("activate", window.on_quotes_pause_resume) self.quotes_menu.append(self.quotes_pause_resume) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quote_favorite = Gtk.MenuItem(_("Save to Favorites")) self.quote_favorite.set_use_underline(True) self.quote_favorite.connect("activate", window.quote_save_to_favorites) self.quotes_menu.append(self.quote_favorite) self.quote_view_favs = Gtk.MenuItem(_("View Favorites...")) self.quote_view_favs.set_use_underline(True) self.quote_view_favs.connect("activate", window.quote_view_favorites) self.quotes_menu.append(self.quote_view_favs) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quote_clipboard = Gtk.MenuItem(_("Copy to Clipboard")) self.quote_clipboard.set_use_underline(True) self.quote_clipboard.connect("activate", window.quote_copy_to_clipboard) self.quotes_menu.append(self.quote_clipboard) self.view_quote = Gtk.MenuItem() self.view_quote.set_use_underline(True) self.view_quote.connect("activate", window.view_quote) self.quotes_menu.append(self.view_quote) self.google_quote_text = Gtk.MenuItem(_("Google Quote")) self.google_quote_text.set_use_underline(True) self.google_quote_text.connect("activate", window.google_quote_text) self.quotes_menu.append(self.google_quote_text) self.google_quote_author = Gtk.MenuItem(_("Google Author")) self.google_quote_author.set_use_underline(True) self.google_quote_author.connect("activate", window.google_quote_author) self.quotes_menu.append(self.google_quote_author) self.quote_fb = Gtk.MenuItem(_("Share on Facebook")) self.quote_fb.set_use_underline(True) self.quote_fb.connect("activate", window.publish_quote_on_facebook) self.quotes_menu.append(self.quote_fb) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quotes_preferences = Gtk.MenuItem(_("Preferences...")) self.quotes_preferences.set_use_underline(True) def _quotes_prefs(widget=None): window.preferences_dialog.ui.notebook.set_current_page(1) window.on_mnu_preferences_activate() self.quotes_preferences.connect("activate", _quotes_prefs) self.quotes_menu.append(self.quotes_preferences) self.quotes_disable = Gtk.MenuItem(_("Turn off")) self.quotes_disable.set_use_underline(True) self.quotes_disable.connect("activate", window.disable_quotes) self.quotes_menu.append(self.quotes_disable) self.quotes = Gtk.MenuItem(_("_Quote")) self.quotes.set_use_underline(True) self.quotes.set_submenu(self.quotes_menu) self.menu.append(self.quotes) self.menu.append(Gtk.SeparatorMenuItem.new()) self.history = Gtk.CheckMenuItem(_("_History")) self.history.set_active(False) self.history.set_use_underline(True) self.history_handler_id = self.history.connect("toggled", window.show_hide_history) self.menu.append(self.history) self.selector = Gtk.CheckMenuItem(_("_Wallpaper Selector")) self.selector.set_active(False) self.selector.set_use_underline(True) self.selector_handler_id = self.selector.connect("toggled", window.show_hide_wallpaper_selector) self.menu.append(self.selector) self.downloads = Gtk.CheckMenuItem(_("Recent _Downloads")) self.downloads.set_active(False) self.downloads.set_use_underline(True) self.downloads_handler_id = self.downloads.connect("toggled", window.show_hide_downloads) self.menu.append(self.downloads) try: from varietyslideshow import varietyslideshow except: logger.warning('Variety Slideshow is not installed. ' 'Install it with "sudo apt-get install variety-slideshow", ' 'read more here: http://peterlevi.com/variety/slideshow/') else: self.menu.append(Gtk.SeparatorMenuItem.new()) self.slideshow = Gtk.MenuItem(_("Start Slideshow")) self.slideshow.connect("activate", window.on_start_slideshow) self.menu.append(self.slideshow) self.menu.append(Gtk.SeparatorMenuItem.new()) self.preferences = Gtk.MenuItem(_("Preferences...")) self.preferences.connect("activate", window.on_mnu_preferences_activate) self.menu.append(self.preferences) self.about = Gtk.MenuItem(_("About")) self.about.connect("activate", window.on_mnu_about_activate) self.menu.append(self.about) self.donate = Gtk.MenuItem(_("Donate")) self.donate.connect("activate", window.on_mnu_donate_activate) self.menu.append(self.donate) self.quit = Gtk.MenuItem(_("Quit")) self.quit.connect("activate",window.on_quit) self.menu.append(self.quit) self.menu.show_all()
class UnsplashDownloader(SimpleDownloader): DESCRIPTION = _("High-resolution photos from Unsplash.com") CLIENT_ID = "072e5048dfcb73a8d9ad59fcf402471518ff8df725df462b0c4fa665f466515a" UTM_PARAMS = "?utm_source=Variety+Wallpaper+Changer&utm_medium=referral" rate_limiting_started_time = 0 @classmethod def get_info(cls): return { "name": "UnsplashDownloader", "description": UnsplashDownloader.DESCRIPTION, "author": "Peter Levi", "version": "0.1", } def get_source_type(self): return "unsplash" def get_description(self): return UnsplashDownloader.DESCRIPTION def get_source_name(self): return "Unsplash.com" def get_source_location(self): return "https://unsplash.com" def get_folder_name(self): return "Unsplash" def get_server_options_key(self): return "unsplash_v2" def get_default_throttling(self): return Throttling(min_download_interval=600, min_fill_queue_interval=1800) def fill_queue(self): if time.time() - UnsplashDownloader.rate_limiting_started_time < 3600: logger.info( lambda: "Unsplash queue empty, but rate limit reached, will try again later" ) return [] page = random.randint(1, 250) url = "https://api.unsplash.com/photos/?page=%d&per_page=30&client_id=%s" % ( page, UnsplashDownloader.CLIENT_ID, ) logger.info(lambda: "Filling Unsplash queue from " + url) r = Util.request(url) if int(r.headers.get("X-Ratelimit-Remaining", 1000000)) < 100: UnsplashDownloader.rate_limiting_started_time = time.time() queue = [] for item in r.json(): try: width = item["width"] height = item["height"] if self.is_size_inadequate(width, height): continue image_url = item["urls"]["full"] origin_url = item["links"][ "html"] + UnsplashDownloader.UTM_PARAMS extra_metadata = { "sourceType": "unsplash", "sfwRating": 100, "author": item["user"]["name"], "authorURL": item["user"]["links"]["html"] + UnsplashDownloader.UTM_PARAMS, "keywords": [ cat["title"].lower().strip() for cat in item["categories"] ], "extraData": { "unsplashDownloadLocation": item["links"]["download_location"], "unsplashDownloadReported": False, }, } queue.append((origin_url, image_url, extra_metadata)) except: logger.exception( lambda: "Could not process an item from Unsplash") raise random.shuffle(queue) return queue def on_image_set_as_wallpaper(self, img, meta): extraData = meta.get("extraData", None) if not extraData: return download_loc = extraData.get("unsplashDownloadLocation") reported = extraData.get("unsplashDownloadReported") if download_loc and not reported: url = "{}?client_id={}".format(download_loc, UnsplashDownloader.CLIENT_ID) Util.fetch(url) meta["extraData"]["unsplashDownloadReported"] = True Util.write_metadata(img, meta)
def create_menu(self, file): options = self.load_options() menu = Gtk.Menu() position_menu = Gtk.Menu() for p, v in ThumbsManager.POSITIONS.items(): item = Gtk.CheckMenuItem(ThumbsManager.POSITION_NAMES[p]) item.set_draw_as_radio(True) item.set_active(options.position == v) def _set_position(widget, pos=p): self.set_position(pos) item.connect("activate", _set_position) position_menu.append(item) size_menu = Gtk.Menu() for size in ThumbsManager.SIZES: item = Gtk.CheckMenuItem(str(size)) item.set_draw_as_radio(True) item.set_active(options.breadth == size) def _set_size(widget, size=size): self.set_size(size) item.connect("activate", _set_size) size_menu.append(item) position_item = Gtk.MenuItem(_("Position")) position_item.set_submenu(position_menu) menu.append(position_item) size_item = Gtk.MenuItem(_("Size")) size_item.set_submenu(size_menu) menu.append(size_item) menu.append(Gtk.SeparatorMenuItem.new()) open_file = Gtk.MenuItem(os.path.basename(file).replace('_', '__')) def _open_file(widget): self.parent.open_file(widget, file) open_file.connect("activate", _open_file) menu.append(open_file) open_folder = Gtk.MenuItem(_("Show Containing Folder")) def _open_folder(widget): self.parent.open_folder(widget, file) open_folder.connect("activate", _open_folder) menu.append(open_folder) info = Util.read_metadata(file) if info and "sourceURL" in info and "sourceName" in info: url = info["sourceURL"] source_name = info["sourceName"] if "Fetched" in source_name: label = _("Fetched: Show Origin") else: label = _("View at %s") % source_name if len(label) > 50: label = label[:50] + "..." show_origin = Gtk.MenuItem(label) def _show_origin(widget=None): logger.info(lambda: "Opening url: " + url) webbrowser.open_new_tab(url) show_origin.connect("activate", _show_origin) menu.append(show_origin) menu.append(Gtk.SeparatorMenuItem.new()) rating_item = Gtk.MenuItem(_("Set EXIF Rating")) rating_item.set_submenu(ThumbsManager.create_rating_menu(file, self.parent)) if not os.access(file, os.W_OK): rating_item.set_sensitive(False) menu.append(rating_item) menu.append(Gtk.SeparatorMenuItem.new()) self.copy_to_favorites = Gtk.MenuItem(_("Copy to _Favorites")) self.copy_to_favorites.set_use_underline(True) def _copy_to_favorites(widget): self.parent.copy_to_favorites(widget, file) self.copy_to_favorites.connect("activate", _copy_to_favorites) menu.append(self.copy_to_favorites) self.move_to_favorites = Gtk.MenuItem(_("Move to _Favorites")) self.move_to_favorites.set_use_underline(True) def _move_to_favorites(widget): self.parent.move_to_favorites(widget, file) self.remove_image(file) self.move_to_favorites.connect("activate", _move_to_favorites) self.move_to_favorites.set_visible(False) menu.append(self.move_to_favorites) trash_item = Gtk.MenuItem(_("Delete to _Trash")) trash_item.set_use_underline(True) def _trash(widget): self.parent.move_to_trash(widget, file) trash_item.connect("activate", _trash) menu.append(trash_item) focus = Gtk.MenuItem(_("Where is it from?")) focus.set_sensitive(self.parent.get_source(file) is not None) def _focus(widget): self.parent.focus_in_preferences(widget, file) focus.connect("activate", _focus) menu.append(focus) menu.append(Gtk.SeparatorMenuItem.new()) def close(widget): self.hide(gdk_thread=True, force=True) close_item = Gtk.MenuItem(_("Close")) close_item.connect("activate", close) menu.append(close_item) menu.show_all() favs_op = self.parent.determine_favorites_operation(file) self.parent.update_favorites_menuitems(self, False, favs_op) return menu
class ThumbsManager: POSITIONS = { "bottom": ThumbsWindow.BOTTOM, "top": ThumbsWindow.TOP, "left": ThumbsWindow.LEFT, "right": ThumbsWindow.RIGHT, } POSITION_NAMES = { "bottom": _("Bottom"), "top": _("Top"), "left": _("Left"), "right": _("Right"), } R_POSITIONS = {v: k for (k, v) in POSITIONS.items()} SIZES = [x * 30 for x in range(2, 11)] UNLIMITED = "Unlimited" LIMITS = [10, 50, 100, 200, 500, 1000, 2000, UNLIMITED] class Options: def __init__(self): self.position = ThumbsWindow.BOTTOM self.breadth = 120 self.limit = 200 def __init__(self, parent): self.parent = parent self.thumbs_window = None self.show_thumbs_lock = threading.Lock() self.pinned = False self.images = [] self.screen = None self.type = None self.folders = None self.active_file = None self.active_position = None def create_menu(self, file): options = self.load_options() menu = Gtk.Menu() position_menu = Gtk.Menu() for p, v in ThumbsManager.POSITIONS.items(): item = Gtk.CheckMenuItem(ThumbsManager.POSITION_NAMES[p]) item.set_draw_as_radio(True) item.set_active(options.position == v) def _set_position(widget, pos=p): self.set_position(pos) item.connect("activate", _set_position) position_menu.append(item) size_menu = Gtk.Menu() for size in ThumbsManager.SIZES: item = Gtk.CheckMenuItem(str(size)) item.set_draw_as_radio(True) item.set_active(options.breadth == size) def _set_size(widget, size=size): self.set_size(size) item.connect("activate", _set_size) size_menu.append(item) limit_menu = Gtk.Menu() for limit in ThumbsManager.LIMITS: item = Gtk.CheckMenuItem(str(limit)) item.set_draw_as_radio(True) item.set_active(options.limit == limit) def _set_limit(widget, limit=limit): self.set_limit(limit) item.connect("activate", _set_limit) limit_menu.append(item) position_item = Gtk.MenuItem(_("Position")) position_item.set_submenu(position_menu) menu.append(position_item) size_item = Gtk.MenuItem(_("Size")) size_item.set_submenu(size_menu) menu.append(size_item) limit_item = Gtk.MenuItem(_("Maximum Shown Images")) limit_item.set_submenu(limit_menu) menu.append(limit_item) menu.append(Gtk.SeparatorMenuItem.new()) open_file = Gtk.MenuItem(os.path.basename(file).replace("_", "__")) def _open_file(widget): self.parent.open_file(widget, file) open_file.connect("activate", _open_file) menu.append(open_file) open_folder = Gtk.MenuItem(_("Show Containing Folder")) def _open_folder(widget): self.parent.open_folder(widget, file) open_folder.connect("activate", _open_folder) menu.append(open_folder) info = Util.read_metadata(file) if info and "sourceURL" in info and "sourceName" in info: url = info["sourceURL"] source_name = info["sourceName"] if "Fetched" in source_name: label = _("Fetched: Show Origin") else: label = _("View at %s") % source_name if len(label) > 50: label = label[:50] + "..." show_origin = Gtk.MenuItem(label) def _show_origin(widget=None): logger.info(lambda: "Opening url: " + url) webbrowser.open_new_tab(url) show_origin.connect("activate", _show_origin) menu.append(show_origin) menu.append(Gtk.SeparatorMenuItem.new()) rating_item = Gtk.MenuItem(_("Set EXIF Rating")) rating_item.set_submenu( ThumbsManager.create_rating_menu(file, self.parent)) if not os.access(file, os.W_OK): rating_item.set_sensitive(False) menu.append(rating_item) menu.append(Gtk.SeparatorMenuItem.new()) self.copy_to_favorites = Gtk.MenuItem(_("Copy to _Favorites")) self.copy_to_favorites.set_use_underline(True) def _copy_to_favorites(widget): self.parent.copy_to_favorites(widget, file) self.copy_to_favorites.connect("activate", _copy_to_favorites) menu.append(self.copy_to_favorites) self.move_to_favorites = Gtk.MenuItem(_("Move to _Favorites")) self.move_to_favorites.set_use_underline(True) def _move_to_favorites(widget): self.parent.move_to_favorites(widget, file) self.remove_image(file) self.move_to_favorites.connect("activate", _move_to_favorites) self.move_to_favorites.set_visible(False) menu.append(self.move_to_favorites) trash_item = Gtk.MenuItem(_("Delete to _Trash")) trash_item.set_use_underline(True) def _trash(widget): self.parent.move_to_trash(widget, file) trash_item.connect("activate", _trash) menu.append(trash_item) focus = Gtk.MenuItem(_("Where is it from?")) focus.set_sensitive(self.parent.get_source(file) is not None) def _focus(widget): self.parent.focus_in_preferences(widget, file) focus.connect("activate", _focus) menu.append(focus) menu.append(Gtk.SeparatorMenuItem.new()) def close(widget): self.hide(force=True) close_item = Gtk.MenuItem(_("Close")) close_item.connect("activate", close) menu.append(close_item) menu.show_all() favs_op = self.parent.determine_favorites_operation(file) self.parent.update_favorites_menuitems(self, False, favs_op) return menu @staticmethod def create_rating_menu(file, main_window): def _set_rating_maker(rating): def _set_rating(widget, rating=rating): try: Util.set_rating(file, rating) main_window.on_rating_changed(file) except Exception: logger.exception(lambda: "Could not set EXIF rating") main_window.show_notification( _("Could not set EXIF rating")) return _set_rating try: actual_rating = Util.get_rating(file) except Exception: actual_rating = None rating_menu = Gtk.Menu() for rating in range(5, 0, -1): item = Gtk.CheckMenuItem("\u2605" * rating) item.set_draw_as_radio(True) item.set_active(actual_rating == rating) item.set_sensitive(not item.get_active()) item.connect("activate", _set_rating_maker(rating)) rating_menu.append(item) rating_menu.append(Gtk.SeparatorMenuItem.new()) unrated_item = Gtk.CheckMenuItem(_("Unrated")) unrated_item.set_draw_as_radio(True) unrated_item.set_active(actual_rating is None or actual_rating == 0) unrated_item.set_sensitive(not unrated_item.get_active()) unrated_item.connect("activate", _set_rating_maker(None)) rating_menu.append(unrated_item) rejected_item = Gtk.CheckMenuItem(_("Rejected")) rejected_item.set_draw_as_radio(True) rejected_item.set_active(actual_rating is not None and actual_rating < 0) rejected_item.set_sensitive(not rejected_item.get_active()) rejected_item.connect("activate", _set_rating_maker(-1)) rating_menu.append(rejected_item) rating_menu.show_all() return rating_menu def repaint(self): self.hide(keep_settings=True) if self.images: self.show(self.unlimited_images, screen=self.screen, type=self.type, folders=self.folders) def set_position(self, position): logger.info(lambda: "Setting thumbs position " + str(position)) options = self.load_options() options.position = ThumbsManager.POSITIONS[position] self.save_options(options) self.repaint() def set_size(self, size): logger.info(lambda: "Setting thumbs size " + str(size)) options = self.load_options() options.breadth = size self.save_options(options) self.repaint() def set_limit(self, limit): logger.info(lambda: "Setting thumbs limit " + str(limit)) options = self.load_options() options.limit = limit self.save_options(options) self.repaint() def pin(self, widget=None): self.pinned = True def on_click(self, thumbs_window, file, widget, event): file = file self.pin() def _resume_scrolling(menu=None): thumbs_window.resume_scrolling() thumbs_window.pause_scrolling() if event.button == 1: if self.is_showing("history"): index = [info["eventbox"] for info in thumbs_window.all].index(widget) self.parent.move_to_history_position(index) else: self.parent.set_wallpaper(file) _resume_scrolling() else: menu = self.create_menu(file) def _compute_position(*args, **kwargs): x, y = event.get_root_coords()[0], event.get_root_coords()[1] h = menu.get_preferred_height()[1] return x, y - h if y - h >= 40 else y, True menu.connect("deactivate", _resume_scrolling) menu.popup(None, None, _compute_position, None, 0, event.time) def mark_active(self, file=None, position=None): self.active_file = file self.active_position = position if self.thumbs_window: if self.is_showing("history"): self.thumbs_window.mark_active(position=position) else: self.thumbs_window.mark_active(file=file) def show(self, images, screen=None, type=None, folders=None): options = self.load_options() with self.show_thumbs_lock: self.unlimited_images = images self.type = type limit = len(self.unlimited_images ) if options.limit == self.UNLIMITED else options.limit self.images = self.unlimited_images[:limit] self.screen = screen self.folders = folders def _go(): try: if self.thumbs_window: self.thumbs_window.destroy() self.thumbs_window = None if len(self.images) > 0: self.initialize_thumbs_window() except Exception: logger.exception(lambda: "Could not create thumbs window:") Util.add_mainloop_task(_go) def initialize_thumbs_window(self): options = self.load_options() def _go(): self.thumbs_window = ThumbsWindow(screen=self.screen, position=options.position, breadth=options.breadth) try: icon = varietyconfig.get_data_file("media", "variety.svg") self.thumbs_window.set_icon_from_file(icon) except Exception: logger.exception(lambda: "Could not set thumbs window icon") if self.type == "history": title = _("Variety History") elif self.type == "downloads": title = _("Variety Recent Downloads") else: title = _("Variety Images") self.thumbs_window.set_title(title) self.thumbs_window.connect("clicked", self.on_click) def _on_close(window, event): self.hide(force=True) self.thumbs_window.connect("delete-event", _on_close) self.mark_active(self.active_file, self.active_position) self.thumbs_window.start(self.images) Util.add_mainloop_task(_go) def load_options(self): options = ThumbsManager.Options() try: config = ConfigObj( os.path.join(self.parent.config_folder, "ui.conf")) try: s = config["thumbs_position"].lower() options.position = ThumbsManager.POSITIONS[s] except Exception: logger.exception( lambda: "Missing or bad thumbs_position option in ui.conf") try: options.breadth = int(config["thumbs_size"]) except Exception: logger.exception( lambda: "Missing or bad thumbs_size option in ui.conf") try: options.limit = (int(config["thumbs_limit"]) if config["thumbs_limit"] != self.UNLIMITED else self.UNLIMITED) except Exception: logger.exception( lambda: "Missing or bad thumbs_limit option in ui.conf") except Exception: logger.exception(lambda: "Could not read ui.conf") self.save_options(options) return options def save_options(self, options): try: config = ConfigObj( os.path.join(self.parent.config_folder, "ui.conf")) try: config["thumbs_position"] = ThumbsManager.R_POSITIONS[ options.position] config["thumbs_size"] = options.breadth config["thumbs_limit"] = str(options.limit) config.write() except Exception: logger.exception( lambda: "Missing or bad thumbs_position option in ui.conf") except Exception: logger.exception(lambda: "Could not save ui.conf") def hide(self, force=True, keep_settings=False): if force: self.pinned = False if self.pinned: return if not keep_settings: self.type = None self.images = [] self.screen = None self.folders = None if self.thumbs_window: def _go(): self.thumbs_window.destroy() self.thumbs_window = None Util.add_mainloop_task(_go) self.parent.update_indicator(auto_changed=False) def remove_image(self, file): self.images = [f for f in self.images if f != file] if self.thumbs_window: if self.thumbs_window.fits_in_screen(+1000): self.repaint() else: self.thumbs_window.remove_image(file) def add_image(self, file): self.images.insert(0, file) if not self.thumbs_window: self.initialize_thumbs_window() else: self.thumbs_window.add_image(file, at_front=True) def is_showing(self, type): return self.type == type def get_folders(self): return self.folders
def create_menu(self, window): self.menu = Gtk.Menu() if not Util.is_unity(): self.next_main = Gtk.MenuItem(_("_Next")) self.next_main.set_use_underline(True) self.next_main.connect("activate", window.next_wallpaper) self.menu.append(self.next_main) self.prev_main = Gtk.MenuItem(_("_Previous")) self.prev_main.set_use_underline(True) self.prev_main.connect("activate", window.prev_wallpaper) self.menu.append(self.prev_main) self.menu.append(Gtk.SeparatorMenuItem.new()) self.file_label = Gtk.MenuItem(_("Current desktop wallpaper")) self.file_label.connect("activate", window.open_file) self.menu.append(self.file_label) self.show_origin = Gtk.MenuItem(_("Show origin")) self.show_origin.connect("activate", window.on_show_origin) self.show_origin.set_sensitive(False) self.menu.append(self.show_origin) self.show_author = Gtk.MenuItem("Show author") self.show_author.connect("activate", window.on_show_author) self.show_author.set_sensitive(False) self.show_author.set_visible(False) self.menu.append(self.show_author) self.copy_to_favorites = Gtk.MenuItem(_("Copy to _Favorites")) self.copy_to_favorites.set_use_underline(True) self.copy_to_favorites.connect("activate", window.copy_to_favorites) self.menu.append(self.copy_to_favorites) self.move_to_favorites = Gtk.MenuItem(_("Move to Favorites")) self.move_to_favorites.set_use_underline(True) self.move_to_favorites.connect("activate", window.move_to_favorites) self.move_to_favorites.set_visible(False) self.menu.append(self.move_to_favorites) self.trash = Gtk.MenuItem(_("Delete to _Trash")) self.trash.set_use_underline(True) self.trash.connect("activate", window.move_to_trash) self.menu.append(self.trash) self.menu.append(Gtk.SeparatorMenuItem.new()) self.image_menu = Gtk.Menu() self.next = Gtk.MenuItem(_("_Next")) self.next.set_use_underline(True) self.next.connect("activate", window.next_wallpaper) self.image_menu.append(self.next) self.prev = Gtk.MenuItem(_("_Previous")) self.prev.set_use_underline(True) self.prev.connect("activate", window.prev_wallpaper) self.image_menu.append(self.prev) self.fast_forward = Gtk.MenuItem(_("_Next, skipping forward history")) self.fast_forward.set_use_underline(True) def _fast_forward(widget): window.next_wallpaper(widget, bypass_history=True) self.fast_forward.connect("activate", _fast_forward) self.image_menu.append(self.fast_forward) self.image_menu.append(Gtk.SeparatorMenuItem.new()) self.scroll_tip = Gtk.MenuItem( _("Tip: Scroll wheel over icon\nfor Next and Previous")) self.scroll_tip.set_sensitive(False) self.image_menu.append(self.scroll_tip) self.image_menu.append(Gtk.SeparatorMenuItem.new()) self.pause_resume = Gtk.MenuItem(_("Pause on current")) self.pause_resume.connect("activate", window.on_pause_resume) self.image_menu.append(self.pause_resume) self.image_item = Gtk.MenuItem(_("_Image")) self.image_item.set_use_underline(True) self.image_item.set_submenu(self.image_menu) self.menu.append(self.image_item) self.image_menu.append(Gtk.SeparatorMenuItem.new()) self.focus = Gtk.MenuItem(_("Where is it from?")) self.focus.connect("activate", window.focus_in_preferences) self.image_menu.append(self.focus) self.no_effects = Gtk.CheckMenuItem(_("Show without effects")) self.no_effects.set_active(False) self.no_effects.set_use_underline(True) def _toggle_no_effects(widget=None): window.toggle_no_effects(self.no_effects.get_active()) self.no_effects_handler_id = self.no_effects.connect( "toggled", _toggle_no_effects) self.image_menu.append(self.no_effects) self.google_image = Gtk.MenuItem(_("Google Image Search")) self.google_image.connect("activate", window.google_image_search) self.image_menu.append(self.google_image) self.rating_separator = Gtk.SeparatorMenuItem.new() self.image_menu.append(self.rating_separator) self.rating = Gtk.MenuItem(_("Set EXIF Rating")) self.image_menu.append(self.rating) # self.image_item = Gtk.MenuItem(_("_Image")) # self.image_item.set_use_underline(True) # self.image_item.set_submenu(self.image_menu) # self.menu.append(self.image_item) # self.quotes_menu = Gtk.Menu() self.next_quote = Gtk.MenuItem(_("_Next")) self.next_quote.set_use_underline(True) self.next_quote.connect("activate", window.next_quote) self.quotes_menu.append(self.next_quote) self.prev_quote = Gtk.MenuItem(_("_Previous")) self.prev_quote.set_use_underline(True) self.prev_quote.connect("activate", window.prev_quote) self.quotes_menu.append(self.prev_quote) self.fast_forward_quote = Gtk.MenuItem( _("_Next, skipping forward history")) self.fast_forward_quote.set_use_underline(True) def _fast_forward_quote(widget): window.next_quote(widget, bypass_history=True) self.fast_forward_quote.connect("activate", _fast_forward_quote) self.quotes_menu.append(self.fast_forward_quote) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quotes_pause_resume = Gtk.MenuItem(_("Pause on current")) self.quotes_pause_resume.connect("activate", window.on_quotes_pause_resume) self.quotes_menu.append(self.quotes_pause_resume) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quote_favorite = Gtk.MenuItem(_("Save to Favorites")) self.quote_favorite.set_use_underline(True) self.quote_favorite.connect("activate", window.quote_save_to_favorites) self.quotes_menu.append(self.quote_favorite) self.quote_view_favs = Gtk.MenuItem(_("View Favorites...")) self.quote_view_favs.set_use_underline(True) self.quote_view_favs.connect("activate", window.quote_view_favorites) self.quotes_menu.append(self.quote_view_favs) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quote_clipboard = Gtk.MenuItem(_("Copy to Clipboard")) self.quote_clipboard.set_use_underline(True) self.quote_clipboard.connect("activate", window.quote_copy_to_clipboard) self.quotes_menu.append(self.quote_clipboard) self.view_quote = Gtk.MenuItem() self.view_quote.set_use_underline(True) self.view_quote.connect("activate", window.view_quote) self.quotes_menu.append(self.view_quote) self.google_quote_text = Gtk.MenuItem(_("Google Quote")) self.google_quote_text.set_use_underline(True) self.google_quote_text.connect("activate", window.google_quote_text) self.quotes_menu.append(self.google_quote_text) self.google_quote_author = Gtk.MenuItem(_("Google Author")) self.google_quote_author.set_use_underline(True) self.google_quote_author.connect("activate", window.google_quote_author) self.quotes_menu.append(self.google_quote_author) self.quotes_menu.append(Gtk.SeparatorMenuItem.new()) self.quotes_preferences = Gtk.MenuItem(_("Preferences...")) self.quotes_preferences.set_use_underline(True) def _quotes_prefs(widget=None): window.preferences_dialog.ui.notebook.set_current_page(1) window.on_mnu_preferences_activate() self.quotes_preferences.connect("activate", _quotes_prefs) self.quotes_menu.append(self.quotes_preferences) self.quotes_disable = Gtk.MenuItem(_("Turn off")) self.quotes_disable.set_use_underline(True) self.quotes_disable.connect("activate", window.disable_quotes) self.quotes_menu.append(self.quotes_disable) self.quotes = Gtk.MenuItem(_("_Quote")) self.quotes.set_use_underline(True) self.quotes.set_submenu(self.quotes_menu) self.menu.append(self.quotes) self.menu.append(Gtk.SeparatorMenuItem.new()) self.history = Gtk.CheckMenuItem(_("_History")) self.history.set_active(False) self.history.set_use_underline(True) self.history_handler_id = self.history.connect( "toggled", window.show_hide_history) self.menu.append(self.history) self.selector = Gtk.CheckMenuItem(_("_Wallpaper Selector")) self.selector.set_active(False) self.selector.set_use_underline(True) self.selector_handler_id = self.selector.connect( "toggled", window.show_hide_wallpaper_selector) self.menu.append(self.selector) self.downloads = Gtk.CheckMenuItem(_("Recent _Downloads")) self.downloads.set_active(False) self.downloads.set_use_underline(True) self.downloads_handler_id = self.downloads.connect( "toggled", window.show_hide_downloads) self.menu.append(self.downloads) if Util.check_variety_slideshow_present(): self.menu.append(Gtk.SeparatorMenuItem.new()) self.slideshow = Gtk.MenuItem(_("Start Slideshow")) self.slideshow.connect("activate", window.on_start_slideshow) self.menu.append(self.slideshow) else: logger.warning( "Variety Slideshow is not installed. This is an optional extension " "adding pan-and-zoom slideshows to Variety: see " "https://github.com/peterlevi/variety-slideshow for details") self.menu.append(Gtk.SeparatorMenuItem.new()) self.preferences = Gtk.MenuItem(_("Preferences...")) self.preferences.connect("activate", window.on_mnu_preferences_activate) self.menu.append(self.preferences) self.about = Gtk.MenuItem(_("About")) self.about.connect("activate", window.on_mnu_about_activate) self.menu.append(self.about) self.donate = Gtk.MenuItem(_("Donate")) self.donate.connect("activate", window.on_mnu_donate_activate) self.menu.append(self.donate) self.quit = Gtk.MenuItem(_("Quit")) self.quit.connect("activate", window.on_quit) self.menu.append(self.quit) self.menu.show_all()
def create_menu(self, file): options = self.load_options() menu = Gtk.Menu() position_menu = Gtk.Menu() for p, v in ThumbsManager.POSITIONS.items(): item = Gtk.CheckMenuItem(ThumbsManager.POSITION_NAMES[p]) item.set_draw_as_radio(True) item.set_active(options.position == v) def _set_position(widget, pos=p): self.set_position(pos) item.connect("activate", _set_position) position_menu.append(item) size_menu = Gtk.Menu() for size in ThumbsManager.SIZES: item = Gtk.CheckMenuItem(str(size)) item.set_draw_as_radio(True) item.set_active(options.breadth == size) def _set_size(widget, size=size): self.set_size(size) item.connect("activate", _set_size) size_menu.append(item) limit_menu = Gtk.Menu() for limit in ThumbsManager.LIMITS: item = Gtk.CheckMenuItem(str(limit)) item.set_draw_as_radio(True) item.set_active(options.limit == limit) def _set_limit(widget, limit=limit): self.set_limit(limit) item.connect("activate", _set_limit) limit_menu.append(item) position_item = Gtk.MenuItem(_("Position")) position_item.set_submenu(position_menu) menu.append(position_item) size_item = Gtk.MenuItem(_("Size")) size_item.set_submenu(size_menu) menu.append(size_item) limit_item = Gtk.MenuItem(_("Maximum Shown Images")) limit_item.set_submenu(limit_menu) menu.append(limit_item) menu.append(Gtk.SeparatorMenuItem.new()) open_file = Gtk.MenuItem(os.path.basename(file).replace("_", "__")) def _open_file(widget): self.parent.open_file(widget, file) open_file.connect("activate", _open_file) menu.append(open_file) open_folder = Gtk.MenuItem(_("Show Containing Folder")) def _open_folder(widget): self.parent.open_folder(widget, file) open_folder.connect("activate", _open_folder) menu.append(open_folder) info = Util.read_metadata(file) if info and "sourceURL" in info and "sourceName" in info: url = info["sourceURL"] source_name = info["sourceName"] if "Fetched" in source_name: label = _("Fetched: Show Origin") else: label = _("View at %s") % source_name if len(label) > 50: label = label[:50] + "..." show_origin = Gtk.MenuItem(label) def _show_origin(widget=None): logger.info(lambda: "Opening url: " + url) webbrowser.open_new_tab(url) show_origin.connect("activate", _show_origin) menu.append(show_origin) menu.append(Gtk.SeparatorMenuItem.new()) rating_item = Gtk.MenuItem(_("Set EXIF Rating")) rating_item.set_submenu( ThumbsManager.create_rating_menu(file, self.parent)) if not os.access(file, os.W_OK): rating_item.set_sensitive(False) menu.append(rating_item) menu.append(Gtk.SeparatorMenuItem.new()) self.copy_to_favorites = Gtk.MenuItem(_("Copy to _Favorites")) self.copy_to_favorites.set_use_underline(True) def _copy_to_favorites(widget): self.parent.copy_to_favorites(widget, file) self.copy_to_favorites.connect("activate", _copy_to_favorites) menu.append(self.copy_to_favorites) self.move_to_favorites = Gtk.MenuItem(_("Move to _Favorites")) self.move_to_favorites.set_use_underline(True) def _move_to_favorites(widget): self.parent.move_to_favorites(widget, file) self.remove_image(file) self.move_to_favorites.connect("activate", _move_to_favorites) self.move_to_favorites.set_visible(False) menu.append(self.move_to_favorites) trash_item = Gtk.MenuItem(_("Delete to _Trash")) trash_item.set_use_underline(True) def _trash(widget): self.parent.move_to_trash(widget, file) trash_item.connect("activate", _trash) menu.append(trash_item) focus = Gtk.MenuItem(_("Where is it from?")) focus.set_sensitive(self.parent.get_source(file) is not None) def _focus(widget): self.parent.focus_in_preferences(widget, file) focus.connect("activate", _focus) menu.append(focus) menu.append(Gtk.SeparatorMenuItem.new()) def close(widget): self.hide(force=True) close_item = Gtk.MenuItem(_("Close")) close_item.connect("activate", close) menu.append(close_item) menu.show_all() favs_op = self.parent.determine_favorites_operation(file) self.parent.update_favorites_menuitems(self, False, favs_op) return menu
# under the terms of the GNU General Public License version 3, as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranties of # MERCHANTABILITY, SATISFACTORY QUALITY, 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/>. ### END LICENSE from variety import _, _u FILTERS = { "Keep original": _("Keep original"), "Grayscale": _("Grayscale"), "Heavy blur": _("Heavy blur"), "Soft blur": _("Soft blur"), "Oil painting": _("Oil painting"), "Pencil sketch": _("Pencil sketch"), "Pointilism": _("Pointilism"), "Pixellate": _("Pixellate") } SOURCES = { "favorites": ("The Favorites folder", _("The Favorites folder")), "fetched": ("The Fetched folder", _("The Fetched folder")), "recommended": ("Recommended by Variety. Adapts to your taste as you mark images as favorite or trash.", _("Recommended by Variety. Adapts to your taste as you mark images as favorite or trash.")),
def get_description(self): return _("Images from Wallhaven.cc")