Пример #1
0
 def _get_itunes_album_artwork_uri(self, artist, album, cancellable=None):
     """
         Get album artwork using 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 []
     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.warning("%s %s %s", e, artist, album)
         Logger.warning(
             "DownloaderArt::_get_album_art_itunes_uri: %s", data)
     return []
Пример #2
0
 def _get_fanarttv_album_artwork_uri(self, artist, album, cancellable=None):
     """
         Get album artwork using FanartTV
         @param artist as str
         @param album as str
         @param cancellable as Gio.Cancellable
         @return uri as str
         @thread safe
     """
     if not get_network_available("FANARTTV"):
         return []
     uris = []
     try:
         search = "%s %s" % (artist, album)
         mbid = self.__get_musicbrainz_mbid("album", search, cancellable)
         if mbid is None:
             return []
         uri = "http://webservice.fanart.tv/v3/music/albums/%s?api_key=%s"
         (status, data) = App().task_helper.load_uri_content_sync(
             uri % (mbid, FANARTTV_ID), cancellable)
         if status:
             decode = json.loads(data.decode("utf-8"))
             for cover in decode["albums"][mbid]["albumcover"]:
                 uris.append(cover["url"])
     except Exception as e:
         Logger.warning("%s %s %s", e, artist, album)
         Logger.warning(
             "DownloaderArt::_get_fanarttv_album_artwork_uri: %s", data)
     return uris
Пример #3
0
 def _get_deezer_album_artwork_uri(self, artist, album, cancellable=None):
     """
         Get album artwork using 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 []
     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.warning("%s %s %s", e, artist, album)
         Logger.warning("DownloaderArt::__get_deezer_album_artwork_uri: %s",
                        data)
     return []
Пример #4
0
 def _get_spotify_artist_artwork_uri(self, artist, cancellable=None):
     """
         Get artist artwork using Spotify
         @param artist as str
         @param cancellable as Gio.Cancellable
         @return uri as str
         @tread safe
     """
     if not get_network_available("SPOTIFY"):
         return []
     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 = App().ws_director.token_ws.get_token("SPOTIFY",
                                                      cancellable)
         bearer = "Bearer %s" % token
         headers = [("Authorization", bearer)]
         (status,
          data) = App().task_helper.load_uri_content_sync_with_headers(
                 uri, headers, 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.warning("%s %s", e, artist)
         Logger.warning(
             "DownloaderArt::_get_spotify_artist_artwork_uri(): %s", data)
     return []
Пример #5
0
 def _get_audiodb_album_artwork_uri(self, artist, album, cancellable=None):
     """
         Get album artwork using AudioDB
         @param artist as str
         @param album as str
         @param cancellable as Gio.Cancellable
         @return uri as str
         @thread safe
     """
     if not get_network_available("AUDIODB"):
         return []
     try:
         album = GLib.uri_escape_string(album, None, True)
         artist = GLib.uri_escape_string(artist, None, True)
         uri = "https://theaudiodb.com/api/v1/json/"
         uri += "%s/searchalbum.php?s=%s&a=%s" % (AUDIODB_CLIENT_ID,
                                                  artist,
                                                  album)
         (status, data) = App().task_helper.load_uri_content_sync(
             uri, cancellable)
         if status:
             decode = json.loads(data.decode("utf-8"))
             if decode["album"]:
                 for item in decode["album"]:
                     uri = item["strAlbumThumb"]
                     return [uri]
     except Exception as e:
         Logger.warning("%s %s %s", e, artist, album)
         Logger.warning(
             "DownloaderArt::_get_audiodb_album_artwork_uri: %s", data)
     return []
Пример #6
0
 def decode_lyrics(bytes):
     try:
         lyrics = b""
         if bytes[0:4] == b"TXXX":
             if bytes[13:24] == b"L\x00y\x00r\x00i\x00c\x00s":
                 lyrics = bytes[29:].replace(b"\x00", b"")
             else:
                 return None
         elif bytes[0:4] == b"USLT":
             # This code sucks, if someone know how to handle this
             # UTF8
             lyrics = bytes.split(b"\x00")[-1]
             # UTF-16
             if not lyrics:
                 lyrics = bytes.split(
                     b"\xff\xfe")[2].replace(b"\x00", b"")
         else:
             return None
         for encoding in ENCODING:
             try:
                 return lyrics.decode(encoding)
             except Exception as e:
                 Logger.warning("TagReader::get_lyrics(ENCODING): %s",
                                e)
     except Exception as e:
         Logger.warning("TagReader::get_lyrics(): %s", e)
     return None
 def __remove_old_tracks(self, uris, scan_type):
     """
         Remove non existent tracks from DB
         @param scan_type as ScanType
     """
     if scan_type != ScanType.EXTERNAL and self.__thread is not None:
         # We need to check files are always in collections
         if scan_type == ScanType.FULL:
             collections = App().settings.get_music_uris()
         else:
             collections = None
         for uri in uris:
             # Handle a stop request
             if self.__thread is None:
                 raise Exception("cancelled")
             in_collection = True
             if collections is not None:
                 in_collection = False
                 for collection in collections:
                     if collection in uri:
                         in_collection = True
                         break
             f = Gio.File.new_for_uri(uri)
             if not in_collection:
                 Logger.warning(
                     "Removed, not in collection anymore: %s -> %s", uri,
                     collections)
                 self.del_from_db(uri, True)
             elif not f.query_exists():
                 Logger.warning("Removed, file has been deleted: %s", uri)
                 self.del_from_db(uri, True)
Пример #8
0
 def search_charts(self, cancellable):
     """
         Add charts to DB
         @param cancellable as Gio.Cancellable
     """
     Logger.info("Get charts with Deezer")
     try:
         album_ids = []
         uri = "https://api.deezer.com/chart/0/albums?limit=30"
         (status, data) = App().task_helper.load_uri_content_sync(
             uri, cancellable)
         if status:
             decode = json.loads(data.decode("utf-8"))
             for album in decode["data"]:
                 album_ids.append(album["id"])
         for album_id in album_ids:
             if cancellable.is_cancelled():
                 raise Exception("Cancelled")
             payload = DeezerWebHelper.get_album_payload(
                 self, album_id, cancellable)
             if payload is None:
                 continue
             lollypop_payload = DeezerWebHelper.lollypop_album_payload(
                 self, payload)
             self.save_album_payload_to_db(lollypop_payload,
                                           StorageType.DEEZER_CHARTS,
                                           True,
                                           cancellable)
     except Exception as e:
         Logger.warning(
             "DeezerCollectionWebService::search_charts(): %s", e)
Пример #9
0
 def __on_load_uri_content(self, uri, loaded, content, api, uris):
     """
         Add loaded pixbuf
         @param uri as str
         @param loaded as bool
         @param content as bytes
         @param uris as [str]
         @param api as str
         @param last as bool
     """
     try:
         if loaded:
             self.__add_pixbuf(content, api)
         if uris:
             (uri, api) = uris.pop(0)
             App().task_helper.load_uri_content(uri,
                                                self._cancellable,
                                                self.__on_load_uri_content,
                                                api,
                                                uris)
         else:
             self.__loaders -= 1
     except Exception as e:
         self.__loaders -= 1
         Logger.warning(
             "ArtworkSearchWidget::__on_load_uri_content(): %s", e)
     if self.__loaders == 0:
         self.__spinner.stop()
Пример #10
0
 def __on_get_youtube_id(self, uri, status, content, track, cancellable,
                         methods):
     """
         Get youtube id or run another method if not found
         @param uri as str
         @param status as bool
         @param content as bytes
         @param track as Track
         @param cancellable as Gio.Cancellable
         @param methods as [function]
     """
     try:
         youtube_id = None
         if status:
             decode = json.loads(content.decode("utf-8"))
             dic = {}
             best = self.__BAD_SCORE
             for i in decode["items"]:
                 score = get_page_score(i["snippet"]["title"], track.name,
                                        track.artists[0], track.album.name)
                 if score == -1 or score == best:
                     continue
                 elif score < best:
                     best = score
                 dic[score] = i["id"]["videoId"]
             # Return url from first dic item
             if best != self.__BAD_SCORE:
                 youtube_id = dic[best]
     except:
         Logger.warning("YouTubeHelper::__on_get_youtube_id(): %s", content)
     self.__emit_uri_loaded(youtube_id, track, cancellable, methods)
Пример #11
0
 def load_tracks(self, album, cancellable):
     """
         Load tracks for album
         @param album as Album
         @param cancellable as Gio.Cancellable
     """
     try:
         spotify_id = album.uri.replace("sp:", "")
         token = App().ws_director.token_ws.get_token(
             "SPOTIFY", cancellable)
         uri = "https://api.spotify.com/v1/albums/%s" % spotify_id
         token = App().ws_director.token_ws.get_token(
             "SPOTIFY", cancellable)
         bearer = "Bearer %s" % token
         headers = [("Authorization", bearer)]
         (status,
          data) = App().task_helper.load_uri_content_sync_with_headers(
              uri, headers, cancellable)
         if status:
             decode = json.loads(data.decode("utf-8"))
             # We want to share the same item as lp_album_id may change
             album_item = album.collection_item
             for track in decode["tracks"]["items"]:
                 lollypop_payload = self.lollypop_track_payload(track)
                 self.save_track_payload_to_db(lollypop_payload, album_item,
                                               album.storage_type, False,
                                               cancellable)
     except Exception as e:
         Logger.warning("SpotifySearch::load_tracks(): %s, %s", e, data)
Пример #12
0
 def get_today_album():
     """
         Get today album
         @return Album/None
     """
     current_date = GLib.DateTime.new_now_local().get_day_of_year()
     (date, album_id) = (0, None)
     try:
         (date,
          album_id) = load(open(LOLLYPOP_DATA_PATH + "/today.bin", "rb"))
         if App().albums.get_storage_type(album_id) == StorageType.NONE:
             date = 0
     except Exception as e:
         Logger.warning("TodayBannerWidget::__get_today_album(): %s", e)
     try:
         if date != current_date:
             storage_type = get_default_storage_type()
             album_id = App().albums.get_randoms(storage_type, None, False,
                                                 1)[0]
             dump((current_date, album_id),
                  open(LOLLYPOP_DATA_PATH + "/today.bin", "wb"))
         return Album(album_id)
     except Exception as e:
         Logger.error("TodayBannerWidget::__get_today_album(): %s", e)
     return None
Пример #13
0
 def __populate_db(self):
     """
         Populate DB in a background task
     """
     try:
         Logger.info("Collection download started")
         self.__is_running = True
         self.__cancellable = Gio.Cancellable()
         storage_types = []
         mask = App().settings.get_value("suggestions-mask").get_int32()
         # Check if storage type needs to be updated
         # Check if albums newer than a week are enough
         timestamp = time() - 604800
         for storage_type in self.__STORAGE_TYPES:
             if not mask & storage_type:
                 continue
             newer_albums = App().albums.get_newer_for_storage_type(
                 storage_type, timestamp)
             if len(newer_albums) < self.MIN_ITEMS_PER_STORAGE_TYPE:
                 storage_types.append(storage_type)
         # Update needed storage types
         if storage_types:
             for storage_type in storage_types:
                 if self.__cancellable.is_cancelled():
                     raise Exception("cancelled")
                 self.__METHODS[storage_type](self, self.__cancellable)
             self.clean_old_albums(storage_types)
             App().artists.update_featuring()
     except Exception as e:
         Logger.warning("CollectionWebService::__populate_db(): %s", e)
     self.__is_running = False
     Logger.info("Collection download finished")
Пример #14
0
    def __add_sync_action(self, name):
        """
            Add sync action
            @param name as str
            @param status as bool
        """
        def on_get_synced(synced, sync_action):
            sync_action.set_state(GLib.Variant.new_boolean(synced))

        synced = False
        devices = list(App().settings.get_value("devices"))
        action_name = "sync_%s" % name
        encoded = sha256(action_name.encode("utf-8")).hexdigest()
        sync_action = Gio.SimpleAction.new_stateful(
            encoded, None, GLib.Variant.new_boolean(synced))
        App().add_action(sync_action)
        try:
            if name == self.__all_devices:
                index = 0
            else:
                index = devices.index(name) + 1
            App().task_helper.run(self._get_synced,
                                  index,
                                  callback=(on_get_synced, sync_action))
        except Exception as e:
            Logger.warning("SyncMenu::__add_sync_action(): %s", e)
        if name != self.__all_devices:
            self.__actions.append(sync_action)
        sync_action.connect("change-state", self.__on_sync_action_change_state,
                            name)
        self.append(name, "app.%s" % encoded)
        return synced
Пример #15
0
 def search(self, search, cancellable):
     """
         Get albums related to search
         We need a thread because we are going to populate DB
         @param search as str
         @param cancellable as Gio.Cancellable
     """
     try:
         while self.wait_for_token():
             if cancellable.is_cancelled():
                 raise Exception("cancelled")
             sleep(1)
         token = "Bearer %s" % self.__token
         helper = TaskHelper()
         helper.add_header("Authorization", token)
         uri = "https://api.spotify.com/v1/search?"
         uri += "q=%s&type=album,track" % search
         (status, data) = helper.load_uri_content_sync(uri, cancellable)
         if status:
             decode = json.loads(data.decode("utf-8"))
             album_ids = []
             self.__create_albums_from_album_payload(
                                              decode["albums"]["items"],
                                              album_ids,
                                              cancellable)
             self.__create_albums_from_tracks_payload(
                                              decode["tracks"]["items"],
                                              album_ids,
                                              cancellable)
     except Exception as e:
         Logger.warning("SpotifyHelper::search(): %s", e)
         # Do not emit search-finished on cancel
         if str(e) == "cancelled":
             return
     GLib.idle_add(self.emit, "search-finished")
Пример #16
0
 def __on_load_uri_content(self, source, result, msg, headers, callback,
                           cancellable, uri, *args):
     """
         Get stream and start reading from it
         @param source as Soup.Session
         @param result as Gio.AsyncResult
         @param msg as Soup.Message
         @param headers as []
         @param cancellable as Gio.Cancellable
         @param callback as a function
         @param uri as str
     """
     try:
         response_headers = msg.get_property("response-headers")
         wait = self.__handle_ratelimit(response_headers, uri)
         if wait is None:
             stream = source.send_finish(result)
             # We use a bytearray here as seems that bytes += is really slow
             stream.read_bytes_async(4096, GLib.PRIORITY_LOW, cancellable,
                                     self.__on_read_bytes_async,
                                     bytearray(0), cancellable, callback,
                                     uri, *args)
         else:
             parsed = urlparse(uri)
             self.__ratelimit[parsed.netloc] = wait
             retries = self.__get_retries_for_uri(uri)
             if retries < 5:
                 self.__retries[uri] += 1
                 self.load_uri_content_sync_with_headers(
                     uri, headers, callback, *args)
             else:
                 del self.__retries[uri]
     except Exception as e:
         Logger.warning("TaskHelper::__on_soup_msg_finished(): %s" % e)
         callback(uri, False, b"", *args)
Пример #17
0
 def get(self, search, storage_type, cancellable):
     """
         Get albums for search
         We need a thread because we are going to populate DB
         @param search as str
         @param storage_type as StorageType
         @param cancellable as Gio.Cancellable
     """
     if not get_network_available("DEEZER"):
         emit_signal(self, "finished")
         return
     try:
         uri = "https://api.deezer.com/search/album?q=%s" %\
             search
         (status, data) = App().task_helper.load_uri_content_sync(
             uri, cancellable)
         if status:
             decode = json.loads(data.decode("utf-8"))
             for albums in decode["data"]:
                 payload = self.lollypop_album_payload(albums)
                 self.save_album_payload_to_db(payload,
                                               storage_type,
                                               True,
                                               cancellable)
     except Exception as e:
         Logger.warning("DeezerSearch::get(): %s", e)
     if not cancellable.is_cancelled():
         emit_signal(self, "finished")
Пример #18
0
 def get(self, search, storage_type, cancellable):
     """
         Get albums for search
         We need a thread because we are going to populate DB
         @param search as str
         @param storage_type as StorageType
         @param cancellable as Gio.Cancellable
     """
     if not get_network_available("MUSICBRAINZ"):
         emit_signal(self, "finished")
         return
     try:
         uri = "http://musicbrainz.org/ws/2/release/?fmt=json&query=%s" %\
             search
         (status, data) = App().task_helper.load_uri_content_sync(
             uri, cancellable)
         if status:
             decode = json.loads(data.decode("utf-8"))
             for release in decode["releases"]:
                 payload = self.lollypop_album_payload(release)
                 self.save_album_payload_to_db(payload,
                                               storage_type,
                                               True,
                                               cancellable)
     except Exception as e:
         Logger.warning("MusicBrainzSearch::get(): %s", e)
     if not cancellable.is_cancelled():
         emit_signal(self, "finished")
Пример #19
0
 def search_similar_albums(self, cancellable):
     """
         Add similar albums to DB
         @param cancellable as Gio.Cancellable
     """
     Logger.info("Get similar albums from Spotify")
     from lollypop.similars_spotify import SpotifySimilars
     similars = SpotifySimilars()
     try:
         storage_type = get_default_storage_type()
         artists = App().artists.get_randoms(
             self.MAX_ITEMS_PER_STORAGE_TYPE, storage_type)
         artist_names = [name for (aid, name, sortname) in artists]
         similar_ids = similars.get_similar_artist_ids(
             artist_names, cancellable)
         # Add albums
         shuffle(similar_ids)
         for similar_id in similar_ids[:self.MAX_ITEMS_PER_STORAGE_TYPE]:
             albums_payload = self.__get_artist_albums_payload(
                 similar_id, cancellable)
             shuffle(albums_payload)
             for album in albums_payload:
                 if cancellable.is_cancelled():
                     raise Exception("Cancelled")
                 lollypop_payload = SpotifyWebHelper.lollypop_album_payload(
                     self, album)
                 self.save_album_payload_to_db(lollypop_payload,
                                               StorageType.SPOTIFY_SIMILARS,
                                               True, cancellable)
                 break
     except Exception as e:
         Logger.warning("SpotifyWebService::search_similar_albums(): %s", e)
Пример #20
0
 def _get_deezer_artist_artwork_uri(self, artist, cancellable=None):
     """
         Get artist artwork using Deezer
         @param artist as str
         @param cancellable as Gio.Cancellable
         @return uri as str
         @tread safe
     """
     if not get_network_available("DEEZER"):
         return []
     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.warning("%s %s", e, artist)
         Logger.warning(
             "DownloaderArt::_get_deezer_artist_artwork_uri(): %s", data)
     return []
Пример #21
0
 def _on_eventbox_realize(self, eventbox):
     """
         Show hand cursor over
     """
     try:
         eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
     except:
         Logger.warning(_("You are using a broken cursor theme!"))
Пример #22
0
 def decode_lyrics(bytes):
     try:
         frame = FrameLangTag(bytes)
         if frame.key == "USLT":
             return frame.string
     except Exception as e:
         Logger.warning("TagReader::get_lyrics(): %s", e)
     return None
Пример #23
0
 def _on_label_realize(self, eventbox):
     """
         @param eventbox as Gtk.EventBox
     """
     try:
         eventbox.get_window().set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
     except:
         Logger.warning(_("You are using a broken cursor theme!"))
Пример #24
0
def decodeUnicode(bites, encoding):
    codec = id3EncodingToString(encoding)
    Logger.debug("Unicode encoding: %s" % codec)
    if (codec.startswith("utf_16") and len(bites) % 2 != 0
            and bites[-1:] == b"\x00"):
        # Catch and fix bad utf16 data, it is everywhere.
        Logger.warning("Fixing utf16 data with extra zero bytes")
        bites = bites[:-1]
    return bites.decode(codec).rstrip("\x00")
Пример #25
0
 def select_first(self):
     """
         Select first available item
     """
     try:
         self._listbox.unselect_all()
         row = self._listbox.get_children()[0]
         row.activate()
     except Exception as e:
         Logger.warning("SelectionList::select_first(): %s", e)
Пример #26
0
 def __on_realize(self, widget):
     """
         Change cursor over eventbox
         @param widget as Gtk.Widget
     """
     try:
         window = widget.get_window()
         if window is not None:
             window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
     except:
         Logger.warning(_("You are using a broken cursor theme!"))
Пример #27
0
def on_realize(widget):
    """
        Set cursor on widget
        @param widget as Gtk.Widget
    """
    try:
        window = widget.get_window()
        if window is not None:
            window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
    except:
        Logger.warning(_("You are using a broken cursor theme!"))
Пример #28
0
 def decode_lyrics(bytes_list, encoding):
     lyrics = []
     try:
         for frame in bytes_list:
             (l, t) = splitUnicode(frame, encoding)
             if l:
                 lyrics.append((decodeUnicode(l, encoding),
                                int.from_bytes(t[1:4], "big")))
     except Exception as e:
         Logger.warning("TagReader::get_synced_lyrics1(): %s", e)
     return lyrics
Пример #29
0
 def _on_label_realize(self, eventbox):
     """
         Change cursor on label
         @param eventbox as Gtk.EventBox
     """
     try:
         if len(self._artist_ids) == 1:
             eventbox.get_window().set_cursor(
                 Gdk.Cursor(Gdk.CursorType.HAND2))
     except:
         Logger.warning(_("You are using a broken cursor theme!"))
Пример #30
0
 def __on_eventbox_realize(self, eventbox):
     """
         Change cursor over eventbox
         @param eventbox as Gdk.Eventbox
     """
     try:
         window = eventbox.get_window()
         if window is not None:
             window.set_cursor(Gdk.Cursor(Gdk.CursorType.HAND2))
     except:
         Logger.warning(_("You are using a broken cursor theme!"))