Exemple #1
0
 def __on_list_two_activated(self, listbox, row):
     """
         Update view based on selected object
         @param listbox as Gtk.ListBox
         @param row as Gtk.ListBoxRow
     """
     Logger.debug("Container::__on_list_two_activated()")
     self._stack.destroy_children()
     if not App().window.is_adaptive:
         App().window.emit("show-can-go-back", False)
         App().window.emit("can-go-back-changed", False)
     genre_ids = self._list_one.selected_ids
     selected_ids = self._list_two.selected_ids
     if not selected_ids or not genre_ids:
         return
     if genre_ids[0] == Type.PLAYLISTS:
         view = self._get_view_playlists(selected_ids)
     elif genre_ids[0] == Type.YEARS:
         view = self._get_view_albums_years(selected_ids)
     elif selected_ids[0] == Type.COMPILATIONS:
         view = self._get_view_albums(genre_ids, selected_ids)
     else:
         view = self._get_view_artists(genre_ids, selected_ids)
     self._stack.add(view)
     self._stack.set_visible_child(view)
Exemple #2
0
 def __playing_now(self, track):
     """
         Now playing track
         @param track as Track
     """
     try:
         payload = self.__get_payload(track)
         post_data = {
             "listen_type": "playing_now",
             "payload": payload
         }
         body = json.dumps(post_data).encode("utf-8")
         msg = Soup.Message.new("POST", self.__uri)
         msg.set_request("application/json",
                         Soup.MemoryUse.STATIC,
                         body)
         msg.request_headers.append("Accept-Charset", "utf-8")
         msg.request_headers.append("Authorization",
                                    "Token %s" % self.user_token)
         data = App().task_helper.send_message_sync(msg,
                                                    self.__cancellable)
         if data is not None:
             Logger.debug("%s: %s", self.__uri, data)
     except Exception as e:
         Logger.error("ListenBrainzWebService::__playing_now(): %s" % e)
 def _load_track(self, track, init_volume=True):
     """
         Load track
         @param track as Track
         @param init volume as bool
         @return False if track not loaded
     """
     if init_volume:
         self._plugins.volume.props.volume = 1.0
     Logger.debug("BinPlayer::_load_track(): %s" % track.uri)
     try:
         self.__cancellable.cancel()
         self.__cancellable = Gio.Cancellable()
         self._current_track = track
         # We check track is URI track, if yes, do a load from Web
         # Will not work if we add another music provider one day
         track_uri = App().tracks.get_uri(track.id)
         if track.is_web and track.uri == track_uri:
             self.emit("loading-changed", True)
             App().task_helper.run(self._load_from_web, track)
             return False
         else:
             self._playbin.set_property("uri", track.uri)
     except Exception as e:  # Gstreamer error
         Logger.error("BinPlayer::_load_track(): %s" % e)
         return False
     return True
Exemple #4
0
 def __listen(self, track, timestamp):
     """
         Scrobble track
         @param track as Track
         @param timestamp as int
     """
     tracks = self.__queue + [(track, timestamp)]
     self.__queue = []
     try:
         for (track, timestamp) in tracks:
             payload = self.__get_payload(track)
             payload[0]["listened_at"] = timestamp
             post_data = {
                 "listen_type": "single",
                 "payload": payload
             }
             body = json.dumps(post_data).encode("utf-8")
             msg = Soup.Message.new("POST", self.__uri)
             msg.set_request("application/json",
                             Soup.MemoryUse.STATIC,
                             body)
             msg.request_headers.append("Accept-Charset", "utf-8")
             msg.request_headers.append("Authorization",
                                        "Token %s" % self.user_token)
             msg.request_headers.append("Content-Type", "application/json")
             data = App().task_helper.send_message_sync(msg,
                                                        self.__cancellable)
             if data is not None:
                 Logger.debug("%s: %s", self.__uri, data)
     except Exception as e:
         Logger.error("ListenBrainzWebService::__listen(): %s" % e)
 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)
 def __save_in_db(self, storage_type):
     """
         Save current tags into DB
         @param storage_type as StorageType
         @return [CollectionItem]
     """
     items = []
     notify_index = 0
     previous_album_id = None
     for uri in list(self.__tags.keys()):
         # Handle a stop request
         if self.__thread is None:
             raise Exception("cancelled")
         Logger.debug("Adding file: %s" % uri)
         tags = self.__tags[uri]
         item = self.__add2db(uri, *tags, storage_type)
         items.append(item)
         self.__progress_count += 1
         self.__update_progress(self.__progress_count,
                                self.__progress_total, 0.001)
         if previous_album_id != item.album_id:
             self.__notify_ui(items[notify_index:])
             notify_index = len(items)
             previous_album_id = item.album_id
         del self.__tags[uri]
     # Handle a stop request
     if self.__thread is None:
         raise Exception("cancelled")
     self.__notify_ui(items)
     return items
Exemple #7
0
 def __playing_now(self, track):
     """
         Now playing track
         @param track as Track
     """
     try:
         token = App().ws_director.token_ws.get_token(
             self.__name, self.__cancellable)
         if token is None:
             return
         args = self.__get_args_for_method("track.updateNowPlaying")
         args.append(("artist", track.artists[0]))
         args.append(("track", track.name))
         args.append(("album", track.album.name))
         if track.mbid and track.mbid.find(":") == -1:
             args.append(("mbid", track.mbid))
         args.append(("duration", str(track.duration // 1000)))
         args.append(("sk", token))
         api_sig = self.__get_sig_for_args(args)
         args.append(("api_sig", api_sig))
         post_data = {}
         for (name, value) in args:
             post_data[name] = value
         msg = Soup.form_request_new_from_hash("POST", self.__uri,
                                               post_data)
         msg.request_headers.append("Accept-Charset", "utf-8")
         data = App().task_helper.send_message_sync(msg, self.__cancellable)
         if data is not None:
             Logger.debug("%s: %s -> %s", self.__uri, data, post_data)
     except Exception as e:
         Logger.error("LastFMWebService::__playing_now(): %s" % e)
 def _on_bus_message_tag(self, bus, message):
     """
         Read tags from stream
         @param bus as Gst.Bus
         @param message as Gst.Message
     """
     # Some radio streams send message tag every seconds!
     changed = False
     if self._current_track.id >= 0 or self._current_track.duration > 0.0:
         return
     Logger.debug("Player::__on_bus_message_tag(): %s" %
                  self._current_track.uri)
     reader = TagReader()
     tags = message.parse_tag()
     title = reader.get_title(tags, "")
     if title != "" and self._current_track.name != title:
         self._current_track.name = title
         changed = True
     if self._current_track.name == "":
         self._current_track.name = self._current_track.uri
         changed = True
     artists = reader.get_artists(tags)
     if artists != "" and self._current_track.artists != artists:
         self._current_track.artists = artists.split(",")
         changed = True
     if not self._current_track.artists:
         self._current_track.artists = self._current_track.album_artists
         changed = True
     if changed:
         self.emit("current-changed")
Exemple #9
0
 def __love(self, artist, title, status):
     """
         Love track
         @param artist as string
         @param title as string
         @param status as bool
     """
     try:
         token = App().ws_director.token_ws.get_token(
             self.__name, self.__cancellable)
         if token is None:
             return
         if status:
             args = self.__get_args_for_method("track.love")
         else:
             args = self.__get_args_for_method("track.unlove")
         args.append(("artist", artist))
         args.append(("track", title))
         args.append(("sk", token))
         api_sig = self.__get_sig_for_args(args)
         args.append(("api_sig", api_sig))
         post_data = {}
         for (name, value) in args:
             post_data[name] = value
         msg = Soup.form_request_new_from_hash("POST", self.__uri,
                                               post_data)
         msg.request_headers.append("Accept-Charset", "utf-8")
         data = App().task_helper.send_message_sync(msg, self.__cancellable)
         if data is not None:
             Logger.debug("%s: %s", self.__uri, data)
     except Exception as e:
         Logger.error("LastFMWebService::__love(): %s" % e)
    def save_album(
        self,
        item,
    ):
        """
            Add album to DB
            @param item as CollectionItem
        """
        Logger.debug("CollectionScanner::save_album(): "
                     "Add album artists %s" % item.album_artists)
        (item.new_album_artist_ids,
         item.album_artist_ids) = self.add_artists(item.album_artists,
                                                   item.aa_sortnames,
                                                   item.mb_album_artist_id)
        # We handle artists already created by any previous save_track()
        for artist_id in item.album_artist_ids:
            if artist_id in self.__pending_new_artist_ids:
                item.new_album_artist_ids.append(artist_id)
                self.__pending_new_artist_ids.remove(artist_id)

        item.lp_album_id = get_lollypop_album_id(item.album_name,
                                                 item.album_artists)
        Logger.debug("CollectionScanner::save_track(): Add album: "
                     "%s, %s" % (item.album_name, item.album_artist_ids))
        (item.new_album, item.album_id) = self.add_album(
            item.album_name, item.mb_album_id, item.lp_album_id,
            item.album_artist_ids, item.uri, item.album_loved, item.album_pop,
            item.album_rate, item.album_synced, item.album_mtime,
            item.storage_type)
        if item.year is not None:
            App().albums.set_year(item.album_id, item.year)
            App().albums.set_timestamp(item.album_id, item.timestamp)
 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 __request(self, listen_type, payload, retry=0):
     """
         Submit payload to service
         @param listen_type as str
         @param payload as []
         @param retry as int (internal)
     """
     self.__wait_for_ratelimit()
     Logger.debug("ListenBrainz %s: %r" % (listen_type, payload))
     data = {"listen_type": listen_type, "payload": payload}
     body = json.dumps(data).encode("utf-8")
     session = Soup.Session.new()
     uri = "https://%s%s" % (HOST_NAME, PATH_SUBMIT)
     msg = Soup.Message.new("POST", uri)
     msg.set_request("application/json", Soup.MemoryUse.STATIC, body)
     msg.request_headers.append("Authorization",
                                "Token %s" % self.user_token)
     try:
         status = session.send_message(msg)
         response_headers = msg.get_property("response-headers")
         self.__handle_ratelimit(response_headers)
         # Too Many Requests
         if status == 429 and retry < 5:
             self.__request(listen_type, payload, retry + 1)
     except Exception as e:
         print("ListenBrainz::__submit():", e)
 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 save(self):
     """
         Saves the metadata db to the MTP device
     """
     try:
         Logger.debug("MtpSyncDb::__save()")
         jsondb = json.dumps({
             "version":
             1,
             "encoder":
             self.__encoder,
             "normalize":
             self.__normalize,
             "tracks_metadata": [{
                 "uri": x,
                 "metadata": y
             } for x, y in sorted(self.__metadata.items())]
         })
         dbfile = Gio.File.new_for_uri(self.__db_uri)
         (tmpfile, stream) = Gio.File.new_tmp()
         stream.get_output_stream().write_all(jsondb.encode("utf-8"))
         tmpfile.copy(dbfile, Gio.FileCopyFlags.OVERWRITE, None, None)
         stream.close()
     except Exception as e:
         Logger.error("MtpSyncDb::__save(): %s", e)
Exemple #15
0
 def __now_playing(self,
                   artist,
                   album,
                   title,
                   duration,
                   mb_track_id,
                   first=True):
     """
         Now playing track
         @param artist as str
         @param title as str
         @param album as str
         @param duration as int
         @param first is internal
         @thread safe
     """
     if App().settings.get_value("disable-scrobbling"):
         return
     try:
         self.update_now_playing(artist=artist,
                                 album=album,
                                 title=title,
                                 duration=duration,
                                 mbid=mb_track_id)
         Logger.debug("LastFM::__now_playing(): %s, %s, %s, %s, %s" %
                      (artist, album, title, duration, mb_track_id))
     except WSError:
         pass
     except Exception as e:
         Logger.error("LastFM::__now_playing(): %s" % e)
         # now playing sometimes fails
         if first:
             self.__connect()
             self.__now_playing(artist, album, title, duration, mb_track_id,
                                False)
 def __scan_to_handle(self, uri):
     """
         Check if file has to be handle by scanner
         @param f as Gio.File
         @return bool
     """
     try:
         file_type = get_file_type(uri)
         # Get file type using Gio (slower)
         if file_type == FileType.UNKNOWN:
             f = Gio.File.new_for_uri(uri)
             info = f.query_info(FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
                                 Gio.FileQueryInfoFlags.NONE)
             if is_pls(info):
                 file_type = FileType.PLS
             elif is_audio(info):
                 file_type = FileType.AUDIO
         if file_type == FileType.PLS:
             Logger.debug("Importing playlist %s" % uri)
             if App().settings.get_value("import-playlists"):
                 App().playlists.import_tracks(uri)
         elif file_type == FileType.AUDIO:
             Logger.debug("Importing audio %s" % uri)
             return True
     except Exception as e:
         Logger.error("CollectionScanner::__scan_to_handle(): %s" % e)
     return False
Exemple #17
0
    def __lazy_loading(self):
        """
            Load the view in a lazy way
        """
        widget = None
        if self.__priority_queue:
            widget = self.__priority_queue.pop(0)
            self.__lazy_queue.remove(widget)
        elif self.__lazy_queue:
            widget = self.__lazy_queue.pop(0)

        if widget is not None:
            widget.connect("populated", self._on_populated)
            widget.populate()
        else:
            self.__loading_state = LoadingState.FINISHED
            emit_signal(self, "populated")
            # Apply filtering
            if App().window.container.type_ahead.get_reveal_child():
                text = App().window.container.type_ahead.entry.get_text()
                if text:
                    self.search_for_child(text)
                else:
                    GLib.idle_add(
                        App().window.container.type_ahead.entry.grab_focus)
            Logger.debug("LazyLoadingView::lazy_loading(): %s",
                         time() - self.__start_time)
Exemple #18
0
 def get_compilation(self, tags):
     """
         Return True if album is a compilation
         @param tags as Gst.TagList
         @return bool
     """
     if tags is None:
         return False
     size = tags.get_tag_size("private-id3v2-frame")
     for i in range(0, size):
         (exists, sample) = tags.get_sample_index("private-id3v2-frame", i)
         if not exists:
             continue
         (exists, m) = sample.get_buffer().map(Gst.MapFlags.READ)
         if not exists:
             continue
         frame = FrameTextTag(m.data)
         if frame.key == "TCMP":
             string = frame.string
             if not string:
                 Logger.debug(tags.to_string())
             return string and string[-1] == "1"
     size = tags.get_tag_size("extended-comment")
     for i in range(0, size):
         (exists, sample) = tags.get_string_index("extended-comment", i)
         if not exists or not sample.startswith("COMPILATION="):
             continue
         return sample[12]
     return False
 def __connect(self, full_sync=False):
     """
         Connect service
         @param full_sync as bool
         @thread safe
     """
     if not get_network_available("LASTFM"):
         return
     try:
         self.session_key = ""
         if self.is_goa:
             auth = self.__goa.oauth2_based
             self.api_key = auth.props.client_id
             self.api_secret = auth.props.client_secret
             self.session_key = auth.call_get_access_token_sync(None)[0]
         else:
             skg = SessionKeyGenerator(self)
             self.session_key = skg.get_session_key(username=self.__login,
                                                    password_hash=md5(
                                                        self.__password))
         if full_sync:
             App().task_helper.run(self.__populate_loved_tracks)
         track = App().player.current_track
         self.playing_now(track)
     except Exception as e:
         Logger.debug("LastFM::__connect(): %s" % e)
 def __wait_for_ratelimit(self):
     """
         Sleep to respect service X-RateLimit
     """
     now = time.time()
     if self.__next_request_time > now:
         delay = self.__next_request_time - now
         Logger.debug("ListenBrainz rate limit applies, delay %d" % delay)
         time.sleep(delay)
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")
Exemple #22
0
 def __on_account_added(self, client, proxy):
     """
         Update proxy and emit account-switched
         @param client as Goa.Client
         @param proxy as Goa.Object
     """
     Logger.debug("GOA account added")
     if self._proxy is None and self.__account_matches_provider(proxy):
         self._proxy = proxy
         emit_signal(self, "account-switched")
Exemple #23
0
 def __on_account_changed(self, client, proxy):
     """
         Reset current account settings
         @param client as Goa.Client
         @param proxy as Goa.Object
     """
     Logger.debug("GOA account changed")
     if self._proxy == proxy:
         self._account = None
         self._oauth2_based = None
Exemple #24
0
 def __on_account_removed(self, client, proxy):
     """
         Try finding a new account and emit account-switched
         @param client as Goa.Client
         @param proxy as Goa.Object
     """
     Logger.debug("GOA account removed")
     if self._proxy == proxy:
         self.__find_account()
         emit_signal(self, "account-switched")
Exemple #25
0
 def __account_matches_provider(self, proxy):
     """
         True if current account match proxy account provider
         @param proxy as Goa.Object
         @return bool
     """
     account = proxy.get_account()
     Logger.debug("GOA __account_matches_provider: %s = %s ?" %
                  (account.props.provider_name, self._provider_name))
     return account.props.provider_name == self._provider_name
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()

        ret = f(*args, **kwargs)

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

        return ret
 def __on_get_secret(self, source, result):
     """
         Store secret proxy
         @param source as GObject.Object
         @param result as Gio.AsyncResult
     """
     try:
         self.__secret = Secret.Service.get_finish(result)
     except Exception as e:
         self.__secret = -1
         Logger.debug("PasswordsHelper::__on_get_secret(): %s" % e)
 def _on_bus_eos(self, bus, message):
     """
         On end of stream, stop playback
         go next otherwise
     """
     Logger.debug("Player::__on_bus_eos(): %s" % self._current_track.uri)
     if self._playbin.get_bus() == bus:
         self._next_context = NextContext.NONE
         if self._next_track.id is not None:
             self._load_track(self._next_track)
         self.emit("current-changed")
Exemple #29
0
 def __on_current_changed(self, player):
     """
         Update toolbar
         @param player as Player
     """
     Logger.debug("Toolbar::_on_current_changed()")
     self.__toolbar_playback.on_current_changed(player)
     self.__toolbar_info.on_current_changed(player)
     if App().player.current_track.id is None:
         self.__toolbar_title.hide()
     elif not App().window.miniplayer:
         self.__toolbar_title.show()
     self.__toolbar_title.on_current_changed(player)
 def __handle_ratelimit(self, response):
     """
         Set rate limit from response
         @param response as Soup.MessageHeaders
     """
     remaining = response.get("X-RateLimit-Remaining")
     reset_in = response.get("X-RateLimit-Reset-In")
     if remaining is None or reset_in is None:
         return
     Logger.debug("ListenBrainz X-RateLimit-Remaining: %s" % remaining)
     Logger.debug("ListenBrainz X-RateLimit-Reset-In: %s" % reset_in)
     if (int(remaining) == 0):
         self.__next_request_time = time.time() + int(reset_in)