def _add_search_item(self, source, param, item, remaining=0, data=None): if not item: if grilo._search_callback_counter == 0 and grilo.search_source: self.emit('no-music-found') return if data != self.model: return artist = utils.get_artist_name(item) album = utils.get_album_title(item) composer = item.get_composer() key = '%s-%s' % (artist, album) if key not in self._albums: self._albums[key] = Grl.Media() self._albums[key].set_title(album) self._albums[key].add_artist(artist) self._albums[key].set_composer(composer) self._albums[key].set_source(source.get_id()) self._albums[key].tracks = [] self._add_item(source, None, self._albums[key], 0, [self.model, 'album']) self._add_item(source, None, self._albums[key], 0, [self.model, 'artist']) self._albums[key].tracks.append(item) self._add_item(source, None, item, 0, [self.model, 'song'])
def _now_playing(self, media): """Internal method called by self.now_playing""" api_key = self._authentication.props.client_id sk = self._authentication.call_get_access_token_sync(None)[0] secret = self._authentication.props.client_secret artist = utils.get_artist_name(media) title = utils.get_media_title(media) sig = ("api_key{}artist{}methodtrack.updateNowPlayingsk{}track" "{}{}").format(api_key, artist, sk, title, secret) api_sig = md5(sig.encode()).hexdigest() request_dict = { "api_key": api_key, "method": "track.updateNowPlaying", "artist": artist, "track": title, "sk": sk, "api_sig": api_sig } try: r = requests.post("https://ws.audioscrobbler.com/2.0/", request_dict) if r.status_code != 200: logger.warn("Failed to update currently played track: %s %s" % (r.status_code, r.reason)) logger.warn(r.text) except Exception as e: logger.warn(e)
def load(self, media): self.progressScale.set_value(0) self._set_duration(media.get_duration()) self.songTotalTimeLabel.set_label( utils.seconds_to_string(media.get_duration())) self.progressScale.set_sensitive(True) self.playBtn.set_sensitive(True) self._sync_prev_next() artist = utils.get_artist_name(media) self.artistLabel.set_label(artist) self._currentArtist = artist self.coverImg.set_from_surface(self._loading_icon_surface) self.cache.lookup(media, ArtSize.xsmall, self._on_cache_lookup, None) self._currentTitle = utils.get_media_title(media) self.titleLabel.set_label(self._currentTitle) self._currentTimestamp = int(time.time()) url = media.get_url() if url != self.player.get_value('current-uri', 0): self.player.set_property('uri', url) if self.currentTrack and self.currentTrack.valid(): currentTrack = self.playlist.get_iter(self.currentTrack.get_path()) self.emit('playlist-item-changed', self.playlist, currentTrack) self.emit('current-changed') self._validate_next_track()
def _add_item(self, source, param, item, remaining=0, data=None): if (not item and remaining == 0): self._window.notifications_popup.pop_loading() self._sidebar.show() return self._offset += 1 artist = utils.get_artist_name(item) row = None if not artist.casefold() in self._artists: # populate sidebar row = SidebarRow() row.props.text = artist row.connect('notify::selected', self._on_selection_changed) self.bind_property('selection-mode', row, 'selection-mode') self._sidebar.add(row) self._artists[artist.casefold()] = { 'albums': [], 'widget': None } self._artists[artist.casefold()]['albums'].append(item) if (row is not None and len(self._sidebar) == 1): self._sidebar.select_row(row) self._sidebar.emit('row-activated', row)
def _add_item(self, source, param, item, remaining=0, data=None): if (not item and remaining == 0): self._window.notifications_popup.pop_loading() self._sidebar.show() return self._offset += 1 artist = utils.get_artist_name(item) if not artist.casefold() in self._artists: # populate sidebar row = Gtk.ListBoxRow() row.artist = artist box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) row.add(box) row.check = Gtk.CheckButton(margin_left=12) row.check.connect('toggled', self._on_selection_toggled) artist_label = Gtk.Label(label=artist, xalign=0, xpad=16, ypad=16, ellipsize=Pango.EllipsizeMode.END) box.pack_start(row.check, False, True, 0) box.pack_start(artist_label, True, True, 0) self._sidebar.add(row) row.show_all() row.check.hide() row.check.bind_property('visible', self, 'selection_mode', GObject.BindingFlags.BIDIRECTIONAL) self._artists[artist.casefold()] = {'albums': [], 'widget': None} self._artists[artist.casefold()]['albums'].append(item)
def load(self, media): self._progress_scale_zero() self._set_duration(media.get_duration()) self.songTotalTimeLabel.set_label( utils.seconds_to_string(media.get_duration())) self.progressScale.set_sensitive(True) self.playBtn.set_sensitive(True) self._sync_prev_next() artist = utils.get_artist_name(media) self.artistLabel.set_label(artist) self._currentArtist = artist self.coverImg.set_from_surface(self._loading_icon_surface) self.cache.lookup(media, ArtSize.xsmall, self._on_cache_lookup, None) self._currentTitle = utils.get_media_title(media) self.titleLabel.set_label(self._currentTitle) self._currentTimestamp = int(time.time()) url = media.get_url() if url != self.player.get_value('current-uri', 0): self.player.set_property('uri', url) if self.currentTrack and self.currentTrack.valid(): currentTrack = self.playlist.get_iter(self.currentTrack.get_path()) self.emit('playlist-item-changed', self.playlist, currentTrack) self.emit('current-changed') self._validate_next_track()
def _add_item(self, source, param, item, remaining=0, data=None): if not item: if remaining == 0: self.view.set_model(self.model) self.window.pop_loading_notification() self.view.show() return self._offset += 1 artist = utils.get_artist_name(item) title = utils.get_media_title(item) _iter = self.model.append(None) loading_icon = Gdk.pixbuf_get_from_surface( self._loadin_icon_surface, 0, 0, self._loading_icon_surface.get_width(), self._loading_icon_surface.get_height()) self.model[_iter][0, 1, 2, 3, 4, 5, 7, 9] = [ str(item.get_id()), '', title, artist, loading_icon, item, 0, False ] self.cache.lookup(item, self._iconWidth, self._iconHeight, self._on_lookup_ready, _iter)
def _add_search_item(self, source, param, item, remaining=0, data=None): if not item: if (grilo._search_callback_counter == 0 and grilo.search_source): self.props.search_state = Search.State.NO_RESULT return if data != self.model: return artist = utils.get_artist_name(item) album = utils.get_album_title(item) key = '%s-%s' % (artist, album) if key not in self._albums: self._albums[key] = Grl.Media() self._albums[key].set_title(album) self._albums[key].add_artist(artist) self._albums[key].set_source(source.get_id()) self._albums[key].songs = [] self._add_item( source, None, self._albums[key], 0, [self.model, 'album']) self._add_item( source, None, self._albums[key], 0, [self.model, 'artist']) self._albums[key].songs.append(item) self._add_item(source, None, item, 0, [self.model, 'song'])
def update(self, album): """Update the album widget. :param Grl.Media album: The grilo media album """ # reset view self._songs = [] self._create_model() for widget in self._disc_listbox.get_children(): self._disc_listbox.remove(widget) self._cover_stack.update(album) self._duration = 0 self._album_name = utils.get_album_title(album) artist = utils.get_artist_name(album) self._title_label.props.label = self._album_name self._title_label.props.tooltip_text = self._album_name self._artist_label.props.label = artist self._artist_label.props.tooltip_text = artist year = utils.get_media_year(album) if not year: year = '----' self._released_info_label.props.label = year self._set_composer_label(album) self._player.connect('song-changed', self._update_model) grilo.populate_album_songs(album, self.add_item)
def _add_item(self, source, param, item, remaining=0, data=None): self.window.notification.set_timeout(0) if not item: if remaining == 0: self.view.set_model(self.model) self.window.notification.dismiss() self.view.show() return self._offset += 1 artist = utils.get_artist_name(item) title = utils.get_media_title(item) _iter = self.model.append(None) loading_icon = Gdk.pixbuf_get_from_surface( self._loadin_icon_surface, 0, 0, self._loading_icon_surface.get_width(), self._loading_icon_surface.get_height()) self.model[_iter][0, 1, 2, 3, 4, 5, 7, 9] = [ str(item.get_id()), '', title, artist, loading_icon, item, 0, False ] self.cache.lookup(item, self._iconWidth, self._iconHeight, self._on_lookup_ready, _iter)
def load(self, media): self._progress_scale_zero() self._set_duration(media.get_duration()) self.songTotalTimeLabel.set_label( utils.seconds_to_string(media.get_duration())) self.progressScale.set_sensitive(True) self.playBtn.set_sensitive(True) self._sync_prev_next() artist = utils.get_artist_name(media) self.artistLabel.set_label(artist) self._cover_stack.update(media) title = utils.get_media_title(media) self.titleLabel.set_label(title) self._time_stamp = int(time.time()) url = media.get_url() if url != self.player.get_value('current-uri', 0): self.player.set_property('uri', url) if self.currentTrack and self.currentTrack.valid(): currentTrack = self.playlist.get_iter(self.currentTrack.get_path()) self.emit('playlist-item-changed', self.playlist, currentTrack) self.emit('current-changed') self._validate_next_track()
def __init__(self, media): """Initialize the AlbumCover :param Grl.Media media: The media object to use """ super().__init__() AlbumCover._nr_albums += 1 self._media = media self._tooltip = TwoLineTip() artist = utils.get_artist_name(media) title = utils.get_media_title(media) self._tooltip.props.title = utils.get_artist_name(media) self._tooltip.props.subtitle = utils.get_media_title(media) self._artist_label.props.label = artist self._title_label.props.label = title self.bind_property( 'selected', self._check, 'active', GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE) self.bind_property('selection-mode', self._check, 'visible', GObject.BindingFlags.BIDIRECTIONAL) self.connect('query-tooltip', self._on_tooltip_query) self._events.add_events(Gdk.EventMask.TOUCH_MASK) self._cover_stack.props.size = Art.Size.MEDIUM self.show() # FIXME: To work around slow updating of the albumsview, # load album covers with a fixed delay. This results in a # quick first show with a placeholder cover and then a # reasonably responsive view while loading the actual # covers. GLib.timeout_add(50 * self._nr_albums, self._cover_stack.update, media, priority=GLib.PRIORITY_LOW)
def __init__(self, media): """Initialize the AlbumCover :param Grl.Media media: The media object to use """ super().__init__() AlbumCover._nr_albums += 1 self._media = media self._tooltip = TwoLineTip() artist = utils.get_artist_name(media) title = utils.get_media_title(media) self._tooltip.props.title = utils.get_artist_name(media) self._tooltip.props.subtitle = utils.get_media_title(media) self._artist_label.props.label = artist self._title_label.props.label = title self.bind_property( 'selected', self._check, 'active', GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE) self.bind_property( 'selection-mode', self._check, 'visible', GObject.BindingFlags.BIDIRECTIONAL) self.connect('query-tooltip', self._on_tooltip_query) self._events.add_events(Gdk.EventMask.TOUCH_MASK) self._cover_stack.props.size = Art.Size.MEDIUM self.show() # FIXME: To work around slow updating of the albumsview, # load album covers with a fixed delay. This results in a # quick first show with a placeholder cover and then a # reasonably responsive view while loading the actual # covers. GLib.timeout_add( 50 * self._nr_albums, self._cover_stack.update, media, priority=GLib.PRIORITY_LOW)
def __init__(self, media, player, model, header_bar, selection_mode_allowed, size_group=None, cover_size_group=None): super().__init__(orientation=Gtk.Orientation.HORIZONTAL) self._size_group = size_group self._cover_size_group = cover_size_group scale = self.get_scale_factor() self._cache = AlbumArtCache(scale) self._loading_icon_surface = DefaultIcon(scale).get( DefaultIcon.Type.loading, ArtSize.MEDIUM) self._media = media self._player = player self._artist = utils.get_artist_name(self._media) self._album_title = utils.get_album_title(self._media) self._model = model self._header_bar = header_bar self._selection_mode = False self._selection_mode_allowed = selection_mode_allowed self._songs = [] self._header_bar._select_button.connect( 'toggled', self._on_header_select_button_toggled) ui = Gtk.Builder() ui.add_from_resource('/org/gnome/Music/ArtistAlbumWidget.ui') self.cover = ui.get_object('cover') self.cover.set_from_surface(self._loading_icon_surface) self._disc_listbox = ui.get_object('disclistbox') self._disc_listbox.set_selection_mode_allowed( self._selection_mode_allowed) ui.get_object('title').set_label(self._album_title) creation_date = self._media.get_creation_date() if creation_date: year = creation_date.get_year() ui.get_object('year').set_markup( '<span color=\'grey\'>{}</span>'.format(year)) if self._size_group: self._size_group.add_widget(ui.get_object('box1')) if self._cover_size_group: self._cover_size_group.add_widget(self.cover) self.pack_start(ui.get_object('ArtistAlbumWidget'), True, True, 0) GLib.idle_add(self._update_album_art) grilo.populate_album_songs(self._media, self._add_item)
def _add_to_blacklist(self): album = utils.get_album_title(self._media) artist = utils.get_artist_name(self._media) if artist not in self._blacklist: self._blacklist[artist] = [] album_stripped = MediaArt.strip_invalid_entities(album) self._blacklist[artist].append(album_stripped)
def _in_blacklist(self): album = utils.get_album_title(self._media) artist = utils.get_artist_name(self._media) album_stripped = MediaArt.strip_invalid_entities(album) if artist in self._blacklist: if album_stripped in self._blacklist[artist]: return True return False
def update(self, media): """Update the CoreAlbum object with new info :param Grl.Media media: A media object """ self.props.media = media self.props.artist = utils.get_artist_name(media) self.props.composer = media.get_composer() self.props.title = utils.get_media_title(media) self.props.year = utils.get_media_year(media)
def _lookup_local(self, item, callback, itr, art_size): """Checks if there is already a local art file, if not calls the remote lookup function""" album = utils.get_album_title(item) artist = utils.get_artist_name(item) def stream_open(thumb_file, result, arguments): try: stream = thumb_file.read_finish(result) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) do_callback(None) return GdkPixbuf.Pixbuf.new_from_stream_async(stream, None, pixbuf_loaded, None) def pixbuf_loaded(stream, result, data): try: pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(result) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) do_callback(None) return do_callback(pixbuf) return def do_callback(pixbuf): if not pixbuf: surface = DefaultIcon(self._scale).get(DefaultIcon.Type.music, art_size) else: surface = _make_icon_frame(pixbuf, art_size, self._scale) # Sets the thumbnail location for MPRIS to use. item.set_thumbnail( GLib.filename_to_uri(thumb_file.get_path(), None)) GLib.idle_add(callback, surface, itr) return success, thumb_file = MediaArt.get_file(artist, album, "album") if (success and thumb_file.query_exists()): thumb_file.read_async(GLib.PRIORITY_LOW, None, stream_open, None) return stripped_album = MediaArt.strip_invalid_entities(album) if (artist in self.blacklist and stripped_album in self.blacklist[artist]): do_callback(None) return self._lookup_remote(item, callback, itr, art_size)
def query(self, media): """Start the remote query :param Grl.Media media: The media object to search art for """ self._album = utils.get_album_title(media) self._artist = utils.get_artist_name(media) # FIXME: It seems this Grilo query does not always return, # especially on queries with little info. grilo.get_album_art_for_item(media, self._remote_album_art)
def _set_grilo_thumbnail_path(self): # TODO: This sets the thumbnail path for the Grilo Media object # to be used by MPRIS. However, calling this by default for # every cache hit is unnecessary. album = utils.get_album_title(self._media) artist = utils.get_artist_name(self._media) success, thumb_file = MediaArt.get_file(artist, album, "album") if success: self._media.set_thumbnail( GLib.filename_to_uri(thumb_file.get_path(), None))
def _update_view(self, player, playlist, current_iter): media = playlist[current_iter][player.Field.SONG] self._duration_label.set_label( utils.seconds_to_string(media.get_duration())) self._play_button.set_sensitive(True) self._sync_prev_next() self._artist_label.set_label(utils.get_artist_name(media)) self._title_label.set_label(utils.get_media_title(media)) self._cover_stack.update(media)
def update(self, media): self.props.media = media self.props.album = utils.get_album_title(media) self.props.album_disc_number = media.get_album_disc_number() self.props.artist = utils.get_artist_name(media) self.props.duration = media.get_duration() self.props.favorite = media.get_favourite() self.props.play_count = media.get_play_count() self.props.title = utils.get_media_title(media) self.props.track_number = media.get_track_number() self.props.url = media.get_url()
def _on_child_activated(self, widget, child, user_data=None): if self.props.selection_mode: return item = child.props.media # Update and display the album widget if not in selection mode self._album_widget.update(item) self._headerbar.props.state = HeaderBar.State.CHILD self._headerbar.props.title = utils.get_album_title(item) self._headerbar.props.subtitle = utils.get_artist_name(item) self.set_visible_child(self._album_widget)
def _lastfm_api_call(self, media, time_stamp, request_type_key): """Internal method called by self.scrobble""" api_key = self._goa_lastfm.client_id sk = self._goa_lastfm.session_key secret = self._goa_lastfm.secret artist = utils.get_artist_name(media) title = utils.get_media_title(media) request_type = { "update now playing": "track.updateNowPlaying", "scrobble": "track.scrobble" } # The album is optional. So only provide it when it is # available. album = media.get_album() request_dict = {} if album: request_dict.update({ "album": album }) if time_stamp is not None: request_dict.update({ "timestamp": str(time_stamp) }) request_dict.update({ "api_key": api_key, "method": request_type[request_type_key], "artist": artist, "track": title, "sk": sk, }) sig = "" for key in sorted(request_dict): sig += key + request_dict[key] sig += secret api_sig = md5(sig.encode()).hexdigest() request_dict.update({ "api_sig": api_sig }) msg = Soup.form_request_new_from_hash( "POST", "https://ws.audioscrobbler.com/2.0/", request_dict) self._soup_session.queue_message( msg, self._lastfm_api_callback, request_type_key)
def _get_metadata(self, media=None, index=None): song_dbus_path = self._get_song_dbus_path(media, index) if not self.player.props.current_song: return {'mpris:trackid': GLib.Variant('o', song_dbus_path)} if not media: media = self.player.props.current_song length = media.get_duration() * 1e6 user_rating = 1.0 if media.get_favourite() else 0.0 artist = utils.get_artist_name(media) metadata = { 'mpris:trackid': GLib.Variant('o', song_dbus_path), 'xesam:url': GLib.Variant('s', media.get_url()), 'mpris:length': GLib.Variant('x', length), 'xesam:trackNumber': GLib.Variant('i', media.get_track_number()), 'xesam:useCount': GLib.Variant('i', media.get_play_count()), 'xesam:userRating': GLib.Variant('d', user_rating), 'xesam:title': GLib.Variant('s', utils.get_media_title(media)), 'xesam:album': GLib.Variant('s', utils.get_album_title(media)), 'xesam:artist': GLib.Variant('as', [artist]), 'xesam:albumArtist': GLib.Variant('as', [artist]) } genre = media.get_genre() if genre is not None: metadata['xesam:genre'] = GLib.Variant('as', [genre]) last_played = media.get_last_played() if last_played is not None: last_played_str = last_played.format("%FT%T%:z") metadata['xesam:lastUsed'] = GLib.Variant('s', last_played_str) # If the media has already been part of an MPRIS playlist, its # thumbnail is already set. Otherwise, try to look for it in the # cache directory and set the media thumbnail for a future use. # The search is only through the cache to prevent any delayed # loading. # FIXME: The thumbnail retrieval should take place in the # player. art_url = media.get_thumbnail() if not art_url: thumb_file = lookup_art_file_from_cache(media) if thumb_file: art_url = GLib.filename_to_uri(thumb_file.get_path()) media.set_thumbnail(art_url) if art_url: metadata['mpris:artUrl'] = GLib.Variant('s', art_url) return metadata
def _lastfm_api_call(self, media, time_stamp, request_type_key): """Internal method called by self.scrobble""" api_key = self._goa_lastfm.client_id sk = self._goa_lastfm.session_key secret = self._goa_lastfm.secret artist = utils.get_artist_name(media) title = utils.get_media_title(media) request_type = { "update now playing": "track.updateNowPlaying", "scrobble": "track.scrobble" } # The album is optional. So only provide it when it is # available. album = media.get_album() request_dict = {} if album: request_dict.update({"album": album}) if time_stamp is not None: request_dict.update({"timestamp": time_stamp}) request_dict.update({ "api_key": api_key, "method": request_type[request_type_key], "artist": artist, "track": title, "sk": sk, }) sig = "" for key in sorted(request_dict): sig += key + str(request_dict[key]) sig += secret api_sig = md5(sig.encode()).hexdigest() request_dict.update({"api_sig": api_sig}) try: r = requests.post("https://ws.audioscrobbler.com/2.0/", request_dict) if r.status_code != 200: logger.warning("Failed to {} track: {} {}".format( request_type_key, r.status_code, r.reason)) logger.warning(r.text) except Exception as e: logger.warning(e)
def lookup_art_file_from_cache(media): """Lookup MediaArt cache art of an album or song. :param Grl.Media media: song or album :returns: a cache file :rtype: Gio.File """ album = utils.get_album_title(media) artist = utils.get_artist_name(media) success, thumb_file = MediaArt.get_file(artist, album, "album") if (not success or not thumb_file.query_exists()): return None return thumb_file
def _add_item(self, source, param, item, remaining=0, data=None): self.window.notification.set_timeout(0) if item is None: if remaining == 0: self.view.set_model(self.model) self.window.notification.dismiss() self.view.show() return self._offset += 1 artist = utils.get_artist_name(item) if not artist.casefold() in self._artists: _iter = self.model.insert_with_valuesv(-1, [2], [artist]) self._artists[artist.casefold()] = {'iter': _iter, 'albums': [], 'widget': None} self._artists[artist.casefold()]['albums'].append(item)
def __init__(self, media, player, model, header_bar, selection_mode_allowed, size_group=None, cover_size_group=None): super().__init__(orientation=Gtk.Orientation.HORIZONTAL) self._size_group = size_group self._cover_size_group = cover_size_group self._media = media self._player = player self._artist = utils.get_artist_name(self._media) self._album_title = utils.get_album_title(self._media) self._model = model self._header_bar = header_bar self._selection_mode = False self._selection_mode_allowed = selection_mode_allowed self._songs = [] self._cover_stack.props.size = Art.Size.MEDIUM self._cover_stack.update(self._media) allowed = self._selection_mode_allowed self._disc_list_box.props.selection_mode_allowed = allowed self.bind_property( 'selection-mode', self._disc_list_box, 'selection-mode', GObject.BindingFlags.BIDIRECTIONAL | GObject.BindingFlags.SYNC_CREATE) self._title.props.label = self._album_title year = utils.get_media_year(self._media) if year: self._year.props.label = year if self._size_group: self._size_group.add_widget(self._album_box) if self._cover_size_group: self._cover_size_group.add_widget(self._cover_stack) grilo.populate_album_songs(self._media, self._add_item)
def _add_item(self, source, param, item, remaining=0, data=None): if not item: if remaining == 0: self._view.set_model(self.model) self._window.notifications_popup.pop_loading() self._view.show() return self._offset += 1 artist = utils.get_artist_name(item) title = utils.get_media_title(item) itr = self.model.append(None) self.model[itr][0, 1, 2, 3, 5, 7, 9] = [ str(item.get_id()), '', title, artist, item, 0, False ]
def _add_item(self, source, param, item, remaining=0, data=None): if (not item and remaining == 0): self._view.set_model(self.model) self._window.notifications_popup.pop_loading() self._view.show() return self._offset += 1 artist = utils.get_artist_name(item) if not artist.casefold() in self._artists: itr = self.model.insert_with_valuesv(-1, [2], [artist]) self._artists[artist.casefold()] = { 'iter': itr, 'albums': [], 'widget': None } self._artists[artist.casefold()]['albums'].append(item)
def _add_item(self, source, param, item, remaining=0, data=None): if (not item and remaining == 0): self.view.set_model(self.model) self.window.pop_loading_notification() self.view.show() return self._offset += 1 artist = utils.get_artist_name(item) if not artist.casefold() in self._artists: itr = self.model.insert_with_valuesv(-1, [2], [artist]) self._artists[artist.casefold()] = { 'iter': itr, 'albums': [], 'widget': None } self._artists[artist.casefold()]['albums'].append(item)
def _add_item_to_model(self, item, model): if not item: self._update_songs_count() if self.player.playlist: self.player._validate_next_track() self.emit('playlist-songs-loaded') return self._offset += 1 title = utils.get_media_title(item) item.set_title(title) artist = utils.get_artist_name(item) model.insert_with_valuesv(-1, [2, 3, 5, 9], [title, artist, item, item.get_favourite()]) self._songs_count += 1
def query(self, media): """Start the cache query :param Grl.Media media: The media object to search art for """ album = utils.get_album_title(media) artist = utils.get_artist_name(media) success, thumb_file = MediaArt.get_file(artist, album, "album") if (success and thumb_file.query_exists()): thumb_file.read_async(GLib.PRIORITY_LOW, None, self._open_stream, None) return self.emit('miss')
def query(self, media): """Start the cache query :param Grl.Media media: The media object to search art for """ album = utils.get_album_title(media) artist = utils.get_artist_name(media) success, thumb_file = MediaArt.get_file(artist, album, "album") if (success and thumb_file.query_exists()): thumb_file.read_async( GLib.PRIORITY_LOW, None, self._open_stream, None) return self.emit('miss')
def query(self, media): """Start the remote query :param Grl.Media media: The media object to search art for """ self._album = utils.get_album_title(media) self._artist = utils.get_artist_name(media) self._media = media if not grilo.props.cover_sources: self.emit('no-remote-sources') grilo.connect('notify::cover-sources', self._on_grilo_cover_sources_changed) else: # FIXME: It seems this Grilo query does not always return, # especially on queries with little info. grilo.get_album_art_for_item(media, self._remote_album_art)
def _add_song_to_model(self, song, model, index=-1): """Add song to a playlist :param Grl.Media song: song to add :param Gtk.ListStore model: model """ if not song: return None title = utils.get_media_title(song) song.set_title(title) artist = utils.get_artist_name(song) iter_ = model.insert_with_valuesv( index, [2, 3, 5, 9], [title, artist, song, song.get_favourite()]) self._update_songs_count(self._songs_count + 1) return iter_
def _lastfm_api_call(self, media, time_stamp, request_type_key): """Internal method called by self.scrobble""" api_key = self._goa_lastfm.client_id sk = self._goa_lastfm.session_key secret = self._goa_lastfm.secret artist = utils.get_artist_name(media) title = utils.get_media_title(media) request_type = { "update now playing": "track.updateNowPlaying", "scrobble": "track.scrobble" } # The album is optional. So only provide it when it is # available. album = media.get_album() request_dict = {} if album: request_dict.update({"album": album}) if time_stamp is not None: request_dict.update({"timestamp": str(time_stamp)}) request_dict.update({ "api_key": api_key, "method": request_type[request_type_key], "artist": artist, "track": title, "sk": sk, }) sig = "" for key in sorted(request_dict): sig += key + request_dict[key] sig += secret api_sig = md5(sig.encode()).hexdigest() request_dict.update({"api_sig": api_sig}) msg = Soup.form_request_new_from_hash( "POST", "https://ws.audioscrobbler.com/2.0/", request_dict) self._soup_session.queue_message(msg, self._lastfm_api_callback, request_type_key)
def query(self, media): """Start the remote query :param Grl.Media media: The media object to search art for """ self._album = utils.get_album_title(media) self._artist = utils.get_artist_name(media) self._media = media if not grilo.props.cover_sources: self.emit('no-remote-sources') grilo.connect( 'notify::cover-sources', self._on_grilo_cover_sources_changed) else: # FIXME: It seems this Grilo query does not always return, # especially on queries with little info. grilo.get_album_art_for_item(media, self._remote_album_art)
def _get_metadata(self, media=None, index=None): song_dbus_path = self._get_song_dbus_path(media, index) if not self.player.props.current_song: return { 'mpris:trackid': GLib.Variant('o', song_dbus_path) } if not media: media = self.player.props.current_song length = media.get_duration() * 1e6 user_rating = 1.0 if media.get_favourite() else 0.0 artist = utils.get_artist_name(media) metadata = { 'mpris:trackid': GLib.Variant('o', song_dbus_path), 'xesam:url': GLib.Variant('s', media.get_url()), 'mpris:length': GLib.Variant('x', length), 'xesam:trackNumber': GLib.Variant('i', media.get_track_number()), 'xesam:useCount': GLib.Variant('i', media.get_play_count()), 'xesam:userRating': GLib.Variant('d', user_rating), 'xesam:title': GLib.Variant('s', utils.get_media_title(media)), 'xesam:album': GLib.Variant('s', utils.get_album_title(media)), 'xesam:artist': GLib.Variant('as', [artist]), 'xesam:albumArtist': GLib.Variant('as', [artist]) } genre = media.get_genre() if genre is not None: metadata['xesam:genre'] = GLib.Variant('as', [genre]) last_played = media.get_last_played() if last_played is not None: last_played_str = last_played.format("%FT%T%:z") metadata['xesam:lastUsed'] = GLib.Variant('s', last_played_str) art_url = media.get_thumbnail() if art_url is not None: metadata['mpris:artUrl'] = GLib.Variant('s', art_url) return metadata
def _add_item(self, source, param, item, remaining=0, data=None): """Adds track item to the model""" if not item and not remaining: self.view.set_model(self.model) self.window.pop_loading_notification() self.view.show() return self._offset += 1 item.set_title(utils.get_media_title(item)) artist = utils.get_artist_name(item) if not item.get_url(): return self.model.insert_with_valuesv(-1, [2, 3, 5, 9], [ utils.get_media_title(item), artist, item, item.get_favourite() ])
def _on_child_activated(self, widget, child, user_data=None): item = child.media_item if self.star_handler.star_renderer_click: self.star_handler.star_renderer_click = False return # Toggle the selection when in selection mode if self.selection_mode: child.check.set_active(not child.check.get_active()) return title = utils.get_media_title(item) self._escaped_title = title self._artist = utils.get_artist_name(item) self._albumWidget.update(self._artist, title, item, self.header_bar, self.selection_toolbar) self.header_bar.set_state(ToolbarState.CHILD_VIEW) self.header_bar.header_bar.set_title(self._escaped_title) self.header_bar.header_bar.sub_title = self._artist self.set_visible_child(self._albumWidget)
def _update_view(self, player): """Updates model when the song changes :param Player player: The main player object """ current_song = player.props.current_song self._duration_label.set_label( utils.seconds_to_string(current_song.get_duration())) self._play_button.set_sensitive(True) self._sync_prev_next() artist = utils.get_artist_name(current_song) title = utils.get_media_title(current_song) self._title_label.props.label = title self._artist_label.props.label = artist self._tooltip.props.title = title self._tooltip.props.subtitle = artist self._cover_stack.update(current_song)
def query(self, media): """Start the local query :param Grl.Media media: The media object to search art for """ if media.get_url() is None: self.emit('unavailable') return self._album = utils.get_album_title(media) self._artist = utils.get_artist_name(media) self._media = media try: discoverer = GstPbutils.Discoverer.new(Gst.SECOND) except GLib.Error as error: logger.warning("Error: {}, {}".format(error.domain, error.message)) self._lookup_cover_in_directory() return discoverer.connect('discovered', self._discovered) discoverer.start() success, path = MediaArt.get_path(self._artist, self._album, "album") if not success: self.emit('unavailable') discoverer.stop() return self._path = path success = discoverer.discover_uri_async(self._media.get_url()) if not success: logger.warning("Could not add url to discoverer.") self.emit('unavailable') discoverer.stop() return
def load(self, media): self.progressScale.set_value(0) self._set_duration(media.get_duration()) self.songTotalTimeLabel.set_label(self.seconds_to_string(media.get_duration())) self.progressScale.set_sensitive(True) self.playBtn.set_sensitive(True) self._sync_prev_next() artist = utils.get_artist_name(media) self.artistLabel.set_label(artist) self._currentArtist = artist album = _("Unknown Album") try: assert media.get_album() is not None album = media.get_album() except: self._currentAlbum = album self.coverImg.set_from_pixbuf(self._no_artwork_icon) self.cache.lookup( media, ART_SIZE, ART_SIZE, self._on_cache_lookup, None, artist, album) self._currentTitle = AlbumArtCache.get_media_title(media) self.titleLabel.set_label(self._currentTitle) self._currentTimestamp = int(time.time()) url = media.get_url() if url != self.player.get_value('current-uri', 0): self.player.set_property('uri', url) if self.currentTrack and self.currentTrack.valid(): currentTrack = self.playlist.get_iter(self.currentTrack.get_path()) self.emit('playlist-item-changed', self.playlist, currentTrack) self.emit('current-changed') self._validate_next_track()
def _create_album_item(self, item): artist = utils.get_artist_name(item) title = utils.get_media_title(item) builder = Gtk.Builder.new_from_resource("/org/gnome/Music/AlbumCover.ui") child = Gtk.FlowBoxChild() child.image = builder.get_object("image") child.check = builder.get_object("check") child.title = builder.get_object("title") child.subtitle = builder.get_object("subtitle") child.events = builder.get_object("events") child.media_item = item child.title.set_label(title) child.subtitle.set_label(artist) child.image.set_from_surface(self._loading_icon_surface) # In the case of off-sized icons (eg. provided in the soundfile) # keep the size request equal to all other icons to get proper # alignment with GtkFlowBox. child.image.set_property("width-request", ArtSize.medium.width) child.image.set_property("height-request", ArtSize.medium.height) child.events.connect("button-release-event", self._on_album_event_triggered, child) child.check_handler_id = child.check.connect("notify::active", self._on_child_toggled, child) child.check.bind_property("visible", self, "selection_mode", GObject.BindingFlags.BIDIRECTIONAL) child.add(builder.get_object("main_box")) child.show() self.cache.lookup(item, ArtSize.medium, self._on_lookup_ready, child) return child
def update(self, artist, album, item, header_bar, selection_toolbar): """Update the album widget. :param str artist: The artist name :param str album: The album name :param item: The grilo media item :param header_bar: The header bar object :param selection_toolbar: The selection toolbar object """ self.selection_toolbar = selection_toolbar self._header_bar = header_bar self._album = album real_artist = utils.get_artist_name(item) self._ui.get_object('cover').set_from_pixbuf(self._loading_icon) ALBUM_ART_CACHE.lookup(item, 256, 256, self._on_look_up, None, real_artist, album) self._duration = 0 self._create_model() GLib.idle_add(grilo.populate_album_songs, item, self.add_item) header_bar._select_button.connect( 'toggled', self._on_header_select_button_toggled) header_bar._cancel_button.connect( 'clicked', self._on_header_cancel_button_clicked) self.view.connect('view-selection-changed', self._on_view_selection_changed) self.view.set_model(self.model) escaped_artist = GLib.markup_escape_text(artist) escaped_album = GLib.markup_escape_text(album) self._ui.get_object('artist_label').set_markup(escaped_artist) self._ui.get_object('title_label').set_markup(escaped_album) if (item.get_creation_date()): self._ui.get_object('released_label_info').set_text( str(item.get_creation_date().get_year())) else: self._ui.get_object('released_label_info').set_text('----') self._player.connect('playlist-item-changed', self._update_model)
def update(self, album): """Update the album widget. :param Grl.Media album: The grilo media album """ # reset view self._songs = [] self._create_model() for widget in self._disc_listbox.get_children(): self._disc_listbox.remove(widget) self._cover_stack.update(album) self._duration = 0 self._album_name = utils.get_album_title(album) artist = utils.get_artist_name(album) self._title_label.props.label = self._album_name self._title_label.props.tooltip_text = self._album_name self._artist_label.props.label = artist self._artist_label.props.tooltip_text = artist year = utils.get_media_year(album) if not year: year = '----' self._released_info_label.props.label = year self._set_composer_label(album) self._album = album self._player.connect('song-changed', self._update_model) grilo.populate_album_songs(album, self.add_item)
def _get_metadata(self, media=None): if not media: media = self.player.get_current_media() if not media: return {} metadata = { 'mpris:trackid': GLib.Variant('o', self._get_media_id(media)), 'xesam:url': GLib.Variant('s', media.get_url()) } try: length = media.get_duration() * 1000000 assert length is not None metadata['mpris:length'] = GLib.Variant('x', length) except: pass try: trackNumber = media.get_track_number() assert trackNumber is not None metadata['xesam:trackNumber'] = GLib.Variant('i', trackNumber) except: pass try: useCount = media.get_play_count() assert useCount is not None metadata['xesam:useCount'] = GLib.Variant('i', useCount) except: pass try: userRating = media.get_rating() assert userRating is not None metadata['xesam:userRating'] = GLib.Variant('d', userRating) except: pass try: title = utils.get_media_title(media) assert title is not None metadata['xesam:title'] = GLib.Variant('s', title) except: pass album = utils.get_album_title(media) metadata['xesam:album'] = GLib.Variant('s', album) artist = utils.get_artist_name(media) metadata['xesam:artist'] = GLib.Variant('as', [artist]) metadata['xesam:albumArtist'] = GLib.Variant('as', [artist]) try: genre = media.get_genre() assert genre is not None metadata['xesam:genre'] = GLib.Variant('as', genre) except: pass try: lastUsed = media.get_last_played() assert lastUsed is not None metadata['xesam:lastUsed'] = GLib.Variant('s', lastUsed) except: pass try: artUrl = media.get_thumbnail() assert artUrl is not None metadata['mpris:artUrl'] = GLib.Variant('s', artUrl) except: pass return metadata
def _update_album_art(self): artist = utils.get_artist_name(self.album) ALBUM_ART_CACHE.lookup(self.album, 128, 128, self._get_album_cover, None, artist, self.album.get_title())
def _lookup_local(self, item, callback, itr, art_size): """Checks if there is already a local art file, if not calls the remote lookup function""" album = utils.get_album_title(item) artist = utils.get_artist_name(item) def stream_open(thumb_file, result, arguments): try: stream = thumb_file.read_finish(result) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) do_callback(None) return GdkPixbuf.Pixbuf.new_from_stream_async(stream, None, pixbuf_loaded, None) def pixbuf_loaded(stream, result, data): try: pixbuf = GdkPixbuf.Pixbuf.new_from_stream_finish(result) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) do_callback(None) return do_callback(pixbuf) return def do_callback(pixbuf): if not pixbuf: surface = DefaultIcon(self._scale).get(DefaultIcon.Type.music, art_size) else: surface = _make_icon_frame(pixbuf, art_size, self._scale) # Sets the thumbnail location for MPRIS to use. item.set_thumbnail(GLib.filename_to_uri(thumb_file.get_path(), None)) GLib.idle_add(callback, surface, itr) return success, thumb_file = MediaArt.get_file(artist, album, "album") if (success and thumb_file.query_exists()): thumb_file.read_async(GLib.PRIORITY_LOW, None, stream_open, None) return stripped_album = MediaArt.strip_invalid_entities(album) if (artist in self.blacklist and stripped_album in self.blacklist[artist]): do_callback(None) return self._lookup_remote(item, callback, itr, art_size)
def _set_album_headerbar(self, album): self._headerbar.props.state = HeaderBar.State.CHILD self._headerbar.props.title = utils.get_album_title(album) self._headerbar.props.subtitle = utils.get_artist_name(album)
def _add_item(self, source, param, item, remaining=0, data=None): if data is None: return model, category = data self.found_items_number = ( self.model.iter_n_children(self.head_iters[0]) + self.model.iter_n_children(self.head_iters[1]) + self.model.iter_n_children(self.head_iters[2]) + self.model.iter_n_children(self.head_iters[3])) if category == 'song' and self.found_items_number == 0 and remaining == 0: if grilo.search_source: self.emit('no-music-found') # We need to remember the view before the search view if self.window.curr_view != self.window.views[5] and \ self.window.prev_view != self.window.views[5]: self.previous_view = self.window.prev_view if remaining == 0: self.window.pop_loading_notification() self.view.show() if not item or model != self.model: return self._offset += 1 title = utils.get_media_title(item) item.set_title(title) artist = utils.get_artist_name(item) # FIXME: Can't be None in treemodel composer = item.get_composer() or "" group = 3 try: group = {'album': 0, 'artist': 1, 'song': 2}[category] except: pass # FIXME: HiDPI icon lookups return a surface that can't be # scaled by GdkPixbuf, so it results in a * scale factor sized # icon for the search view. _iter = None if category == 'album': _iter = self.model.insert_with_values( self.head_iters[group], -1, [0, 2, 3, 4, 5, 9, 11, 13], [str(item.get_id()), title, artist, self._loading_icon, item, 2, category, composer]) self.cache.lookup(item, ArtSize.small, self._on_lookup_ready, _iter) elif category == 'song': _iter = self.model.insert_with_values( self.head_iters[group], -1, [0, 2, 3, 4, 5, 9, 11, 13], [str(item.get_id()), title, artist, self._loading_icon, item, 2 if source.get_id() != 'grl-tracker-source' \ else item.get_favourite(), category, composer]) self.cache.lookup(item, ArtSize.small, self._on_lookup_ready, _iter) else: if not artist.casefold() in self._artists: _iter = self.model.insert_with_values( self.head_iters[group], -1, [0, 2, 4, 5, 9, 11, 13], [str(item.get_id()), artist, self._loading_icon, item, 2, category, composer]) self.cache.lookup(item, ArtSize.small, self._on_lookup_ready, _iter) self._artists[artist.casefold()] = {'iter': _iter, 'albums': []} self._artists[artist.casefold()]['albums'].append(item) if self.model.iter_n_children(self.head_iters[group]) == 1: path = self.model.get_path(self.head_iters[group]) path = self.filter_model.convert_child_path_to_path(path) self.view.get_generic_view().expand_row(path, False)
def _lookup_remote(self, item, callback, itr, art_size): """Lookup remote art Lookup remote art through Grilo and if found copy locally. Call _lookup_local to finish retrieving suitable art. """ album = utils.get_album_title(item) artist = utils.get_artist_name(item) @log def delete_cb(src, result, data): try: src.delete_finish(result) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) @log def splice_cb(src, result, data): tmp_file, iostream = data try: src.splice_finish(result) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) art_retrieved(False) return success, cache_path = MediaArt.get_path(artist, album, "album") try: # FIXME: I/O blocking MediaArt.file_to_jpeg(tmp_file.get_path(), cache_path) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) art_retrieved(False) return art_retrieved(True) tmp_file.delete_async(GLib.PRIORITY_LOW, None, delete_cb, None) @log def async_read_cb(src, result, data): try: istream = src.read_finish(result) except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) art_retrieved(False) return try: [tmp_file, iostream] = Gio.File.new_tmp() except Exception as err: logger.warn("Error: %s, %s", err.__class__, err) art_retrieved(False) return ostream = iostream.get_output_stream() # FIXME: Passing the iostream here, otherwise it gets # closed. PyGI specific issue? ostream.splice_async(istream, Gio.OutputStreamSpliceFlags.CLOSE_SOURCE | Gio.OutputStreamSpliceFlags.CLOSE_TARGET, GLib.PRIORITY_LOW, None, splice_cb, [tmp_file, iostream]) @log def album_art_for_item_cb(source, param, item, count, error): if error: logger.warn("Grilo error %s", error) art_retrieved(False) return thumb_uri = item.get_thumbnail() if thumb_uri is None: art_retrieved(False) return src = Gio.File.new_for_uri(thumb_uri) src.read_async(GLib.PRIORITY_LOW, None, async_read_cb, None) @log def art_retrieved(result): if not result: if artist not in self.blacklist: self.blacklist[artist] = [] album_stripped = MediaArt.strip_invalid_entities(album) self.blacklist[artist].append(album_stripped) self.lookup(item, art_size, callback, itr) grilo.get_album_art_for_item(item, album_art_for_item_cb)