def _on_map_lyrics(self, widget): """ Load on map @param widget as Gtk.Viewport """ self._on_child_unmap(widget) Lp().settings.set_value('infoswitch', GLib.Variant('s', 'lyrics')) self.__jump_button.hide() if self.__current_track.id is None: self.__current_track = Lp().player.current_track # First try to get lyrics from tags from lollypop.tagreader import TagReader reader = TagReader() try: info = reader.get_info(self.__current_track.uri) except: info = None lyrics = "" if info is not None: tags = info.get_tags() lyrics = reader.get_lyrics(tags) if lyrics or InfoPopover.WebView is None\ or not get_network_available(): label = Gtk.Label() label.set_vexpand(True) label.set_hexpand(True) label.set_margin_top(10) label.set_margin_end(10) label.show() widget.add(label) if lyrics: label.set_label(lyrics) elif not get_network_available(): string = GLib.markup_escape_text(_("Network access disabled")) label.get_style_context().add_class('dim-label') label.set_markup( "<span font_weight='bold' size='xx-large'>" + string + "</span>") else: string = GLib.markup_escape_text( _("No lyrics found, please install gir1.2-webkit2-4.0")) label.get_style_context().add_class('dim-label') label.set_markup( "<span font_weight='bold' size='xx-large'>" + string + "</span>") elif get_network_available(): artists = ", ".join(Lp().player.current_track.artists) title = self.__current_track.name search = GLib.uri_escape_string(artists + " " + title, None, True) url = "http://genius.com/search?q=%s" % search # Delayed load due to WebKit memory loading and Gtk animation web = self.WebView(True, True) web.add_word('search') web.add_word('lyrics') web.show() widget.add(web) GLib.timeout_add(250, web.load, url, OpenLink.OPEN)
def _on_network_access_changed(self, *ignore): """ Destroy if not allowed anymore """ if not get_network_available("SPOTIFY") or\ not get_network_available("YOUTUBE"): self.destroy()
def get_similar_artists(self, artist_ids, cancellable): """ Get similar artists @param artist_ids as [int] @param cancellable as Gio.Cancellable @return [(str, None)] """ artist_names = [] result = [] for artist_id in artist_ids: artist_names.append(App().artists.get_name(artist_id)) if get_network_available("DEEZER"): result = self.__deezer_helper.get_similar_artists( artist_names, cancellable) if not result and get_network_available("SPOTIFY"): result = self.__spotify_helper.get_similar_artists( artist_names, cancellable) if not result and get_network_available("LASTFM"): result = self.__lastfm_helper.get_similar_artists( artist_names, cancellable) if not result: result = self.__local_helper.get_similar_artists( artist_names, cancellable) return result
def __init__(self, genre_ids, artist_ids, view_type=ViewType.SCROLLED): """ Init album view @param genre_ids as [int] @param artist_ids as [int] @param view_type as ViewType """ FlowBoxView.__init__(self, view_type) ViewController.__init__(self, ViewControllerType.ALBUM) self._widget_class = AlbumSimpleWidget self.__genre_ids = genre_ids self.__artist_ids = artist_ids if genre_ids and genre_ids[0] < 0: if genre_ids[0] == Type.WEB: if not Gio.NetworkMonitor.get_default().get_network_available( ): self._empty_message = _("Network not available") self._box.hide() elif GLib.find_program_in_path("youtube-dl") is None: self._empty_message = _("Missing youtube-dl command") self._box.hide() elif not get_network_available("SPOTIFY") or\ not get_network_available("YOUTUBE"): self._empty_message = _("You need to enable Spotify ") + \ _("and YouTube in network settings") self._box.hide() self._empty_icon_name = get_icon_name(genre_ids[0]) if view_type & ViewType.SMALL: self._scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER)
def _on_map_lyrics(self, widget): """ Load on map @param widget as Gtk.Viewport """ self._on_child_unmap(widget) Lp().settings.set_value('infoswitch', GLib.Variant('s', 'lyrics')) self.__jump_button.hide() if self.__current_track.id is None: self.__current_track = Lp().player.current_track # First try to get lyrics from tags from lollypop.tagreader import TagReader reader = TagReader() try: info = reader.get_info(self.__current_track.uri) except: info = None lyrics = "" if info is not None: tags = info.get_tags() lyrics = reader.get_lyrics(tags) if lyrics or InfoPopover.WebView is None\ or not get_network_available(): label = Gtk.Label() label.set_vexpand(True) label.set_hexpand(True) label.set_margin_top(10) label.set_margin_end(10) label.show() widget.add(label) if lyrics: label.set_label(lyrics) elif not get_network_available(): string = GLib.markup_escape_text(_("Network access disabled")) label.get_style_context().add_class('dim-label') label.set_markup("<span font_weight='bold' size='xx-large'>" + string + "</span>") else: string = GLib.markup_escape_text( _("No lyrics found, please install gir1.2-webkit2-4.0")) label.get_style_context().add_class('dim-label') label.set_markup("<span font_weight='bold' size='xx-large'>" + string + "</span>") elif get_network_available(): title = self.__current_track.name if self.__current_track.id == Type.RADIOS: search = GLib.uri_escape_string(title, None, True) else: artists = ", ".join(Lp().player.current_track.artists) search = GLib.uri_escape_string(artists + " " + title, None, True) url = "http://genius.com/search?q=%s" % search # Delayed load due to WebKit memory loading and Gtk animation web = self.WebView(True, True) web.add_word('search') web.add_word('lyrics') web.show() widget.add(web) GLib.timeout_add(250, web.load, url, OpenLink.OPEN)
def __init__(self, artist_ids): """ Init menu @param artist_ids as [int] """ Gio.Menu.__init__(self) section = Gio.Menu() radio_action = Gio.SimpleAction(name="radio_action_collection") App().add_action(radio_action) radio_action.connect("activate", self.__on_radio_action_activate, artist_ids) menu_item = Gio.MenuItem.new(_("Related tracks"), "app.radio_action_collection") menu_item.set_attribute_value("close", GLib.Variant("b", True)) section.append_item(menu_item) radio_action = Gio.SimpleAction(name="radio_action_loved") App().add_action(radio_action) radio_action.connect("activate", self.__on_radio_action_activate, artist_ids) menu_item = Gio.MenuItem.new(_("Loved tracks"), "app.radio_action_loved") menu_item.set_attribute_value("close", GLib.Variant("b", True)) section.append_item(menu_item) radio_action = Gio.SimpleAction(name="radio_action_populars") App().add_action(radio_action) radio_action.connect("activate", self.__on_radio_action_activate, artist_ids) menu_item = Gio.MenuItem.new(_("Popular tracks"), "app.radio_action_populars") menu_item.set_attribute_value("close", GLib.Variant("b", True)) section.append_item(menu_item) self.append_section(_("From collection"), section) section = Gio.Menu() radio_action = Gio.SimpleAction(name="radio_action_deezer") App().add_action(radio_action) radio_action.connect("activate", self.__on_radio_action_activate, artist_ids) radio_action.set_enabled(get_network_available("DEEZER")) menu_item = Gio.MenuItem.new(_("Deezer"), "app.radio_action_deezer") menu_item.set_attribute_value("close", GLib.Variant("b", True)) section.append_item(menu_item) radio_action = Gio.SimpleAction(name="radio_action_lastfm") App().add_action(radio_action) radio_action.connect("activate", self.__on_radio_action_activate, artist_ids) radio_action.set_enabled(get_network_available("LASTFM")) menu_item = Gio.MenuItem.new(_("Last.fm"), "app.radio_action_lastfm") menu_item.set_attribute_value("close", GLib.Variant("b", True)) section.append_item(menu_item) radio_action = Gio.SimpleAction(name="radio_action_spotify") App().add_action(radio_action) radio_action.connect("activate", self.__on_radio_action_activate, artist_ids) radio_action.set_enabled(get_network_available("SPOTIFY")) menu_item = Gio.MenuItem.new(_("Spotify"), "app.radio_action_spotify") menu_item.set_attribute_value("close", GLib.Variant("b", True)) section.append_item(menu_item) self.append_section(_("From the Web"), section)
def play_radio_from_lastfm(self, artist_ids): """ Play a radio from the Last.fm for artist ids @param artist_ids as [int] """ self.__play_radio_common() if get_network_available("LASTFM") and\ get_network_available("YOUTUBE"): from lollypop.similars_lastfm import LastFMSimilars similars = LastFMSimilars() self.__load_similars(similars, artist_ids)
def play_radio_from_deezer(self, artist_ids): """ Play a radio from the Last.fm for artist ids @param artist_ids as [int] """ self.__play_radio_common() if get_network_available("DEEZER") and\ get_network_available("YOUTUBE"): from lollypop.similars_deezer import DeezerSimilars similars = DeezerSimilars() self.__load_similars(similars, artist_ids)
def play_radio_from_spotify(self, artist_ids): """ Play a radio from the Spotify for artist ids @param artist_ids as [int] """ self.__play_radio_common() if get_network_available("SPOTIFY") and\ get_network_available("YOUTUBE"): from lollypop.similars_spotify import SpotifySimilars similars = SpotifySimilars() self.__load_similars(similars, artist_ids)
def playing_now(self, track): """ Submit a playing now notification for a track @param track as Track """ if not get_network_available("LASTFM") and get_network_available(): return if App().settings.get_value("disable-scrobbling"): return if track.id is not None and track.id >= 0 and self.available: App().task_helper.run(self.__playing_now, track.artists[0], track.album_name, track.title, int(track.duration), track.mb_track_id)
def __init__(self, header=False): """ Init search menu @param header as bool """ Gio.Menu.__init__(self) section = Gio.Menu() if header: from lollypop.menu_header import MenuHeader self.append_item(MenuHeader( _("Suggestions"), "org.gnome.Lollypop-suggestions-symbolic")) mask = App().settings.get_value("suggestions-mask").get_int32() action = Gio.SimpleAction.new_stateful( "spotify_new_releases", None, GLib.Variant("b", mask & StorageType.SPOTIFY_NEW_RELEASES)) action.connect("change-state", self.__on_change_state, StorageType.SPOTIFY_NEW_RELEASES) action.set_enabled(get_network_available("SPOTIFY")) App().add_action(action) menu_item = Gio.MenuItem.new(_("New releases on Spotify"), "app.spotify_new_releases") section.append_item(menu_item) action = Gio.SimpleAction.new_stateful( "spotify_similars", None, GLib.Variant("b", mask & StorageType.SPOTIFY_SIMILARS)) action.connect("change-state", self.__on_change_state, StorageType.SPOTIFY_SIMILARS) action.set_enabled(get_network_available("SPOTIFY")) App().add_action(action) menu_item = Gio.MenuItem.new(_("Suggestions from Spotify"), "app.spotify_similars") section.append_item(menu_item) action = Gio.SimpleAction.new_stateful( "deezer_charts", None, GLib.Variant("b", mask & StorageType.DEEZER_CHARTS)) action.connect("change-state", self.__on_change_state, StorageType.DEEZER_CHARTS) action.set_enabled(get_network_available("DEEZER")) App().add_action(action) menu_item = Gio.MenuItem.new(_("Top albums on Deezer"), "app.deezer_charts") section.append_item(menu_item) self.append_section(_("From the Web"), section)
def get_uri(self, track, cancellable, methods=[]): """ Get helper URI @param track as Track @param cancellable as Gio.Cancellable @param methods as [function] """ if not methods: methods = [self.__get_youtube_id] if get_network_available("STARTPAGE"): methods.append(self.__get_youtube_id_start) if get_network_available("DUCKDUCKGO"): methods.append(self.__get_youtube_id_duckduck) method = methods.pop(0) method(track, cancellable, methods)
def __on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self.__scrolled.get_vadjustment().set_value(0.0) if self.__current_url is not None: self.__previous_urls.append(self.__current_url) self.populate(item.URL) elif item.TYPE == "audio": if get_network_available(): # Cache for toolbar t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, Lp().window.toolbar.artsize)) t.daemon = True t.start() # Cache for MPRIS t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, ArtSize.BIG)) t.daemon = True t.start() # Cache for miniplayer t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, WindowSize.SMALL)) t.daemon = True t.start() Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True
def __populate(self, search=""): """ Same as populate @param search as str @thread safe """ urls = [] if get_network_available(): if search != "": urls = Lp().art.get_google_arts(search) elif self.__album is not None: urls = Lp().art.get_google_arts("%s+%s" % ( self.__artist, self.__album.name)) elif self.__artist_id is not None: for album_id in Lp().artists.get_albums([self.__artist_id]): for genre_id in Lp().albums.get_genre_ids(album_id): genre = Lp().genres.get_name(genre_id) urls += Lp().art.get_google_arts("%s+%s" % ( self.__artist, genre)) urls += Lp().art.get_google_arts(self.__artist) if urls: self.__add_pixbufs(urls, search) else: GLib.idle_add(self.__show_not_found) else: GLib.idle_add(self._spinner.stop)
def __update_for_url(self, url): """ Update charts for url @param url as str """ if not get_network_available(): return debug("ItunesCharts::__update_for_url(): %s => %s" % (url, self.__LIMIT)) web = Web() ids = self.__get_ids(url) position = len(ids) while ids: sleep(10) (itunes_id, itunes_genre) = ids.pop(0) album = self.__get_album(itunes_id) if self.__stop: return if album is None or not album.subitems: position -= 1 continue album.mtime = self.__time + position for item in album.subitems: item.mtime = self.__time debug("ItunesCharts::__update_for_url(): %s - %s" % ( album.name, album.artists)) t = TagReader() with SqlCursor(Lp().db) as sql: genre_ids = t.add_genres(itunes_genre) sql.commit() genre_ids.append(Type.ITUNES) web.save_album_thread(album, DbPersistent.CHARTS, genre_ids) position -= 1
def __update_for_url(self, url): """ Update charts for url @param url as str """ if not get_network_available(): return debug("SpotifyCharts::__update_for_url(): %s => %s" % (url, self.__count)) ids = self.__get_ids(url) web = Web() search = SpotifySearch() while ids: sleep(10) track_id = ids.pop(0) album_id = search.get_album_id(track_id) album = search.get_album(album_id) if album is None or album.exists_in_db(): continue if self._stop: return Lp().db.del_tracks(Lp().tracks.get_old_from_charts(self.__count)) debug("SpotifyCharts::__update_for_url(): %s - %s - %s" % ( album.name, album.artists, album_id)) web.save_album(album, DbPersistent.CHARTS)
def get_google_arts(self, search): """ Get arts on duck image corresponding to search @param search words as string @return [urls as string] """ data = None urls = [] if not get_network_available(): return [] cs_api_key = Lp().settings.get_value('cs-api-key').get_string() try: f = Lio.File.new_for_uri( "https://www.googleapis.com/" "customsearch/v1?key=%s&cx=%s" "&q=%s&searchType=image" % (cs_api_key, GOOGLE_API_ID, Lio.uri_escape_string(search, "", False))) (status, data, tag) = f.load_contents() if status: decode = json.loads(data.decode('utf-8')) if decode is None: return urls for item in decode['items']: urls.append(item['link']) except Exception as e: print(e) return urls
def __update_for_url(self, url): """ Update charts for url @param url as str """ if not get_network_available(): return debug("SpotifyCharts::__update_for_url(): %s" % (url)) ids = self.__get_ids(url) web = Web() search = SpotifySearch() position = len(ids) while ids: sleep(10) track_id = ids.pop(0) album = search.get_track(track_id) if self.__stop: return if album is None or not album.subitems: position -= 1 continue for item in album.subitems: item.mtime = self.__time + position debug("SpotifyCharts::__update_for_url(): %s - %s - %s" % ( album.name, album.artists, track_id)) web.save_album_thread(album, DbPersistent.CHARTS, [Type.SPOTIFY]) position -= 1
def get_items(self, url): """ Get radio entries for uri @param uri as string """ items = [] if not get_network_available(): raise f = Lio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if not status: raise root = xml.fromstring(data) for child in root.iter('outline'): try: item = TuneItem() item.URL = child.attrib['URL'] item.TEXT = child.attrib['text'] try: item.LOGO = child.attrib['image'] except: pass item.TYPE = child.attrib['type'] items.append(item) except: del item return items
def get_page_infos(self, name): """ Get page infos @param page name as str @return (url as str, content as str) """ if not get_network_available(): return (None, None) page = wikipedia.page(name) if page is None: return (None, None) content = page.content content = re.sub(r'%s ==' % _('Modify'), ' ==', content) jpegs = [] shuffle(page.images) for url in page.images: if url.lower().endswith('.jpg'): jpegs.append(url) # Search specific string in urls if name.replace(' ', '_').lower() in url.lower(): return (url, content.encode(encoding='UTF-8')) # If we only found one jpg, then use it url = None if jpegs: url = jpegs[0] return (url, content.encode(encoding='UTF-8'))
def _load_web(self, track, play=True): """ Load track url and play it @param track as Track @param play as bool @return True if loading """ if not get_network_available(): # Force widgets to update (spinners) self.emit("current-changed") return False try: from lollypop.web import Web if play: self.emit("loading-changed", True) t = Thread(target=Web.play_track, args=(track, play, self.__set_gv_uri)) t.daemon = True t.start() return True except Exception as e: self._current_track = Track() self.stop() self.emit("current-changed") if Lp().notify is not None: Lp().notify.send(str(e), track.uri) print("PlayerBin::_load_web()", e)
def connect(self, password): """ Connect lastfm @param password as str/None """ if self.__goa is not None: t = Thread(target=self.__connect, args=('', '', True)) t.daemon = True t.start() # Get username/password from GSettings/Secret elif Secret is not None and\ get_network_available(): self.__username = Lp().settings.get_value( 'lastfm-login').get_string() if password is None: schema = Secret.Schema.new("org.gnome.Lollypop", Secret.SchemaFlags.NONE, SecretSchema) Secret.password_lookup(schema, SecretAttributes, None, self.__on_password_lookup) else: t = Thread(target=self.__connect, args=(self.__username, password, True)) t.daemon = True t.start()
def do_startup(self): """ Init application """ Gtk.Application.do_startup(self) Notify.init("Lollypop") if not self.window: self.init() menu = self.__setup_app_menu() # If GNOME/Unity, add appmenu if is_gnome() or is_unity(): self.set_app_menu(menu) self.window = Window() # If not GNOME/Unity add menu to toolbar if not is_gnome() and not is_unity(): self.window.setup_menu(menu) self.window.connect("delete-event", self.__hide_on_delete) self.window.init_list_one() self.window.show() self.player.restore_state() # We add to mainloop as we want to run # after player::restore_state() signals GLib.idle_add(self.window.toolbar.set_mark) self.charts = None if self.settings.get_value("show-charts"): if GLib.find_program_in_path("youtube-dl") is not None: from lollypop.charts import Charts self.charts = Charts() if get_network_available(): self.charts.start() else: self.settings.set_value("network-search", GLib.Variant("b", False)) self.__preload_portal()
def __update_for_url(self, url): """ Update charts for url @param url as str """ if not get_network_available(): return debug("ItunesCharts::__update_for_url(): %s => %s" % (url, self.__LIMIT)) web = Web() ids = self.__get_ids(url) position = len(ids) while ids: sleep(10) (itunes_id, itunes_genre) = ids.pop(0) album = self.__get_album(itunes_id) if self.__stop: return if album is None or not album.subitems: position -= 1 continue album.mtime = self.__time + position for item in album.subitems: item.mtime = self.__time debug("ItunesCharts::__update_for_url(): %s - %s" % (album.name, album.artists)) t = TagReader() with SqlCursor(Lp().db) as sql: genre_ids = t.add_genres(itunes_genre) sql.commit() genre_ids.append(Type.ITUNES) web.save_album_thread(album, DbPersistent.CHARTS, genre_ids) position -= 1
def do_startup(self): """ Init application """ Gtk.Application.do_startup(self) Notify.init("Lollypop") # Check locale, we want unicode! (code, encoding) = getlocale() if encoding is None or encoding != "UTF-8": builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/Unicode.ui') self.window = builder.get_object('unicode') self.window.set_application(self) self.window.show() elif not self.window: self.init() menu = self.__setup_app_menu() # If GNOME/Unity, add appmenu if is_gnome() or is_unity(): self.set_app_menu(menu) self.window = Window(self) # If not GNOME/Unity add menu to toolbar if not is_gnome() and not is_unity(): self.window.setup_menu(menu) self.window.connect('delete-event', self.__hide_on_delete) self.window.init_list_one() self.window.show() self.player.restore_state() # We add to mainloop as we want to run # after player::restore_state() signals GLib.idle_add(self.window.toolbar.set_mark) # Will not start sooner # Ubuntu > 16.04 if Gtk.get_minor_version() > 18: from lollypop.inhibitor import Inhibitor # Ubuntu <= 16.04, Debian Jessie, ElementaryOS else: from lollypop.inhibitor_legacy import Inhibitor self.inhibitor = Inhibitor() self.charts = None if self.settings.get_value('network-search'): if GLib.find_program_in_path("youtube-dl") is not None: from lollypop.charts import Charts self.charts = Charts() if get_network_available(): self.charts.update() cs_api_key = self.settings.get_value( 'cs-api-key').get_string() default_cs_api_key = self.settings.get_default_value( 'cs-api-key').get_string() if (not cs_api_key or cs_api_key == default_cs_api_key) and\ self.notify is not None: self.notify.send( _("Google Web Services need a custom API key"), _("Lollypop needs this to search artwork and music.")) else: self.settings.set_value('network-search', GLib.Variant('b', False))
def _update_charts_setting(self, widget, state): """ Update show charts setting @param widget as Gtk.Switch @param state as bool """ if Lp().settings.get_value('network-access'): GLib.idle_add(Lp().window.add_remove_from, (Type.CHARTS, _("The charts"), ""), True, state) if state: if Lp().charts is None: from lollypop.charts import Charts Lp().charts = Charts() if get_network_available(): Lp().charts.start() elif Lp().notify is not None: Lp().notify.send(_("The charts"), _("Network access disabled")) else: Lp().charts.stop() Lp().scanner.clean_charts() Lp().settings.set_value('show-charts', GLib.Variant('b', state))
def _get_deezer_album_artwork_uri(self, artist, album, cancellable=None): """ Get album artwork uri from deezer @param artist as str @param album as str @param cancellable as Gio.Cancellable @return uri as str @tread safe """ if not get_network_available("DEEZER"): return None try: album_formated = GLib.uri_escape_string(album, None, True) uri = "https://api.deezer.com/search/album/?" +\ "q=%s&output=json" % album_formated (status, data) = App().task_helper.load_uri_content_sync(uri, cancellable) if status: decode = json.loads(data.decode("utf-8")) uri = None for item in decode["data"]: if noaccents(item["artist"]["name"].lower()) ==\ noaccents(artist.lower()): uri = item["cover_xl"] return uri except Exception as e: Logger.error("ArtDownloader::__get_deezer_album_artwork_uri: %s" % e) return None
def connect(self, password): """ Connect lastfm @param password as str/None """ if self.__goa is not None: t = Thread(target=self.__connect, args=('', '', True)) t.daemon = True t.start() # Get username/password from GSettings/Secret elif Secret is not None and\ get_network_available(): self.__username = Lp().settings.get_value( 'lastfm-login').get_string() if password is None: schema = Secret.Schema.new("org.gnome.Lollypop", Secret.SchemaFlags.NONE, SecretSchema) Secret.password_lookup(schema, SecretAttributes, None, self.__on_password_lookup) else: t = Thread(target=self.__connect, args=(self.__username, password, True)) t.daemon = True t.start()
def __enable_network_search(self): """ True if shoud enable network search @return bool """ return GLib.find_program_in_path("youtube-dl") is not None and\ get_network_available()
def _get_spotify_artist_artwork_uri(self, artist, cancellable=None): """ Return spotify artist information @param artist as str @param cancellable as Gio.Cancellable @return uri as str @tread safe """ if not get_network_available("SPOTIFY"): return None try: artist_formated = GLib.uri_escape_string(artist, None, True).replace(" ", "+") uri = "https://api.spotify.com/v1/search?q=%s" % artist_formated +\ "&type=artist" token = "Bearer %s" % self.__get_spotify_token(cancellable) helper = TaskHelper() helper.add_header("Authorization", token) (status, data) = helper.load_uri_content_sync(uri, cancellable) if status: uri = None decode = json.loads(data.decode("utf-8")) for item in decode["artists"]["items"]: if noaccents(item["name"].lower()) ==\ noaccents(artist.lower()): uri = item["images"][0]["url"] return uri except Exception as e: Logger.debug( "ArtDownloader::_get_spotify_artist_artwork_uri(): %s" % e) return None
def _get_audiodb_artist_artwork_uri(self, artist, cancellable=None): """ Get artist artwork uri from audiodb @param artist as str @param cancellable as Gio.Cancellable @return uri as str @thread safe """ if not get_network_available("AUDIODB"): return None try: artist = GLib.uri_escape_string(artist, None, True) uri = "https://theaudiodb.com/api/v1/json/" uri += "%s/search.php?s=%s" % (AUDIODB_CLIENT_ID, artist) (status, data) = App().task_helper.load_uri_content_sync(uri, cancellable) if status: decode = json.loads(data.decode("utf-8")) uri = None for item in decode["artists"]: for key in ["strArtistFanart", "strArtistThumb"]: uri = item[key] if uri is not None: return uri except Exception as e: Logger.error("ArtDownloader::_get_audiodb_artist_artwork_uri: %s" % e) return None
def _get_deezer_artist_artwork_uri(self, artist, cancellable=None): """ Return deezer artist information @param artist as str @param cancellable as Gio.Cancellable @return uri as str @tread safe """ if not get_network_available("DEEZER"): return None try: artist_formated = GLib.uri_escape_string(artist, None, True).replace(" ", "+") uri = "https://api.deezer.com/search/artist/?" +\ "q=%s&output=json&index=0&limit=1&" % artist_formated (status, data) = App().task_helper.load_uri_content_sync(uri, cancellable) if status: uri = None decode = json.loads(data.decode("utf-8")) uri = decode["data"][0]["picture_xl"] return uri except Exception as e: Logger.debug( "ArtDownloader::_get_deezer_artist_artwork_uri(): %s" % e) return None
def __populate(self, search=""): """ Same as populate @param search as str @thread safe """ urls = [] if get_network_available(): if search != "": urls = Lp().art.get_google_arts(search) elif self.__album is not None: urls = Lp().art.get_google_arts( "%s+%s" % (self.__artist, self.__album.name)) elif self.__artist_id is not None: for album_id in Lp().artists.get_albums([self.__artist_id]): for genre_id in Lp().albums.get_genre_ids(album_id): genre = Lp().genres.get_name(genre_id) urls += Lp().art.get_google_arts( "%s+%s" % (self.__artist, genre)) urls += Lp().art.get_google_arts(self.__artist) if urls: self.__add_pixbufs(urls, search) else: GLib.idle_add(self.__show_not_found) else: GLib.idle_add(self._spinner.stop)
def get_items(self, url): """ Get radio entries for uri @param uri as string """ items = [] if not get_network_available(): raise f = Gio.File.new_for_uri(url) (status, data, tag) = f.load_contents() if not status: raise root = xml.fromstring(data) for child in root.iter('outline'): try: item = TuneItem() item.URL = child.attrib['URL'] item.TEXT = child.attrib['text'] try: item.LOGO = child.attrib['image'] except: pass item.TYPE = child.attrib['type'] items.append(item) except: del item return items
def get_google_arts(self, search): """ Get arts on duck image corresponding to search @param search words as string @return [urls as string] """ data = None urls = [] if not get_network_available(): return [] cs_api_key = Lp().settings.get_value('cs-api-key').get_string() try: f = Lio.File.new_for_uri("https://www.googleapis.com/" "customsearch/v1?key=%s&cx=%s" "&q=%s&searchType=image" % (cs_api_key, GOOGLE_API_ID, GLib.uri_escape_string(search, "", False))) (status, data, tag) = f.load_contents() if status: decode = json.loads(data.decode('utf-8')) if decode is None: return urls for item in decode['items']: urls.append(item['link']) except Exception as e: print(e) return urls
def _load_web(self, track, play=True): """ Load track url and play it @param track as Track @param play as bool @return True if loading """ if not get_network_available(): # Force widgets to update (spinners) self.emit('current-changed') return False try: from lollypop.web import Web if play: self.emit('loading-changed', True) t = Thread(target=Web.play_track, args=(track, play, self.__set_gv_uri)) t.daemon = True t.start() return True except Exception as e: self._current_track = Track() self.stop() self.emit('current-changed') if Lp().notify is not None: Lp().notify.send(str(e), track.uri) print("PlayerBin::_load_web()", e)
def __on_activate_link(self, link, item): """ Update header with new link @param link as Gtk.LinkButton @param item as TuneIn Item """ if item.TYPE == "link": self.__scrolled.get_vadjustment().set_value(0.0) if self.__current_url is not None: self.__previous_urls.append(self.__current_url) self.populate(item.URL) elif item.TYPE == "audio": if get_network_available(): # Cache for toolbar t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, Lp().window.toolbar.artsize)) t.daemon = True t.start() # Cache for MPRIS t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, ArtSize.BIG)) t.daemon = True t.start() # Cache for miniplayer t = Thread(target=Lp().art.copy_uri_to_cache, args=(item.LOGO, item.TEXT, WindowSize.SMALL)) t.daemon = True t.start() Lp().player.load_external(item.URL, item.TEXT) Lp().player.play_this_external(item.URL) return True
def __reset_database(self, track_ids, count, history): """ Backup database and reset @param track ids as [int] @param count as int @param history as History """ if track_ids: track_id = track_ids.pop(0) uri = Lp().tracks.get_uri(track_id) f = Lio.File.new_for_uri(uri) name = f.get_basename() album_id = Lp().tracks.get_album_id(track_id) popularity = Lp().tracks.get_popularity(track_id) rate = Lp().tracks.get_rate(track_id) ltime = Lp().tracks.get_ltime(track_id) mtime = Lp().albums.get_mtime(album_id) duration = Lp().tracks.get_duration(track_id) loved = Lp().albums.get_loved(album_id) album_popularity = Lp().albums.get_popularity(album_id) album_rate = Lp().albums.get_rate(album_id) history.add(name, duration, popularity, rate, ltime, mtime, loved, album_popularity, album_rate) self.__progress.set_fraction((count - len(track_ids)) / count) GLib.idle_add(self.__reset_database, track_ids, count, history) else: self.__progress.hide() Lp().player.stop() Lp().db.drop_db() Lp().db = Database() Lp().window.show_genres(Lp().settings.get_value('show-genres')) Lp().window.update_db() self.__progress.get_toplevel().set_deletable(True) if Lp().charts is not None and get_network_available(): Lp().charts.start()
def _get_itunes_album_artwork_uri(self, artist, album, cancellable=None): """ Get album artwork uri from itunes @param artist as str @param album as str @param cancellable as Gio.Cancellable @return uri as str @tread safe """ if not get_network_available("ITUNES"): return None try: album_formated = GLib.uri_escape_string(album, None, True).replace(" ", "+") uri = "https://itunes.apple.com/search" +\ "?entity=album&term=%s" % album_formated (status, data) = App().task_helper.load_uri_content_sync(uri, cancellable) if status: decode = json.loads(data.decode("utf-8")) for item in decode["results"]: if noaccents(item["artistName"].lower()) ==\ noaccents(artist.lower()): uri = item["artworkUrl60"].replace( "60x60", "1024x1024") return uri except Exception as e: Logger.error("ArtDownloader::_get_album_art_itunes_uri: %s" % e) return None
def get_charts_ids(self, genre_ids=[]): """ Get charts track ids @param genre ids as [int] @return albums ids as [int] """ result = [] order = " ORDER BY mtime DESC,\ artists.sortname\ COLLATE NOCASE COLLATE LOCALIZED,\ tracks.year,\ tracks.name\ COLLATE NOCASE COLLATE LOCALIZED" with SqlCursor(Lp().db) as sql: filters = tuple([Type.CHARTS] + genre_ids) request = "SELECT DISTINCT tracks.rowid FROM tracks,\ track_genres, artists, track_artists\ WHERE EXISTS (\ SELECT track_genres.track_id\ FROM track_genres\ WHERE tracks.rowid = track_genres.track_id\ AND track_genres.genre_id=?)\ AND artists.rowid=track_artists.artist_id\ AND tracks.rowid=track_artists.track_id\ AND track_genres.track_id=tracks.rowid AND (" for genre_id in genre_ids: request += "track_genres.genre_id=? OR " request += "1=0)" if not get_network_available(): request += " AND tracks.persistent=%s" % DbPersistent.NONE request += order result = sql.execute(request, filters) return list(itertools.chain(*result))
def _update_charts_setting(self, widget, state): """ Update show charts setting @param widget as Gtk.Switch @param state as bool """ if Lp().settings.get_value("network-access"): GLib.idle_add(Lp().window.add_remove_from, (Type.CHARTS, _("The charts"), ""), True, state) if state: if Lp().charts is None: from lollypop.charts import Charts Lp().charts = Charts() if get_network_available(): Lp().charts.start() elif Lp().notify is not None: Lp().notify.send(_("The charts"), _("Network access disabled")) else: Lp().charts.stop() Lp().scanner.clean_charts() Lp().settings.set_value("show-charts", GLib.Variant("b", state))
def __enable_network_search(self): """ True if shoud enable network search @return bool """ return GLib.find_program_in_path("youtube-dl") is not None and\ get_network_available()
def __on_not_found(self): """ Show not found child """ InfoContent.__on_not_found(self) if get_network_available(): t = Thread(target=self.__setup_menu, args=(self._artist, self.__album)) t.daemon = True t.start()
def connect_sync(self, password): """ Connect lastfm sync @param password as str """ if get_network_available(): self.__username = Lp().settings.get_value( 'lastfm-login').get_string() self.__connect(self.__username, password) t = Thread(target=self.__populate_loved_tracks, args=(True,)) t.daemon = True t.start()
def populate(self, artist_ids): """ Populate view artist ids @param artist ids as int """ if get_network_available(): artists = [] for artist_id in artist_ids: artists.append(Lp().artists.get_name(artist_id)) t = Thread(target=self.__populate, args=(artists,)) t.daemon = True t.start()
def __init__(self, artist_ids, genre_ids): """ Init ArtistView @param artist id as int (Current if None) @param genre id as int """ ArtistAlbumsView.__init__(self, artist_ids, genre_ids) self.__art_signal_id = None self.connect('realize', self.__on_realize) self.connect('unrealize', self.__on_unrealize) builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/ArtistView.ui') builder.connect_signals(self) self.__artwork = builder.get_object('artwork') self.__artwork_box = builder.get_object('artwork-box') self.__label = builder.get_object('artist') self.__jump_button = builder.get_object('jump-button') self.__jump_button.set_tooltip_text(_("Go to current track")) self.__add_button = builder.get_object('add-button') self.__play_button = builder.get_object('play-button') self.__grid = builder.get_object('header') if Lp().lastfm is None: builder.get_object('lastfm-button').hide() elif not get_network_available(): builder.get_object('lastfm-button').set_sensitive(False) builder.get_object('lastfm-button').set_tooltip_text( _("Network access disabled")) self._overlay.add_overlay(self.__grid) self.__empty = Gtk.Grid() self.__empty.show() self._albumbox.add(self.__empty) self._albumbox.set_row_spacing(20) self.__scale_factor = self.__artwork.get_scale_factor() self.__set_artwork() self.__set_add_icon() self.__on_lock_changed(Lp().player) artists = [] for artist_id in artist_ids: artists.append(Lp().artists.get_name(artist_id)) if Lp().settings.get_value('artist-artwork'): self.__label.set_markup( "<span size='x-large' weight='bold'>" + GLib.markup_escape_text(", ".join(artists)) + "</span>") else: self.__label.set_markup( "<span size='large' weight='bold'>" + GLib.markup_escape_text(", ".join(artists)) + "</span>")
def cache_album_art(self, album_id): """ Download album artwork @param album id as int """ if album_id in self.__albums_history: return if get_network_available(): self.__albums_queue.append(album_id) if not self.__in_albums_download: t = Thread(target=self.__cache_albums_art) t.daemon = True t.start()
def __on_not_found(self): """ Show not found child """ if get_network_available(): error = _("No information for this artist") else: error = _("Network access disabled") self.__error.set_markup( "<span font_weight='bold' size='xx-large'>" + error + "</span>") self.set_visible_child_name('notfound')
def _on_test_btn_clicked(self, button): """ Test lastfm connection @param button as Gtk.Button """ self._update_lastfm_settings(True) if not get_network_available(): self.__test_img.set_from_icon_name('computer-fail-symbolic', Gtk.IconSize.MENU) return t = Thread(target=self.__test_lastfm_connection) t.daemon = True t.start()
def do_startup(self): """ Init application """ Gtk.Application.do_startup(self) Notify.init("Lollypop") # Check locale, we want unicode! (code, encoding) = getlocale() if encoding is None or encoding != "UTF-8": builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/Unicode.ui') self.window = builder.get_object('unicode') self.window.set_application(self) self.window.show() elif not self.window: self.init() menu = self.__setup_app_menu() # If GNOME/Unity, add appmenu if is_gnome() or is_unity(): self.set_app_menu(menu) self.window = Window() # If not GNOME/Unity add menu to toolbar if not is_gnome() and not is_unity(): self.window.setup_menu(menu) self.window.connect('delete-event', self.__hide_on_delete) self.window.init_list_one() self.window.show() self.player.restore_state() # We add to mainloop as we want to run # after player::restore_state() signals GLib.idle_add(self.window.toolbar.set_mark) # Will not start sooner # Ubuntu > 16.04 if Gtk.get_minor_version() > 18: from lollypop.inhibitor import Inhibitor # Ubuntu <= 16.04, Debian Jessie, ElementaryOS else: from lollypop.inhibitor_legacy import Inhibitor self.inhibitor = Inhibitor() self.charts = None if self.settings.get_value('show-charts'): if GLib.find_program_in_path("youtube-dl") is not None: from lollypop.charts import Charts self.charts = Charts() if get_network_available(): self.charts.update() else: self.settings.set_value('network-search', GLib.Variant('b', False))
def unlove(self, artist, title): """ Unlove track @param artist as string @param title as string @thread safe """ # Love the track on lastfm if get_network_available() and\ self.__is_auth: track = self.get_track(artist, title) try: track.unlove() except Exception as e: print("Lastfm::unlove(): %s" % e)
def get_albums(self, genre_id): """ Get all availables albums for genres @return Array of id as int """ with SqlCursor(Lp().db) as sql: filters = (genre_id, ) request = "SELECT albums.rowid\ FROM albums, album_genres\ WHERE album_genres.genre_id=?\ AND album_genres.album_id=albums.rowid" if not get_network_available(): request += " AND albums.synced!=%s" % Type.NONE result = sql.execute(request, filters) return list(itertools.chain(*result))
def get_recents(self): """ Return recent albums @return array of albums ids as int """ with SqlCursor(Lp().db) as sql: filters = (Type.CHARTS, ) request = "SELECT DISTINCT albums.rowid\ FROM albums, album_genres\ WHERE album_genres.genre_id!=?\ AND album_genres.album_id=albums.rowid" if not get_network_available(): request += " AND albums.synced!=%s" % Type.NONE request += " ORDER BY mtime DESC LIMIT 100" result = sql.execute(request, filters) return list(itertools.chain(*result))
def __on_password_lookup(self, source, result): """ Init self object @param source as GObject.Object @param result Gio.AsyncResult """ try: password = Secret.password_lookup_finish(result) self.__password = password if get_network_available(): t = Thread(target=self.__connect, args=(self.__username, password)) t.daemon = True t.start() except Exception as e: print("Lastfm::__on_password_lookup(): %s" % e)
def __populate(self, current_search=""): """ Same as populate @param current search as str @thread safe """ urls = [] if get_network_available(): for search in self.__get_current_searches(): urls += Lp().art.get_google_arts(search) if urls: self.__add_pixbufs(urls, current_search) else: self.__fallback(current_search) else: GLib.idle_add(self._spinner.stop)
def get_artist_info(self, artist): """ Get artist infos @param artist as str @return (url as str, content as str) """ if not get_network_available(): return (None, None, None) last_artist = self.get_artist(artist) try: content = last_artist.get_bio_content( language=getdefaultlocale()[0][0:2]) except: content = last_artist.get_bio_content() content = re.sub(r'<.*Last.fm.*>.', '', content) url = last_artist.get_cover_image(3) return (url, content.encode(encoding='UTF-8'))
def now_playing(self, artist, album, title, duration): """ Now playing track @param artist as str @param title as str @param album as str @param duration as int """ if get_network_available() and\ self.__is_auth and Secret is not None: t = Thread(target=self.__now_playing, args=(artist, album, title, duration)) t.daemon = True t.start()
def __get_album(self, itunes_id): """ Get itunes album items @param id as int @return SearchItem/None """ if not get_network_available(): return language = getdefaultlocale()[0][0:2] try: debug("ItunesCharts::__get_album(): %s" % itunes_id) url = self.__INFO % (itunes_id, language) f = Gio.File.new_for_uri(url) (status, data, tag) = f.load_contents(self.__cancel) if not status or self.__stop: return decode = json.loads(data.decode('utf-8')) item = decode['results'][0] album_item = SearchItem() album_item.name = item['collectionName'] album_item.album_name = album_item.name album_item.artists.append(item['artistName']) album_item.cover = item['artworkUrl60'].replace( '60x60', '512x512') for item in decode['results'][1:]: track_item = SearchItem() track_item.is_track = True track_item.name = item['trackName'] track_item.album = album_item.name track_item.year = item['releaseDate'][:4] track_item.tracknumber = int( item['trackNumber']) track_item.discnumber = int( item['discNumber']) track_item.duration = int( item['trackTimeMillis']) / 1000 if album_item.artists[0] != item['artistName']: track_item.artists.append(album_item.artists[0]) track_item.artists.append(item['artistName']) album_item.subitems.append(track_item) return album_item except Exception as e: print("ItunesCharts::__get_album()", e) return None
def do_scrobble(self, artist, album, title, timestamp): """ Scrobble track @param artist as str @param title as str @param album as str @param timestamp as int @param duration as int """ if get_network_available() and\ self.__is_auth and Secret is not None: t = Thread(target=self.__scrobble, args=(artist, album, title, timestamp)) t.daemon = True t.start()
def load(self, track): """ Load radio at uri @param track as Track """ if get_network_available(): try: self.__current = track parser = TotemPlParser.Parser.new() parser.connect("entry-parsed", self.__on_entry_parsed, track) parser.parse_async(track.uri, True, None, self.__on_parse_finished, track) except Exception as e: print("RadioPlayer::load(): ", e) if self.is_party: self.set_party(False) self._next_track = Track() self.emit('next-changed')