def add(self, uris):
     """
         Add uris to collection
     """
     GLib.idle_add(App().window.container.progress.pulse, True)
     walk_uris = list(uris)
     while walk_uris:
         uri = walk_uris.pop(0)
         if not uri:
             continue
         try:
             f = Gio.File.new_for_uri(uri)
             file_type = f.query_file_type(Gio.FileQueryInfoFlags.NONE,
                                           None)
             if file_type == Gio.FileType.DIRECTORY:
                 infos = f.enumerate_children(
                     "standard::name,standard::type,standard::is-hidden",
                     Gio.FileQueryInfoFlags.NONE, None)
                 for info in infos:
                     f = infos.get_child(info)
                     child_uri = f.get_uri()
                     if info.get_is_hidden():
                         continue
                     elif info.get_file_type() == Gio.FileType.DIRECTORY:
                         walk_uris.append(child_uri)
                     else:
                         if is_audio(f):
                             self.__add_file(f)
             elif is_audio(f):
                 self.__add_file(f)
             else:
                 Logger.info("Not an audio file %s" % uri)
         except Exception as e:
             Logger.error("CollectionImporter::add(): %s" % e)
     GLib.idle_add(App().window.container.progress.pulse, False)
Пример #2
0
 def load(self, base_uri):
     """
         Loads the metadata db from the MTP device
         @param base_uri as str
     """
     self.__base_uri = base_uri
     self.__db_uri = self.__base_uri + "/lollypop-sync.db"
     Logger.debug("MtpSyncDb::__load_db()")
     try:
         dbfile = Gio.File.new_for_uri(self.__db_uri)
         (status, jsonraw, tags) = dbfile.load_contents(None)
         if status:
             jsondb = json.loads(jsonraw.decode("utf-8"))
             if "encoder" in jsondb:
                 self.__encoder = jsondb["encoder"]
             if "normalize" in jsondb:
                 self.__normalize = jsondb["normalize"]
             if "version" in jsondb and jsondb["version"] == 1:
                 for m in jsondb["tracks_metadata"]:
                     self.__metadata[m["uri"]] = m["metadata"]
             else:
                 Logger.info("MtpSyncDb::__load_db():"
                             " unknown sync db version")
     except Exception as e:
         Logger.error("MtpSyncDb::load(): %s" % e)
Пример #3
0
 def __cache_artist_info(self, artist):
     """
         Cache artist information
         @param artist as str
     """
     content = None
     try:
         if get_network_available("WIKIPEDIA"):
             wikipedia = Wikipedia()
             content = wikipedia.get_content(artist)
         else:
             for (api, a_helper, ar_helper, helper) in self._WEBSERVICES:
                 if helper is None:
                     continue
                 try:
                     method = getattr(self, helper)
                     content = method(artist)
                     if content is not None:
                         break
                 except Exception as e:
                     Logger.error(
                         "InfoDownloader::__cache_artists_artwork(): %s" %
                         e)
         self.save_artist_information(artist, content)
     except Exception as e:
         Logger.info("InfoDownloader::__cache_artist_info(): %s" % e)
     GLib.idle_add(self.emit, "artist-info-changed", artist)
Пример #4
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")
Пример #5
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)
Пример #6
0
 def restore_state(self):
     """
         Restore player state
     """
     try:
         if App().settings.get_value("save-state"):
             current_track_id = load(open(LOLLYPOP_DATA_PATH +
                                          "/track_id.bin", "rb"))
             self.set_queue(load(open(LOLLYPOP_DATA_PATH +
                                      "/queue.bin", "rb")))
             albums = load(open(LOLLYPOP_DATA_PATH + "/Albums.bin", "rb"))
             playlist_ids = load(open(LOLLYPOP_DATA_PATH +
                                      "/playlist_ids.bin", "rb"))
             (is_playing, was_party) = load(open(LOLLYPOP_DATA_PATH +
                                                 "/player.bin", "rb"))
             if playlist_ids and playlist_ids[0] == Type.RADIOS:
                 radios = Radios()
                 track = Track()
                 name = radios.get_name(current_track_id)
                 url = radios.get_url(name)
                 track.set_radio(name, url)
                 self.load(track, is_playing)
             elif App().tracks.get_uri(current_track_id) != "":
                 if albums:
                     if was_party:
                         App().lookup_action("party").change_state(
                             GLib.Variant("b", True))
                     else:
                         self._albums = load(open(
                                             LOLLYPOP_DATA_PATH +
                                             "/Albums.bin",
                                             "rb"))
                     # Load track from player albums
                     current_track = Track(current_track_id)
                     index = self.album_ids.index(current_track.album.id)
                     for track in self._albums[index].tracks:
                         if track.id == current_track_id:
                             self._load_track(track)
                             break
                 else:
                     for playlist_id in playlist_ids:
                         tracks = App().playlists.get_tracks(playlist_id)
                         App().player.populate_playlist_by_tracks(
                             tracks, playlist_ids)
                         for track in tracks:
                             if track.id == current_track_id:
                                 self._load_track(track)
                                 break
                 if is_playing:
                     self.play()
                 else:
                     self.pause()
                 position = load(open(LOLLYPOP_DATA_PATH + "/position.bin",
                                 "rb"))
                 self.seek(position / Gst.SECOND)
             else:
                 Logger.info("Player::restore_state(): track missing")
             self.emit("playlist-changed")
     except Exception as e:
         Logger.error("Player::restore_state(): %s" % e)
Пример #7
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)
 def update(self, scan_type, uris=[]):
     """
         Update database
         @param scan_type as ScanType
         @param uris as [str]
     """
     self.__disable_compilations = not App().settings.get_value(
         "show-compilations")
     App().lookup_action("update_db").set_enabled(False)
     # Stop previous scan
     if self.is_locked() and scan_type != ScanType.EXTERNAL:
         self.stop()
         GLib.timeout_add(250, self.update, scan_type, uris)
     elif App().ws_director.collection_ws is not None and\
             not App().ws_director.collection_ws.stop():
         GLib.timeout_add(250, self.update, scan_type, uris)
     else:
         if scan_type == ScanType.FULL:
             uris = App().settings.get_music_uris()
         if not uris:
             return
         # Register to progressbar
         if scan_type != ScanType.EXTERNAL:
             App().window.container.progress.add(self)
             App().window.container.progress.set_fraction(0, self)
         Logger.info("Scan started")
         # Launch scan in a separate thread
         self.__thread = App().task_helper.run(self.__scan, scan_type, uris)
Пример #9
0
 def load(self):
     """
         Load track URI
     """
     uri = self.__load_from_cache()
     if uri is None:
         self.__load_uri_with_helper()
     else:
         Logger.info("%s loaded from cache", uri)
         self.__load_uri_content_with_helper(uri, None)
Пример #10
0
 def _create_cache(self):
     """
         Create cache dir
     """
     d = Gio.File.new_for_path(self._CACHE_PATH)
     if not d.query_exists():
         try:
             d.make_directory_with_parents()
         except:
             Logger.info("Can't create %s" % self._CACHE_PATH)
Пример #11
0
 def __init__(self):
     """
         Init album art
     """
     try:
         d = Gio.File.new_for_path(self._INFO_PATH)
         if not d.query_exists():
             d.make_directory_with_parents()
     except:
         Logger.info("Can't create %s" % self._INFO_PATH)
Пример #12
0
def create_dir(path):
    """
        Create dir
        @param path as str
    """
    d = Gio.File.new_for_path(path)
    if not d.query_exists():
        try:
            d.make_directory_with_parents()
        except:
            Logger.info("Can't create %s" % path)
Пример #13
0
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()

        ret = f(*args, **kwargs)

        elapsed_time = time.perf_counter() - start_time
        Logger.info(
            "%s::%s: execution time %d:%f" %
            (f.__module__, f.__name__, elapsed_time / 60, elapsed_time % 60))

        return ret
Пример #14
0
 def start(self):
     """
         Start web service (load save queue)
     """
     try:
         self.__cancellable = Gio.Cancellable()
         self.__queue = load(
             open(LOLLYPOP_DATA_PATH + "/%s_queue.bin" % self.__name, "rb"))
     except Exception as e:
         Logger.info("ListenBrainzWebService::__init__(): %s", e)
         self.__queue = []
 def __on_spotify_similar_artists(self, artists):
     """
         Add one album
         @param artists as [str]
     """
     similar_artist_ids = self.__get_artist_ids(artists)
     if similar_artist_ids:
         Logger.info("Found similar artists via Spotify:")
         if self.albums:
             self.__add_a_new_album(similar_artist_ids)
         else:
             self.__add_a_new_track(similar_artist_ids)
Пример #16
0
 def __init__(self):
     """
         Init ListenBrainz object
     """
     GObject.GObject.__init__(self)
     try:
         self.__uri = "https://api.listenbrainz.org/1/submit-listens"
         self.__name = "listenbrainz"
         self.__queue = []
         self.start()
     except Exception as e:
         Logger.info("LastFM::__init__(): %s", e)
Пример #17
0
 def get_uri_content(self, uri, cancellable):
     """
         Get content uri
         @param uri as str
         @param cancellable as Gio.Cancellable
         @return content uri as str/None
     """
     Logger.info("Loading %s with Invidious %s", uri, self.__server)
     youtube_id = uri.replace("https://www.youtube.com/watch?v=", "")
     video = self.__VIDEO % youtube_id
     uri = "%s/%s" % (self.__server, video)
     App().task_helper.load_uri_content(uri, cancellable,
                                        self.__on_uri_content)
Пример #18
0
 def stop(self):
     """
         Stop current tasks and save queue to disk
         @return bool
     """
     self.__cancellable.cancel()
     try:
         with open(LOLLYPOP_DATA_PATH + "/%s_queue.bin" % self.__name,
                   "wb") as f:
             dump(list(self.__queue), f)
     except Exception as e:
         Logger.info("ListenBrainzWebService::stop: %s", e)
     return True
Пример #19
0
 def __init__(self):
     """
         Init ListenBrainz object
     """
     GObject.GObject.__init__(self)
     try:
         self.__queue_id = None
         self.__queue = load(
             open(LOLLYPOP_DATA_PATH + "/listenbrainz_queue.bin", "rb"))
     except Exception as e:
         Logger.info("LastFM::__init__(): %s", e)
         self.__queue = []
     self.__next_request_time = 0
Пример #20
0
 def _on_bus_error(self, bus, message):
     """
         Try a codec install and update current track
         @param bus as Gst.Bus
         @param message as Gst.Message
     """
     Logger.info("Player::_on_bus_error(): %s" % message.parse_error()[1])
     App().window.container.pulse(False)
     if self.__codecs.is_missing_codec(message):
         self.__codecs.install()
         App().scanner.stop()
     elif App().notify is not None:
         App().notify.send(message.parse_error()[0].message)
     self.stop()
 def __on_get_local_similar_artists(self, artists):
     """
         Add one album from artists to player
         @param artists as []
     """
     if self.__next_cancellable.is_cancelled():
         return
     similar_artist_ids = self.__get_artist_ids(artists)
     album = None
     if similar_artist_ids:
         album = self.__get_album_from_artists(similar_artist_ids)
     if album is not None:
         Logger.info("Found a similar album")
         self.add_album(album)
Пример #22
0
 def get_music_uris(self):
     """
         Return music uris
         @return [str]
     """
     uris = self.get_value("music-uris")
     if not uris:
         filename = GLib.get_user_special_dir(
             GLib.UserDirectory.DIRECTORY_MUSIC)
         if filename:
             uris = [GLib.filename_to_uri(filename)]
         else:
             Logger.info("You need to add a music uri"
                         " to org.gnome.Lollypop in dconf")
     return list(uris)
Пример #23
0
 def init():
     """
         Init store
     """
     try:
         d = Gio.File.new_for_path(InformationStore._INFO_PATH)
         if not d.query_exists():
             d.make_directory_with_parents()
     except:
         Logger.info("Can't create %s" % InformationStore._INFO_PATH)
     try:
         d = Gio.File.new_for_path(InformationStore._CACHE_PATH)
         if not d.query_exists():
             d.make_directory_with_parents()
     except:
         Logger.info("Can't create %s" % InformationStore._CACHE_PATH)
Пример #24
0
 def __get_bio_content(self):
     """
         Get bio content and call callback
         @param content as str
     """
     content = None
     try:
         wikipedia = Wikipedia(self.__cancellable)
         content = wikipedia.get_content(self.__artist_name)
     except Exception as e:
         Logger.info("InformationPopover::__get_bio_content(): %s" % e)
     try:
         if content is None and App().lastfm is not None:
             content = App().lastfm.get_artist_bio(self.__artist_name)
     except Exception as e:
         Logger.info("InformationPopover::__get_bio_content(): %s" % e)
     return content
Пример #25
0
 def _on_bus_error(self, bus, message):
     """
         Try a codec install and update current track
         @param bus as Gst.Bus
         @param message as Gst.Message
     """
     self.emit("loading-changed", False)
     Logger.info("Player::_on_bus_error(): %s" % message.parse_error()[1])
     if self.current_track.id is not None and self.current_track.id >= 0:
         if self.__codecs.is_missing_codec(message):
             self.__codecs.install()
             App().scanner.stop()
             self.stop()
         elif App().notify is not None:
             (error, parsed) = message.parse_error()
             App().notify.send(parsed)
             self.stop()
Пример #26
0
 def get_similar_artists(self, artist_names, cancellable):
     """
         Get similar artists
         @param artist_ids as [int]
         @param cancellable as Gio.Cancellable
         @return [(str, None)]
     """
     artist_ids = []
     for artist_name in artist_names:
         artist_ids.append(App().artists.get_id(artist_name)[0])
     genre_ids = App().artists.get_genre_ids(artist_ids,
                                             StorageType.COLLECTION)
     artists = App().artists.get(genre_ids, StorageType.COLLECTION)
     shuffle(artists)
     result = [(name, None) for (artist_id, name, sortname) in artists]
     if result:
         Logger.info("Found similar artists with LocalSimilars")
     return result
 def __finish(self, items):
     """
         Notify from main thread when scan finished
         @param items as [CollectionItem]
     """
     track_ids = [item.track_id for item in items]
     self.__thread = None
     Logger.info("Scan finished")
     App().lookup_action("update_db").set_enabled(True)
     App().window.container.progress.set_fraction(1.0, self)
     self.stop()
     emit_signal(self, "scan-finished", track_ids)
     # Update max count value
     App().albums.update_max_count()
     # Update featuring
     App().artists.update_featuring()
     if App().ws_director.collection_ws is not None:
         App().ws_director.collection_ws.start()
 def __on_lastfm_similar_artists(self, artists):
     """
         Add one album or run a Spotify search if none
         @param artists as [str]
     """
     similar_artist_ids = self.__get_artist_ids(artists)
     if not similar_artist_ids:
         if self.current_track.artist_ids:
             artist_id = self.current_track.artist_ids[0]
             artist_name = App().artists.get_name(artist_id)
             App().spotify.get_artist_id(artist_name,
                                         self.__on_get_spotify_artist_id)
     else:
         Logger.info("Found a similar artist via Last.fm")
         if self.albums:
             self.__add_a_new_album(similar_artist_ids)
         else:
             self.__add_a_new_track(similar_artist_ids)
Пример #29
0
 def __handle_listenbrainz(self, acl):
     """
         Start/stop ListenBrainz based on acl
         @param acl as int
     """
     start = acl & NetworkAccessACL["MUSICBRAINZ"]
     if start and self.__listenbrainz_ws is None:
         from lollypop.ws_listenbrainz import ListenBrainzWebService
         self.__listenbrainz_ws = ListenBrainzWebService()
         self.__listenbrainz_ws.start()
         App().settings.bind("listenbrainz-user-token",
                             self.__listenbrainz_ws, "user_token", 0)
         Logger.info("ListenBrainz web service started")
     elif not start and self.__listenbrainz_ws is not None:
         App().settings.unbind(self.__listenbrainz_ws,
                               "listenbrainz-user-token")
         self.__listenbrainz_ws = None
         Logger.info("ListenBrainz web service stopping")
Пример #30
0
 def get_similar_artists(self, artist_names, cancellable):
     """
         Search similar artists
         @param artist_names as [str]
         @param cancellable as Gio.Cancellable
         @return [(str, str)] as [(artist_name, cover_uri)]
     """
     result = []
     for artist_name in artist_names:
         try:
             for similar in self.__get_similar_artists(artist_name):
                 if cancellable.is_cancelled():
                     raise Exception("cancelled")
                 result.append((similar, None))
         except Exception as e:
             Logger.error("LastFMSimilars::get_similar_artists(): %s", e)
     if result:
         Logger.info("Found similar artists with LastFMSimilars")
     return result