def _moveid(self, cmd_args): """ Move id in playlist @syntax move track_id destination @param args as str @return msg as str """ try: tracks_ids = Lp().playlists.get_tracks_ids(Type.MPD) arg = self._get_args(cmd_args) track_id = int(arg[0]) orig = tracks_ids.index(track_id) dst = int(arg[1]) del tracks_ids[orig] tracks_ids.insert(dst, track_id) Lp().playlists.clear(Type.MPD) tracks = [] for track_id in tracks_ids: tracks.append(Track(track_id)) Lp().player.set_user_playlist_by_id(Type.NONE) Lp().playlists.add_tracks(Type.MPD, tracks, False) except: pass return ""
def do_render(self, ctx, widget, background_area, cell_area, flags): if self.album == Type.NONE: return surface = Lp().art.get_album_artwork(Album(self.album), ArtSize.MEDIUM, widget.get_scale_factor()) width = surface.get_width() height = surface.get_height() # If cover smaller than wanted size, translate translate_x = cell_area.x translate_y = cell_area.y wanted = ArtSize.MEDIUM * widget.get_scale_factor() if width < wanted: translate_x += (wanted - width) / 2 if height < wanted: translate_y += (wanted - height) / 2 ctx.translate(translate_x, translate_y) ctx.new_sub_path() radius = 2 degrees = pi / 180 ctx.arc(width + 2 - radius, radius, radius - 0.5, -90 * degrees, 0 * degrees) ctx.arc(width + 2 - radius, height + 2 - radius, radius - 0.5, 0 * degrees, 90 * degrees) ctx.arc(radius, height + 2 - radius, radius - 0.5, 90 * degrees, 180 * degrees) ctx.arc(radius, radius, radius - 0.5, 180 * degrees, 270 * degrees) ctx.close_path() ctx.set_line_width(1) ctx.fill() ctx.set_source_surface(surface, 1, 1) ctx.paint()
def _download_albums_art(self): """ Download albums artwork (from queue) @thread safe """ self._in_albums_download = True sql = Lp().db.get_cursor() while self._albums_queue: album_id = self._albums_queue.pop() album = Lp().albums.get_name(album_id) artist = Lp().albums.get_artist_name(album_id) pixbuf = self._get_album_art_spotify(artist, album) if pixbuf is None: pixbuf = self._get_album_art_itunes(artist, album) if pixbuf is None: pixbuf = self._get_album_art_lastfm(artist, album) if pixbuf is None: continue try: Lp().art.save_album_artwork(pixbuf, album_id) Lp().art.clean_album_cache(Album(album_id)) GLib.idle_add(Lp().art.album_artwork_update, album_id) except Exception as e: print("ArtDownloader::_download_albums_art: %s" % e) self._in_albums_download = False sql.close()
def _get_album_art_lastfm(self, artist, album): """ Get album artwork from lastfm @param artist as string @param album as string @return pixbuf as GdkPixbuf.Pixbuf @tread safe """ pixbuf = None if Lp().lastfm is not None: try: last_album = Lp().lastfm.get_album(artist, album) url = last_album.get_cover_image(4) if url is not None: s = Gio.File.new_for_uri(url) (status, data, tag) = s.load_contents() if status: stream = Gio.MemoryInputStream.new_from_data(data, None) pixbuf = GdkPixbuf.Pixbuf.new_from_stream_at_scale( stream, ArtSize.MONSTER, ArtSize.MONSTER, False, None) except Exception as e: print("ArtDownloader::_get_album_art_lastfm: %s" % e) return pixbuf
def _sort_items(self, model, itera, iterb, data): """ Sort model """ if not self._updating: return False a_index = model.get_value(itera, 0) b_index = model.get_value(iterb, 0) # Static vs static if a_index < 0 and b_index < 0: return a_index < b_index # Static entries always on top elif b_index < 0: return True # Static entries always on top if a_index < 0: return False # String comparaison for non static else: if self._is_artists: a = Lp().artists.get_sortname(a_index) b = Lp().artists.get_sortname(b_index) else: a = model.get_value(itera, 1) b = model.get_value(iterb, 1) return a.lower() > b.lower()
def __update_metadata(self): if self.__get_status() == 'Stopped': self.__metadata = {} else: if Lp().player.current_track.id >= 0: track_id = Lp().player.current_track.id else: track_id = randint(10000000, 90000000) self.__metadata['mpris:trackid'] = self.__get_media_id(track_id) track_number = Lp().player.current_track.number if track_number is None: track_number = 1 self.__metadata['xesam:trackNumber'] = GLib.Variant('i', track_number) self.__metadata['xesam:title'] = GLib.Variant( 's', Lp().player.current_track.name) self.__metadata['xesam:album'] = GLib.Variant( 's', Lp().player.current_track.album.name) self.__metadata['xesam:artist'] = GLib.Variant( 'as', Lp().player.current_track.artists) self.__metadata['xesam:albumArtist'] = GLib.Variant( 'as', Lp().player.current_track.album_artists) self.__metadata['mpris:length'] = GLib.Variant( 'x', Lp().player.current_track.duration * 1000000) self.__metadata['xesam:genre'] = GLib.Variant( 'as', Lp().player.current_track.genres) self.__metadata['xesam:url'] = GLib.Variant( 's', Lp().player.current_track.uri) self.__metadata["xesam:userRating"] = GLib.Variant( 'd', Lp().player.current_track.get_popularity() / 5) if Lp().player.current_track.id == Type.RADIOS: cover_path = Lp().art.get_radio_cache_path( ", ".join(Lp().player.current_track.artists), ArtSize.MONSTER) elif Lp().player.current_track.id == Type.EXTERNALS: cover_path = "/tmp/lollypop_mpris.jpg" pixbuf = Lp().art.pixbuf_from_tags( GLib.filename_from_uri(Lp().player.current_track.uri)[0], ArtSize.MONSTER) if pixbuf is not None: pixbuf.savev(cover_path, "jpeg", ["quality"], ["90"]) else: cover_path = Lp().art.get_album_cache_path( Lp().player.current_track.album, ArtSize.MONSTER) if cover_path is not None: self.__metadata['mpris:artUrl'] = GLib.Variant( 's', "file://" + cover_path) elif 'mpris:artUrl' in self.__metadata: self.__metadata['mpris:artUrl'] = GLib.Variant('s', '')
def set_party(self, party): """ Set party mode on if party is True Play a new random track if not already playing @param party as bool """ if party == self._is_party: return self._is_party = party self.reset_history() self.context.genre_ids = {} if self.plugins1.rgvolume is not None and\ self.plugins2.rgvolume is not None: if party: self.context.next = NextContext.NONE self.plugins1.rgvolume.props.album_mode = 0 self.plugins2.rgvolume.props.album_mode = 0 else: self.plugins1.rgvolume.props.album_mode = 1 self.plugins2.rgvolume.props.album_mode = 1 if party: self._external_tracks = [] self.context.genre_ids = {} self.context.track_id = None party_ids = self.get_party_ids() if party_ids: self._albums = Lp().albums.get_party_ids(party_ids) else: self._albums = Lp().albums.get_ids() # We do not store genre_ids for ALL/POPULARS/... genre_ids = [] for genre_id in party_ids: if genre_id > 0: genre_ids.append(genre_id) # Set context for each album for album_id in self._albums: self.context.genre_ids[album_id] = genre_ids self.context.artist_ids[album_id] = [] # Start a new song if not playing if (self.current_track.id in [None, Type.RADIOS])\ and self._albums: track_id = self._get_random() self.load(Track(track_id)) elif not self.is_playing(): self.play() else: # We need to put some context, take first available genre if self.current_track.id: self.set_albums(self.current_track.id, [self.current_track.album_artist_id], []) self.emit('party-changed', party)
def get_party_ids(self): """ Return party ids @return [ids as int] """ party_settings = Lp().settings.get_value("party-ids") ids = [] genre_ids = Lp().genres.get_ids() genre_ids.append(Type.POPULARS) genre_ids.append(Type.RECENTS) for setting in party_settings: if isinstance(setting, int) and setting in genre_ids: ids.append(setting) return ids
def __init__(self): """ Init popover """ Gtk.Popover.__init__(self) party_grid = Gtk.Grid() party_grid.set_property('margin-start', 10) party_grid.set_property('margin-end', 10) party_grid.set_property('margin-bottom', 5) party_grid.set_property('margin-top', 5) party_grid.set_column_spacing(10) party_grid.set_row_spacing(7) party_grid.show() scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled.add(party_grid) scrolled.show() self.add(scrolled) size = Lp().window.get_size() self.set_size_request(-1, size[1]*0.6) genres = Lp().genres.get() genres.insert(0, (Type.POPULARS, _("Populars"))) genres.insert(1, (Type.RECENTS, _("Recently added"))) ids = Lp().player.get_party_ids() i = 0 x = 0 for genre_id, genre in genres: label = Gtk.Label() label.set_property('halign', Gtk.Align.START) label.set_ellipsize(Pango.EllipsizeMode.END) label.set_text(genre) label.set_tooltip_text(genre) label.show() switch = Gtk.Switch() if genre_id in ids: switch.set_state(True) switch.connect("state-set", self._on_switch_state_set, genre_id) switch.show() party_grid.attach(label, x, i, 1, 1) party_grid.attach(switch, x+1, i, 1, 1) if x == 0: x += 2 else: label.set_property('margin-start', 15) i += 1 x = 0
def _add_artist(self, scanner, artist_id, album_id): """ Add artist to artist list @param scanner as CollectionScanner @param artist id as int @param album id as int """ artist_name = Lp().artists.get_name(artist_id) if self._show_genres: genre_ids = Lp().albums.get_genre_ids(album_id) genre_ids.append(Type.ALL) if self._list_one.get_selected_id() in genre_ids: self._list_two.add_value((artist_id, artist_name)) else: self._list_one.add_value((artist_id, artist_name))
def update_cover(self): """ Update cover for album id id needed """ if self._cover is None: return surface = Lp().art.get_album_artwork( self._album, ArtSize.BIG * self._cover.get_scale_factor()) self._cover.set_from_surface(surface) if surface.get_height() > surface.get_width(): self._overlay_orientation = Gtk.Orientation.VERTICAL else: self._overlay_orientation = Gtk.Orientation.HORIZONTAL del surface
def _test_lastfm_connection(self): """ Test lastfm connection @thread safe """ try: u = Lp().lastfm.get_authenticated_user() u.get_id() GLib.idle_add(self._test_img.set_from_icon_name, 'object-select-symbolic', Gtk.IconSize.MENU) except: GLib.idle_add(self._test_img.set_from_icon_name, 'computer-fail-symbolic', Gtk.IconSize.MENU)
def _update_metadata(self): if self._get_status() == 'Stopped': self._metadata = {} else: if Lp().player.current_track.id >= 0: self._metadata['mpris:trackid'] = dbus.ObjectPath( '/org/lollypop/%s' % Lp().player.current_track.id) else: # MPRIS SUX track_id = randint(10000000, 90000000) self._metadata['mpris:trackid'] = dbus.ObjectPath( '/org/lollypop/%s' % track_id) track_number = Lp().player.current_track.number if track_number is None: track_number = 1 self._metadata['xesam:trackNumber'] = track_number self._metadata['xesam:title'] = Lp().player.current_track.name self._metadata['xesam:album'] = Lp( ).player.current_track.album.name self._metadata['xesam:artist'] = Lp().player.current_track.artists self._metadata['xesam:albumArtist'] = \ ", ".join(Lp().player.current_track.album_artists) self._metadata['mpris:length'] = dbus.Int64( Lp().player.current_track.duration * 1000000) self._metadata['xesam:genre'] = Lp().player.current_track.genres\ or "Web" self._metadata['xesam:url'] = Lp().player.current_track.uri self._metadata["xesam:userRating"] = \ Lp().player.current_track.get_popularity() / 5 if Lp().player.current_track.id == Type.RADIOS: cover_path = Lp().art.get_radio_cache_path( ", ".join(Lp().player.current_track.artists), ArtSize.MONSTER) elif Lp().player.current_track.id == Type.EXTERNALS: cover_path = "/tmp/lollypop_mpris.jpg" pixbuf = Lp().art.pixbuf_from_tags( GLib.filename_from_uri(Lp().player.current_track.uri)[0], ArtSize.MONSTER) if pixbuf is not None: pixbuf.savev(cover_path, "jpeg", ["quality"], ["90"]) else: cover_path = Lp().art.get_album_cache_path( Lp().player.current_track.album, ArtSize.MONSTER) if cover_path is not None: self._metadata['mpris:artUrl'] = "file://" + cover_path elif 'mpris:artUrl' in self._metadata: self._metadata['mpris:artUrl'] = ''
def set_cover(self): """ Set cover for album if state changed """ if self._cover is None: return surface = Lp().art.get_album_artwork( self._album, ArtSize.BIG * self._cover.get_scale_factor()) self._cover.set_from_surface(surface) self._cover.set_size_request(100, 100) if surface.get_height() > surface.get_width(): self._overlay_orientation = Gtk.Orientation.VERTICAL else: self._overlay_orientation = Gtk.Orientation.HORIZONTAL del surface
class Disc: """ Represent an album disc """ def __init__(self, album, disc_number): self.db = Lp().albums self.album = album self.number = disc_number self._track_ids = [] @property def track_ids(self): """ Get all tracks ids of the disc @return list of int """ if not self._track_ids: self._track_ids = self.db.get_disc_tracks(self.album.id, self.album.genre_ids, self.album.artist_ids, self.number) return self._track_ids @property def tracks(self): """ Get all tracks of the disc @return list of Track """ return [Track(id) for id in self.track_ids]
class Disc: """ Represent an album disc """ def __init__(self, album, disc_number): self.db = Lp().albums self.album = album self.number = disc_number self._track_ids = [] @property def name(self): """ Disc name @return disc name as str """ @property def track_ids(self): """ Get all tracks ids of the disc @return list of int """ if not self._track_ids: self._track_ids = self.db.get_disc_tracks(self.album.id, self.album.genre_ids, self.album.artist_ids, self.number) # If user tagged track with an artist not present in album if not self._track_ids: print("%s missing an album artist in artists" % self.album.name) self._track_ids = self.db.get_disc_tracks(self.album.id, self.album.genre_ids, [], self.number) return self._track_ids @property def tracks(self): """ Get all tracks of the disc @return list of Track """ return [Track(id) for id in self.track_ids]
def _on_map_wikia(self, widget, force=False): """ Load on map @param widget as Gtk.Viewport @param force as bool """ self._menu.hide() if self._current is None: self._current = self._get_current() artist = Lp().artists.get_name(self._current[0]) title = Lp().tracks.get_name(self._current[2]) Lp().settings.set_value('infoswitch', GLib.Variant('s', 'wikia')) url = "http://lyrics.wikia.com/wiki/%s:%s" % (artist.replace(' ', '_'), title.replace(' ', '_')) # Delayed load due to WebKit memory loading GLib.timeout_add(250, self._load_web, widget, url, True, True)
def _party_switch_state(self, widget, state, genre_id): """ Update party ids when use change a switch in dialog @param widget as unused, state as widget state, genre id as int """ ids = Lp().player.get_party_ids() if state: try: ids.append(genre_id) except: pass else: try: ids.remove(genre_id) except: pass Lp().settings.set_value('party-ids', GLib.Variant('ai', ids))
def _on_current_changed(self, player): """ Update notification with track_id infos @param player Player """ if player.current_track.title == '': return state = Lp().window.get_window().get_state() app = Lp().window.get_application() if player.current_track.id is None or\ state & Gdk.WindowState.FOCUSED or\ app.is_fullscreen(): return if player.current_track.id == Type.RADIOS: cover_path = Lp().art.get_radio_cache_path( player.current_track.artist, ArtSize.BIG) else: cover_path = Lp().art.get_album_cache_path( player.current_track.album, ArtSize.BIG) if cover_path is not None: self._notification.set_hint('image-path', GLib.Variant('s', cover_path)) else: self._notification.set_hint('image-path', GLib.Variant('s', '')) if player.current_track.album.name == '': self._notification.update( player.current_track.title, # TRANSLATORS: by refers to the artist, _("by %s") % '<b>' + player.current_track.artist + '</b>', 'lollypop') else: self._notification.update( player.current_track.title, # TRANSLATORS: by refers to the artist, # from to the album _("by %s, from %s") % ('<b>' + player.current_track.artist + '</b>', '<i>' + player.current_track.album.name + '</i>'), 'lollypop') try: self._notification.show() except: pass
def set_party_ids(self): """ Set party mode ids """ party_ids = self.get_party_ids() if party_ids: self._albums = Lp().albums.get_party_ids(party_ids) else: self._albums = Lp().albums.get_ids() # We do not store genre_ids for ALL/POPULARS/... genre_ids = [] for genre_id in party_ids: if genre_id > 0: genre_ids.append(genre_id) # Set context for each album for album_id in self._albums: self._context.genre_ids[album_id] = genre_ids self._context.artist_ids[album_id] = []
def set_albums(self, track_id, artist_ids, genre_ids): """ Set album list (for next/prev) @param track id as int @param artist id as int @param genre id as int """ # Invalid track if track_id is None: return album = Track(track_id).album self._albums = [] ShufflePlayer.reset_history(self) # We are not playing a user playlist anymore self._user_playlist = [] self._user_playlist_id = None # We are in all artists if (genre_ids and genre_ids[0] == Type.ALL) or\ (artist_ids and artist_ids[0] == Type.ALL): self._albums = Lp().albums.get_compilations() self._albums += Lp().albums.get_ids() # We are in populars view, add popular albums elif genre_ids and genre_ids[0] == Type.POPULARS: if self._shuffle in [Shuffle.TRACKS_ARTIST, Shuffle.ALBUMS_ARTIST]: self._albums = [] self.next_track = Track() for album_id in Lp().albums.get_populars(): if Lp().albums.get_artist_id(album_id) == \ album.artist_id: self._albums.append(album_id) else: self._albums = Lp().albums.get_populars() # We are in recents view, add recent albums elif genre_ids and genre_ids[0] == Type.RECENTS: self._albums = Lp().albums.get_recents() # We are in randoms view, add random albums elif genre_ids and genre_ids[0] == Type.RANDOMS: self._albums = Lp().albums.get_cached_randoms() # We are in compilation view without genre elif genre_ids and genre_ids[0] == Type.COMPILATIONS: self._albums = Lp().albums.get_compilations() # Random tracks/albums for artist elif self._shuffle in [Shuffle.TRACKS_ARTIST, Shuffle.ALBUMS_ARTIST]: self._albums = Lp().albums.get_ids([album.artist_id], genre_ids) # Add all albums for genre else: if not artist_ids: self._albums = Lp().albums.get_compilations(genre_ids) self._albums += Lp().albums.get_ids(artist_ids, genre_ids) album.set_genre(genre_ids) if track_id in album.tracks_ids: self.context.artist_ids = artist_ids self.context.genre_ids = genre_ids # Shuffle album list if needed self._shuffle_albums() else: # Error self.stop()
def _populate(self): """ Same as _populate_threaded() @thread safe """ self._urls = Lp().art.get_duck_arts(self._name+"+logo+radio") if self._urls: self._add_pixbufs() else: GLib.idle_add(self._show_not_found)
def _get_album_art_lastfm(self, artist, album): """ Get album artwork from lastfm @param artist as string @param album as string @return data as bytes @tread safe """ image = None if Lp().lastfm is not None: try: last_album = Lp().lastfm.get_album(artist, album) url = last_album.get_cover_image(4) if url is not None: s = Gio.File.new_for_uri(url) (status, image, tag) = s.load_contents() except Exception as e: print("ArtDownloader::_get_album_art_lastfm: %s" % e) return image
def _populate(self): """ Same as _populate_threaded() @thread safe """ self._urls = Lp().art.get_google_arts(self._name + "+logo+radio", self._start) if self._urls: self._start += GOOGLE_INC self._add_pixbufs() else: GLib.idle_add(self._show_not_found)
def set_party(self, party): """ Set party mode on if party is True Play a new random track if not already playing @param party as bool """ self.reset_history() if self._rgvolume is not None: if party: self.context.next = NextContext.NONE self._rgvolume.props.album_mode = 0 else: self._rgvolume.props.album_mode = 1 self._is_party = party if party: self._external_tracks = [] self.context.genre_ids = [] self.context.track_id = None party_ids = self.get_party_ids() if party_ids: self._albums = Lp().albums.get_party_ids(party_ids) else: self._albums = Lp().albums.get_ids() # Start a new song if not playing if (self.current_track.id in [None, Type.RADIOS])\ and self._albums: track_id = self._get_random() self.load(Track(track_id)) elif not self.is_playing(): self.play() else: # We need to put some context, take first available genre if self.current_track.id: self.set_albums(self.current_track.id, [self.current_track.album_artist_id], []) self.emit('party-changed', party) Lp().window.update_view()
def do_render(self, ctx, widget, background_area, cell_area, flags): size = ArtSize.MEDIUM * widget.get_scale_factor() surface = Lp().art.get_album_artwork(Album(self.album), size) width = surface.get_width() height = surface.get_height() ctx.translate(cell_area.x, cell_area.y) ctx.new_sub_path() radius = 2 degrees = pi / 180 ctx.arc(width + 2 - radius, radius, radius - 0.5, -90 * degrees, 0 * degrees) ctx.arc(width + 2 - radius, height + 2 - radius, radius - 0.5, 0 * degrees, 90 * degrees) ctx.arc(radius, height + 2 - radius, radius - 0.5, 90 * degrees, 180 * degrees) ctx.arc(radius, radius, radius - 0.5, 180 * degrees, 270 * degrees) ctx.close_path() ctx.set_line_width(1) ctx.fill() ctx.set_source_surface(surface, 1, 1) ctx.paint()
def _string_for_track_id(self, track_id, index=Type.NONE): """ Get mpd protocol string for track id @param track id as int @param track index as int @return str """ if track_id is None: msg = "" else: track = Track(track_id) if index == Type.NONE: index = 1 if Lp().player.is_party(): tracks_ids = [Lp().player.prev_track.id, Lp().player.current_track.id, Lp().player.next_track.id] index = tracks_ids.index(track_id) else: tracks_ids = Lp().playlists.get_tracks_ids(Type.MPD) try: index = tracks_ids.index(track_id) + 1 except: pass msg = "file: %s\nArtist: %s\nAlbum: %s\nAlbumArtist: %s\ \nTitle: %s\nDate: %s\nGenre: %s\nTime: %s\nId: %s\nPos: %s\nTrack: %s\n" % ( track.path, track.artist, track.album.name, track.album_artist, track.name, track.album.year, track.genre, track.duration, track.id, index, index) return msg
def __on_current_changed(self, player): """ Send notification with track_id infos @param player Player """ if player.current_track.title == '' or self.__inhibitor: self.__inhibitor = False return state = Lp().window.get_window().get_state() app = Lp().window.get_application() if player.current_track.id is None or\ state & Gdk.WindowState.FOCUSED or\ app.is_fullscreen(): return if player.current_track.id == Type.RADIOS: cover_path = Lp().art.get_radio_cache_path( player.current_track.album_artists[0], ArtSize.BIG) else: cover_path = Lp().art.get_album_cache_path( player.current_track.album, ArtSize.BIG) if cover_path is None: cover_path = 'org.gnome.Lollypop' if player.current_track.album.name == '': self.__notification.show_new( player.current_track.title, # TRANSLATORS: by refers to the artist, _("by %s") % '<b>' + ", ".join(player.current_track.artists) + '</b>', cover_path) else: self.__notification.show_new( player.current_track.title, # TRANSLATORS: by refers to the artist, # from to the album _("by %s, from %s") % ('<b>' + ", ".join(player.current_track.artists) + '</b>', '<i>' + player.current_track.album.name + '</i>'), cover_path)
def _move(self, cmd_args): """ Move range in playlist @syntax move position destination @param args as str @return msg as str """ # TODO implement range tracks_ids = Lp().playlists.get_tracks_ids(Type.MPD) arg = self._get_args(cmd_args) orig = int(arg[0]) dst = int(arg[1]) if orig != dst: track_id = tracks_ids[orig] del tracks_ids[orig] tracks_ids.insert(dst, track_id) Lp().playlists.clear(Type.MPD, False) tracks = [] for track_id in tracks_ids: tracks.append(Track(track_id)) Lp().player.set_user_playlist_by_id(Type.NONE) Lp().playlists.add_tracks(Type.MPD, tracks, False) return ""
def exists_in_db(self): """ Search if item exists in db @return bool """ artist_ids = [] for artist in self.artists: artist_id = Lp().artists.get_id(artist) artist_ids.append(artist_id) if self.is_track: for track_id in Lp().tracks.get_ids_for_name(self.name): db_artist_ids = Lp().tracks.get_artist_ids(track_id) union = list(set(artist_ids) & set(db_artist_ids)) if union == db_artist_ids: return True else: album_ids = Lp().albums.get_ids(artist_ids, []) album_ids += Lp().albums.get_ids(artist_ids, [Type.CHARTS]) for album_id in album_ids: album_name = Lp().albums.get_name(album_id) if album_name.lower() == self.album_name.lower(): return True return False
def __add2db(self, uri, mtime): """ Add new file to db with information @param uri as string @param mtime as int @return track id as int """ f = Lio.File.new_for_uri(uri) debug("CollectionScanner::add2db(): Read tags") info = self.get_info(uri) tags = info.get_tags() name = f.get_basename() title = self.get_title(tags, name) artists = self.get_artists(tags) composers = self.get_composers(tags) performers = self.get_performers(tags) a_sortnames = self.get_artist_sortnames(tags) aa_sortnames = self.get_album_artist_sortnames(tags) album_artists = self.get_album_artist(tags) album_name = self.get_album_name(tags) genres = self.get_genres(tags) discnumber = self.get_discnumber(tags) discname = self.get_discname(tags) tracknumber = self.get_tracknumber(tags, name) year = self.get_original_year(tags) if year is None: year = self.get_year(tags) duration = int(info.get_duration()/1000000000) # If no artists tag, use album artist if artists == "": artists = album_artists # if artists is always null, no album artists too, # use composer/performer if artists == "": artists = performers album_artists = composers if artists == "": artists = album_artists if artists == "": artists = _("Unknown") debug("CollectionScanner::add2db(): Restore stats") # Restore stats (track_pop, track_rate, track_ltime, album_mtime, loved, album_pop, album_rate) = self.__history.get(name, duration) # If nothing in stats, use track mtime if album_mtime == 0: album_mtime = mtime debug("CollectionScanner::add2db(): Add artists %s" % artists) artist_ids = self.add_artists(artists, album_artists, a_sortnames) debug("CollectionScanner::add2db(): " "Add album artists %s" % album_artists) album_artist_ids = self.add_album_artists(album_artists, aa_sortnames) new_artist_ids = list(set(album_artist_ids) | set(artist_ids)) debug("CollectionScanner::add2db(): Add album: " "%s, %s" % (album_name, album_artist_ids)) (album_id, new_album) = self.add_album(album_name, album_artist_ids, uri, loved, album_pop, album_rate, False) genre_ids = self.add_genres(genres) # Add track to db debug("CollectionScanner::add2db(): Add track") track_id = Lp().tracks.add(title, uri, duration, tracknumber, discnumber, discname, album_id, year, track_pop, track_rate, track_ltime) debug("CollectionScanner::add2db(): Update tracks") self.update_track(track_id, artist_ids, genre_ids, mtime) self.update_album(album_id, album_artist_ids, genre_ids, album_mtime, year) if new_album: with SqlCursor(Lp().db) as sql: sql.commit() for genre_id in genre_ids: GLib.idle_add(self.emit, "genre-updated", genre_id, True) for artist_id in new_artist_ids: GLib.idle_add(self.emit, "artist-updated", artist_id, True) return track_id
def __copy_to_device(self, playlists): """ Copy file from playlist to device @param playlists as [str] """ for playlist in playlists: m3u = None stream = None if playlist != Type.NONE: try: playlist_name = Lp().playlists.get_name(playlist) # Create playlist m3u = Gio.File.new_for_path("/tmp/lollypop_%s.m3u" % (playlist_name, )) self.__retry( m3u.replace_contents, (b'#EXTM3U\n', None, False, Gio.FileCreateFlags.REPLACE_DESTINATION, None)) stream = m3u.open_readwrite(None) except Exception as e: print("DeviceWidget::_copy_to_device(): %s" % e) # Get tracks if playlist == Type.NONE: track_ids = [] album_ids = Lp().albums.get_synced_ids() for album_id in album_ids: track_ids += Lp().albums.get_track_ids(album_id) else: track_ids = Lp().playlists.get_track_ids(playlist) # Start copying for track_id in track_ids: if track_id is None: continue if not self._syncing: self._fraction = 1.0 self.__in_thread = False return track = Track(track_id) if not track.uri.startswith('file:'): continue album_name = escape(track.album_name.lower()) is_compilation = track.album.artist_ids[0] == Type.COMPILATIONS if is_compilation: on_device_album_uri = "%s/%s" %\ (self._uri, album_name) else: artists = escape(", ".join(track.album.artists).lower()) on_device_album_uri = "%s/%s_%s" %\ (self._uri, artists, album_name) d = Gio.File.new_for_uri(on_device_album_uri) if not d.query_exists(None): self.__retry(d.make_directory_with_parents, (None, )) # Copy album art art = Lp().art.get_album_artwork_path(track.album) if art is not None: src_art = Gio.File.new_for_path(art) art_uri = "%s/cover.jpg" % on_device_album_uri self.__copied_art_uris.append(art_uri) dst_art = Gio.File.new_for_uri(art_uri) if not dst_art.query_exists(None): self.__retry( src_art.copy, (dst_art, Gio.FileCopyFlags.OVERWRITE, None, None)) filepath = GLib.filename_from_uri(track.uri)[0] track_name = escape(GLib.path_get_basename(filepath)) # Check extension, if not mp3, convert ext = os.path.splitext(filepath)[1] if (ext != ".mp3" or self.__normalize) and self.__convert: convertion_needed = True track_name = track_name.replace(ext, ".mp3") else: convertion_needed = False src_track = Gio.File.new_for_uri(track.uri) info = src_track.query_info('time::modified', Gio.FileQueryInfoFlags.NONE, None) # Prefix track with mtime to make sure updating it later mtime = info.get_attribute_as_string('time::modified') dst_uri = "%s/%s_%s" % (on_device_album_uri, mtime, track_name) if stream is not None: if is_compilation: line = "%s/%s_%s\n" %\ (album_name, mtime, track_name) else: line = "%s_%s/%s_%s\n" %\ (artists, album_name, mtime, track_name) self.__retry(stream.get_output_stream().write, (line.encode(encoding='UTF-8'), None)) dst_track = Gio.File.new_for_uri(dst_uri) if not dst_track.query_exists(None): if convertion_needed: mp3_uri = "file:///tmp/%s" % track_name mp3_file = Gio.File.new_for_uri(mp3_uri) pipeline = self.__convert_to_mp3(src_track, mp3_file) # Check if encoding is finished if pipeline is not None: bus = pipeline.get_bus() bus.add_signal_watch() bus.connect('message::eos', self.__on_bus_eos) self.__encoding = True while self.__encoding and self._syncing: sleep(1) bus.disconnect_by_func(self.__on_bus_eos) pipeline.set_state(Gst.State.PAUSED) pipeline.set_state(Gst.State.READY) pipeline.set_state(Gst.State.NULL) self.__retry( mp3_file.move, (dst_track, Gio.FileCopyFlags.OVERWRITE, None, None)) # To be sure try: mp3_file.delete(None) except: pass else: self.__retry(src_track.copy, (dst_track, Gio.FileCopyFlags.OVERWRITE, None, None)) else: self.__done += 1 self.__done += 1 self._fraction = self.__done / self.__total if stream is not None: stream.close() if m3u is not None: playlist_name = escape(playlist_name) dst = Gio.File.new_for_uri(self._uri + '/' + playlist_name + '.m3u') self.__retry(m3u.move, (dst, Gio.FileCopyFlags.OVERWRITE, None, None))
def _on_action_press_event(self, widget, event): """ Append album to current list if not present Remove it if present @param: widget as Gtk.EventBox @param: event as Gdk.Event """ if Lp().player.locked: return True if Lp().player.has_album(self._album): if Lp().player.current_track.album.id == self._album.id: # If not last album, skip it if len(Lp().player.get_albums()) > 1: Lp().player.skip_album() Lp().player.remove_album(self._album) # remove it and stop playback by going to next track else: Lp().player.remove_album(self._album) Lp().player.set_next() Lp().player.next() else: Lp().player.remove_album(self._album) self._show_append(True) else: if Lp().player.is_playing() and not Lp().player.get_albums(): Lp().player.play_album(self._album) else: Lp().player.add_album(self._album) self._show_append(False) return True
def _on_next_btn_clicked(self, button): """ Next track on next button clicked @param button as Gtk.Button """ Lp().player.next()
def __on_volume_changed(self, player, data=None): self.PropertiesChanged(self.__MPRIS_PLAYER_IFACE, {'Volume': GLib.Variant('d', Lp().player.volume), }, [])
def __disable_react_to_playback(self): if self.__status_handler_id is not None: Lp().player.disconnect(self.__status_handler_id)
def Next(self): if Lp().notify is not None: Lp().notify.inhibit() Lp().player.next()
def __scan(self, uris): """ Scan music collection for music files @param uris as [string], uris to scan @thread safe """ if self.__history is None: self.__history = History() mtimes = Lp().tracks.get_mtimes() (new_tracks, new_dirs, ignore_dirs) = self.__get_objects_for_uris( uris) orig_tracks = Lp().tracks.get_uris(ignore_dirs) was_empty = len(orig_tracks) == 0 if ignore_dirs: if Lp().notify is not None: Lp().notify.send(_("Lollypop is detecting an empty folder."), _("Check your music settings.")) count = len(new_tracks) + len(orig_tracks) # Add monitors on dirs if self.__inotify is not None: for d in new_dirs: if d.startswith("file://"): self.__inotify.add_monitor(d) with SqlCursor(Lp().db) as sql: i = 0 # Look for new files/modified files try: to_add = [] for uri in new_tracks: if self.__thread is None: return try: GLib.idle_add(self.__update_progress, i, count) f = Lio.File.new_for_uri(uri) info = f.query_info("time::modified", Gio.FileQueryInfoFlags.NONE, None) mtime = int(info.get_attribute_as_string( "time::modified")) # If songs exists and mtime unchanged, continue, # else rescan if uri in orig_tracks: orig_tracks.remove(uri) i += 1 if mtime <= mtimes.get(uri, mtime + 1): i += 1 continue else: self.__del_from_db(uri) # On first scan, use modification time # Else, use current time if not was_empty: mtime = int(time()) to_add.append((uri, mtime)) except Exception as e: print("CollectionScanner::__scan(mtime):", e) # Clean deleted files # Now because we need to populate history for uri in orig_tracks: i += 1 GLib.idle_add(self.__update_progress, i, count) self.__del_from_db(uri) # Add files to db for (uri, mtime) in to_add: try: debug("Adding file: %s" % uri) i += 1 GLib.idle_add(self.__update_progress, i, count) self.__add2db(uri, mtime) except Exception as e: print("CollectionScanner::__scan(add):", e, uri) sql.commit() except Exception as e: print("CollectionScanner::__scan():", e) GLib.idle_add(self.__finish) del self.__history self.__history = None
def Pause(self): Lp().player.pause()
def __update_progress(self, current, total): """ Update progress bar status @param scanned items as int, total items as int """ Lp().window.progress.set_fraction(current / total, self)
def load(): artists = Lp().artists.get(genre_ids) compilations = Lp().albums.get_compilation_ids(genre_ids) return (artists, compilations)
def do(self, search_items): """ Return tracks containing name @param search items as [str] @return tracks as [SearchItem] """ self.__stop = False # Local search added_album_ids = [] added_track_ids = [] for item in search_items: if self.__stop: return albums = [] tracks_non_album_artist = [] # Get all albums for all artists and non album_artist tracks for artist_id in Lp().artists.search(item): if self.__stop: return for album_id in Lp().albums.get_ids([artist_id], []): if (album_id, artist_id) not in albums: albums.append((album_id, artist_id)) for track_id, track_name in Lp( ).tracks.get_as_non_album_artist(artist_id): tracks_non_album_artist.append((track_id, track_name)) for album_id, artist_id in albums: if self.__stop: return if album_id in added_album_ids: continue search_item = SearchItem() search_item.id = album_id added_album_ids.append(album_id) search_item.is_track = False search_item.artist_ids = [artist_id] search_item.year = Lp().albums.get_year(album_id) self._items.append(search_item) GLib.idle_add(self.emit, 'item-found') try: year = int(item) albums = Lp().albums.get_by_year(year) except: albums = [] albums += Lp().albums.search(item) for album_id in albums: if self.__stop: return if album_id in added_album_ids: continue search_item = SearchItem() search_item.id = album_id added_album_ids.append(album_id) search_item.is_track = False search_item.artist_ids = Lp().albums.get_artist_ids(album_id) search_item.year = Lp().albums.get_year(album_id) self._items.append(search_item) GLib.idle_add(self.emit, 'item-found') for track_id, track_name in Lp().tracks.search( item) + tracks_non_album_artist: if self.__stop: return if track_id in added_track_ids: continue search_item = SearchItem() search_item.id = track_id added_track_ids.append(track_id) search_item.is_track = True search_item.artist_ids = Lp().tracks.get_artist_ids(track_id) self._items.append(search_item) GLib.idle_add(self.emit, 'item-found') self._finished = True GLib.idle_add(self.emit, 'item-found')
def __init__(self, parent): """ Init widget @param device as Device @param parent as Gtk.Widget """ Gtk.Bin.__init__(self) MtpSync.__init__(self) self.__parent = parent self.__stop = False self._uri = None builder = Gtk.Builder() builder.add_from_resource('/org/gnome/Lollypop/DeviceManagerWidget.ui') widget = builder.get_object('widget') self.__error_label = builder.get_object('error-label') self.__switch_albums = builder.get_object('switch_albums') self.__switch_albums.set_state(Lp().settings.get_value('sync-albums')) self.__switch_mp3 = builder.get_object('switch_mp3') self.__switch_normalize = builder.get_object('switch_normalize') if not self._check_encoder_status(): self.__switch_mp3.set_sensitive(False) self.__switch_normalize.set_sensitive(False) self.__switch_mp3.set_tooltip_text( _("You need to install " + "gstreamer-plugins-ugly")) else: self.__switch_mp3.set_state(Lp().settings.get_value('convert-mp3')) self.__menu_items = builder.get_object('menu-items') self.__menu = builder.get_object('menu') self.__model = Gtk.ListStore(bool, str, int) self.__selection_list = SelectionList(False) self.__selection_list.connect('item-selected', self.__on_item_selected) widget.attach(self.__selection_list, 1, 1, 1, 1) self.__selection_list.set_hexpand(True) self.__view = builder.get_object('view') self.__view.set_model(self.__model) builder.connect_signals(self) self.add(widget) self.__infobar = builder.get_object('infobar') self.__infobar_label = builder.get_object('infobarlabel') renderer0 = Gtk.CellRendererToggle() renderer0.set_property('activatable', True) renderer0.connect('toggled', self.__on_item_toggled) column0 = Gtk.TreeViewColumn(" ✓", renderer0, active=0) column0.set_clickable(True) column0.connect('clicked', self.__on_column0_clicked) renderer1 = CellRendererAlbum() self.__column1 = Gtk.TreeViewColumn("", renderer1, album=2) renderer2 = Gtk.CellRendererText() renderer2.set_property('ellipsize-set', True) renderer2.set_property('ellipsize', Pango.EllipsizeMode.END) self.__column2 = Gtk.TreeViewColumn("", renderer2, markup=1) self.__column2.set_expand(True) self.__view.append_column(column0) self.__view.append_column(self.__column1) self.__view.append_column(self.__column2)
def __remove_from_device(self, playlists): """ Delete files not available in playlist """ track_uris = [] track_ids = [] # Get tracks if playlists and playlists[0] == Type.NONE: track_ids = [] album_ids = Lp().albums.get_synced_ids() for album_id in album_ids: track_ids += Lp().albums.get_track_ids(album_id) else: for playlist in playlists: track_ids += Lp().playlists.get_track_ids(playlist) # Get tracks uris for track_id in track_ids: if not self._syncing: self._fraction = 1.0 self.__in_thread = False return track = Track(track_id) if not track.uri.startswith('file:'): continue album_name = escape(track.album_name.lower()) if track.album.artist_ids[0] == Type.COMPILATIONS: on_device_album_uri = "%s/%s" % (self._uri, album_name) else: artists = escape(", ".join(track.album.artists).lower()) on_device_album_uri = "%s/%s_%s" % (self._uri, artists, album_name) filepath = GLib.filename_from_uri(track.uri)[0] track_name = escape(GLib.path_get_basename(filepath)) # Check extension, if not mp3, convert ext = os.path.splitext(filepath)[1] if ext != ".mp3" and self.__convert: track_name = track_name.replace(ext, ".mp3") on_disk = Gio.File.new_for_path(filepath) info = on_disk.query_info('time::modified', Gio.FileQueryInfoFlags.NONE, None) # Prefix track with mtime to make sure updating it later mtime = info.get_attribute_as_string('time::modified') dst_uri = "%s/%s_%s" % (on_device_album_uri, mtime, track_name) track_uris.append(dst_uri) on_mtp_files = self.__get_track_files() # Delete file on device and not in playlists for uri in on_mtp_files: if not self._syncing: self._fraction = 1.0 self.__in_thread = False return if uri not in track_uris and uri not in self.__copied_art_uris: to_delete = Gio.File.new_for_uri(uri) self.__retry(to_delete.delete, (None, )) self.__done += 1 self._fraction = self.__done / self.__total
def Previous(self): if Lp().notify is not None: Lp().notify.inhibit() Lp().player.prev()
def __del_from_db(self, uri): """ Delete track from db @param uri as str """ try: f = Lio.File.new_for_uri(uri) name = f.get_basename() track_id = Lp().tracks.get_id_by_uri(uri) album_id = Lp().tracks.get_album_id(track_id) genre_ids = Lp().tracks.get_genre_ids(track_id) album_artist_ids = Lp().albums.get_artist_ids(album_id) artist_ids = Lp().tracks.get_artist_ids(track_id) popularity = Lp().tracks.get_popularity(track_id) rate = Lp().tracks.get_rate(track_id) ltime = Lp().tracks.get_ltime(track_id) mtime = Lp().albums.get_mtime(album_id) duration = Lp().tracks.get_duration(track_id) album_popularity = Lp().albums.get_popularity(album_id) album_rate = Lp().albums.get_rate(album_id) loved = Lp().albums.get_loved(album_id) uri = Lp().tracks.get_uri(track_id) self.__history.add(name, duration, popularity, rate, ltime, mtime, loved, album_popularity, album_rate) Lp().tracks.remove(track_id) Lp().tracks.clean(track_id) deleted = Lp().albums.clean(album_id) if deleted: with SqlCursor(Lp().db) as sql: sql.commit() GLib.idle_add(self.emit, "album-updated", album_id, True) for artist_id in album_artist_ids + artist_ids: Lp().artists.clean(artist_id) GLib.idle_add(self.emit, "artist-updated", artist_id, False) for genre_id in genre_ids: Lp().genres.clean(genre_id) GLib.idle_add(self.emit, "genre-updated", genre_id, False) except Exception as e: print("CollectionScanner::__del_from_db:", e)
def _on_volume_changed(self, player, data=None): self.PropertiesChanged(self.MPRIS_PLAYER_IFACE, {'Volume': dbus.Double( Lp().player.volume), }, [])
def populate(self): """ Populate widget content """ self.get_style_context().remove_class('loading') self.set_sensitive(True) self.set_property('has-tooltip', True) self.connect('query-tooltip', self.__on_query_tooltip) row_widget = Gtk.EventBox() row_widget.set_property('valign', Gtk.Align.CENTER) row_widget.set_margin_top(self.__MARGIN) row_widget.set_margin_end(self.__MARGIN) grid = Gtk.Grid() grid.set_column_spacing(8) if self.__album.artists: artists = escape(", ".join(self.__album.artists)) else: artists = _("Compilation") self.__artist_label = Gtk.Label.new("<b>%s</b>" % artists) self.__artist_label.set_use_markup(True) self.__artist_label.set_hexpand(True) self.__artist_label.set_property('halign', Gtk.Align.START) self.__artist_label.set_ellipsize(Pango.EllipsizeMode.END) self.__title_label = Gtk.Label.new(self.__album.name) self.__title_label.set_ellipsize(Pango.EllipsizeMode.END) cover = Gtk.Image() cover.get_style_context().add_class('small-cover-frame') surface = Lp().art.get_album_artwork(self.__album, ArtSize.MEDIUM, self.get_scale_factor()) cover.set_from_surface(surface) cover.set_size_request(ArtSize.MEDIUM, ArtSize.MEDIUM) del surface self.__play_indicator = Gtk.Image.new_from_icon_name( 'media-playback-start-symbolic', Gtk.IconSize.MENU) delete_button = Gtk.Button.new_from_icon_name('user-trash-symbolic', Gtk.IconSize.MENU) # Here a hack to make old Gtk version support min-height css attribute # min-height = 24px, borders = 2px delete_button.set_property('height-request', 26) delete_button.get_image().set_opacity(0.2) delete_button.set_relief(Gtk.ReliefStyle.NONE) delete_button.get_style_context().add_class('menu-button') delete_button.get_style_context().add_class('track-menu-button') delete_button.set_property('valign', Gtk.Align.CENTER) delete_button.connect('clicked', self.__on_delete_clicked) vgrid = Gtk.Grid() vgrid.set_column_spacing(5) vgrid.add(self.__play_indicator) vgrid.add(self.__title_label) grid.attach(self.__artist_label, 1, 0, 1, 1) grid.attach(delete_button, 2, 0, 1, 2) grid.attach(cover, 0, 0, 1, 2) grid.attach(vgrid, 1, 1, 1, 1) row_widget.add(grid) self.add(row_widget) self.get_style_context().add_class('trackrow') self.show_play_indicator( self.__album.id == Lp().player.current_track.album.id) self.show_all() self.drag_source_set(Gdk.ModifierType.BUTTON1_MASK, [], Gdk.DragAction.MOVE) self.drag_source_add_text_targets() self.drag_dest_set(Gtk.DestDefaults.DROP | Gtk.DestDefaults.MOTION, [], Gdk.DragAction.MOVE) self.drag_dest_add_text_targets() self.connect('drag-begin', self.__on_drag_begin) self.connect('drag-data-get', self.__on_drag_data_get) self.connect('drag-data-received', self.__on_drag_data_received) self.connect('drag-motion', self.__on_drag_motion) self.connect('drag-leave', self.__on_drag_leave)
def _update_metadata(self): if self._get_status() == 'Stopped': self._metadata = {} else: if Lp().player.current_track.id >= 0: self._metadata['mpris:trackid'] = dbus.ObjectPath( '/org/lollypop/%s' % Lp().player.current_track.id) else: # MPRIS SUX track_id = randint(10000000, 90000000) self._metadata['mpris:trackid'] = dbus.ObjectPath( '/org/lollypop/%s' % track_id) track_number = Lp().player.current_track.number if track_number is None: track_number = 1 self._metadata['xesam:trackNumber'] = track_number self._metadata['xesam:title'] = Lp().player.current_track.name self._metadata['xesam:album'] = Lp( ).player.current_track.album.name self._metadata['xesam:artist'] = Lp().player.current_track.artists self._metadata['xesam:albumArtist'] = \ ", ".join(Lp().player.current_track.album_artists) self._metadata['mpris:length'] = dbus.Int64( Lp().player.current_track.duration * 1000000) self._metadata['xesam:genre'] = Lp().player.current_track.genres\ or "Web" self._metadata['xesam:url'] = Lp().player.current_track.uri rate = Lp().player.current_track.get_rate() if rate == Type.NONE: rate = Lp().player.current_track.get_popularity() self._metadata["xesam:userRating"] = dbus.Double(rate / 5) if Lp().player.current_track.id == Type.RADIOS: cover_path = Lp().art.get_radio_cache_path( ", ".join(Lp().player.current_track.artists), ArtSize.MONSTER) elif Lp().player.current_track.id == Type.EXTERNALS: cover_path = "/tmp/lollypop_mpris.jpg" pixbuf = Lp().art.pixbuf_from_tags( GLib.filename_from_uri(Lp().player.current_track.uri)[0], ArtSize.MONSTER) if pixbuf is not None: pixbuf.savev(cover_path, "jpeg", ["quality"], ["90"]) else: cover_path = Lp().art.get_album_cache_path( Lp().player.current_track.album, ArtSize.MONSTER) if cover_path is not None: self._metadata['mpris:artUrl'] = "file://" + cover_path elif 'mpris:artUrl' in self._metadata: self._metadata['mpris:artUrl'] = ''
def load(): genres = Lp().genres.get() return genres
def Set(self, interface, property_name, new_value): if property_name == 'Volume': Lp().player.set_volume(new_value)
def load(): artists = Lp().artists.get_local() compilations = Lp().albums.get_compilation_ids() return (artists, compilations)
def _on_prev_btn_clicked(self, button): """ Previous track on prev button clicked @param button as Gtk.Button """ Lp().player.prev()
def SetPosition(self, track_id, position): Lp().player.seek(position/1000000)
def __update_metadata(self): if self.__get_status() == 'Stopped': self.__metadata = {} else: if Lp().player.current_track.id >= 0: track_id = Lp().player.current_track.id else: track_id = randint(10000000, 90000000) self.__metadata['mpris:trackid'] = self.__get_media_id(track_id) track_number = Lp().player.current_track.number if track_number is None: track_number = 1 self.__metadata['xesam:trackNumber'] = GLib.Variant('i', track_number) self.__metadata['xesam:title'] = GLib.Variant( 's', Lp().player.current_track.name) self.__metadata['xesam:album'] = GLib.Variant( 's', Lp().player.current_track.album.name) self.__metadata['xesam:artist'] = GLib.Variant( 'as', Lp().player.current_track.artists) self.__metadata['xesam:albumArtist'] = GLib.Variant( 'as', Lp().player.current_track.album_artists) self.__metadata['mpris:length'] = GLib.Variant( 'x', Lp().player.current_track.duration * 1000000) self.__metadata['xesam:genre'] = GLib.Variant( 'as', Lp().player.current_track.genres) self.__metadata['xesam:url'] = GLib.Variant( 's', Lp().player.current_track.uri) rate = Lp().player.current_track.get_rate() if rate == Type.NONE: rate = Lp().player.current_track.get_popularity() self.__metadata["xesam:userRating"] = GLib.Variant('d', rate / 5) if Lp().player.current_track.id == Type.RADIOS: cover_path = Lp().art.get_radio_cache_path( ", ".join(Lp().player.current_track.artists), ArtSize.MONSTER) elif Lp().player.current_track.id == Type.EXTERNALS: cover_path = "/tmp/lollypop_mpris.jpg" pixbuf = Lp().art.pixbuf_from_tags( GLib.filename_from_uri(Lp().player.current_track.uri)[0], ArtSize.MONSTER) if pixbuf is not None: pixbuf.savev(cover_path, "jpeg", ["quality"], ["90"]) else: cover_path = Lp().art.get_album_cache_path( Lp().player.current_track.album, ArtSize.MONSTER) if cover_path is not None: self.__metadata['mpris:artUrl'] = GLib.Variant( 's', "file://" + cover_path) elif 'mpris:artUrl' in self.__metadata: self.__metadata['mpris:artUrl'] = GLib.Variant('s', '')
def Stop(self): Lp().player.stop()
def PlayPause(self): Lp().player.play_pause()
def is_loved(track_id): """ Check if object is in loved playlist @return bool """ return Lp().playlists.exists_track(Type.LOVED, track_id)
def _sync(self, playlists, convert, normalize): """ Sync playlists with device. If playlists contains Type.NONE, sync albums marked as to be synced @param playlists as [str] @param convert as bool @param normalize as bool """ try: self.__in_thread = True self.__convert = convert self.__normalize = normalize self.__errors = False self.__errors_count = 0 self.__copied_art_uris = [] # For progress bar self.__total = 1 self.__done = 0 self._fraction = 0.0 plnames = [] GLib.idle_add(Lp().window.progress.set_fraction, 0, self) if playlists and playlists[0] == Type.NONE: # New tracks for synced albums album_ids = Lp().albums.get_synced_ids() for album_id in album_ids: self.__total += len(Lp().albums.get_track_ids(album_id)) else: # New tracks for playlists for playlist in playlists: plnames.append(Lp().playlists.get_name(playlist)) self.__total += len(Lp().playlists.get_tracks(playlist)) # Old tracks try: children = self.__get_track_files() self.__total += len(children) except: pass GLib.idle_add(self._update_progress) # Copy new tracks to device if self._syncing: self.__copy_to_device(playlists) # Remove old tracks from device if self._syncing: self.__remove_from_device(playlists) # Remove empty dirs self.__remove_empty_dirs() # Remove old playlists d = Gio.File.new_for_uri(self._uri) infos = d.enumerate_children( 'standard::name', Gio.FileQueryInfoFlags.NOFOLLOW_SYMLINKS, None) for info in infos: f = info.get_name() if f.endswith(".m3u") and f[:-4] not in plnames: uri = self._uri + '/' + f d = Gio.File.new_for_uri(uri) self.__retry(d.delete, (None, )) d = Gio.File.new_for_uri(self._uri + "/unsync") if not d.query_exists(None): self.__retry(d.make_directory_with_parents, (None, )) except Exception as e: print("DeviceManagerWidget::_sync(): %s" % e) self._fraction = 1.0 self._syncing = False self.__in_thread = False if self.__errors: GLib.idle_add(self.__on_errors)
def Play(self): if Lp().player.current_track.id is None: Lp().player.set_party(True) else: Lp().player.play()