def __init__(self): """ Init playbin """ # In the case of gapless playback, both 'about-to-finish' # and 'eos' can occur during the same stream. self.__track_in_pipe = False self.__cancellable = Gio.Cancellable() self.__codecs = Codecs() self._current_track = Track() self._next_track = Track() self._prev_track = Track() self._playbin = self._playbin1 = Gst.ElementFactory.make( "playbin", "player") self._playbin2 = Gst.ElementFactory.make("playbin", "player") self._plugins = self._plugins1 = PluginsPlayer(self._playbin1) self._plugins2 = PluginsPlayer(self._playbin2) for playbin in [self._playbin1, self._playbin2]: flags = playbin.get_property("flags") flags &= ~GstPlayFlags.GST_PLAY_FLAG_VIDEO playbin.set_property("flags", flags) playbin.set_property("buffer-size", 5 << 20) playbin.set_property("buffer-duration", 10 * Gst.SECOND) playbin.connect("notify::volume", self.__on_volume_changed) playbin.connect("about-to-finish", self._on_stream_about_to_finish) bus = playbin.get_bus() bus.add_signal_watch() bus.connect("message::error", self._on_bus_error) bus.connect("message::eos", self._on_bus_eos) bus.connect("message::element", self._on_bus_element) bus.connect("message::stream-start", self._on_stream_start) bus.connect("message::tag", self._on_bus_message_tag) self._start_time = 0
def remove(playlist_id): tracks = [] for obj in self.__objects: if isinstance(obj, Album): for track_id in obj.track_ids: tracks.append(Track(track_id)) else: tracks = [Track(obj.id)] App().playlists.remove_tracks(playlist_id, tracks, True)
def __init__(self, object): """ Init edit menu @param object as Album/Track """ Gio.Menu.__init__(self) # Ignore genre_ids/artist_ids if isinstance(object, Album): self.__object = Album(object.id) else: self.__object = Track(object.id) self.__set_save_action() if self.__object.storage_type & StorageType.COLLECTION: self.__set_open_action()
def stop(self): """ Change player state to STOPPED @param force as bool """ self._current_track = Track() self._current_track = Track() self._prev_track = Track() self._next_track = Track() emit_signal(self, "current-changed") emit_signal(self, "prev-changed") emit_signal(self, "next-changed") self._playbin.set_state(Gst.State.NULL) emit_signal(self, "status-changed")
def set_next(self): """ Play next track """ if self._current_track.id is None: return if self._current_track.id == self.__stop_after_track_id: self._next_track = Track() return try: next_track = QueuePlayer.next(self) if next_track.id is None: # Diverge current track to restore playback from queue diverge_current_track = None if self._queue_current_track is not None: diverge_current_track = self._current_track self._current_track = self._queue_current_track if App().settings.get_value("shuffle") or self.is_party: next_track = ShufflePlayer.next(self) else: next_track = LinearPlayer.next(self) # Restore current track if diverge_current_track is not None: self._current_track = diverge_current_track self._queue_current_track = None self._next_track = next_track emit_signal(self, "next-changed") except Exception as e: Logger.error("Player::set_next(): %s" % e)
def __play_radio_common(self): """ Emit signal and reset cancellable """ emit_signal(self, "loading-changed", True, Track()) self.__radio_cancellable.cancel() self.__radio_cancellable = Gio.Cancellable()
def __get_next(self): """ Next track in shuffle mode @return track as Track """ try: if App().settings.get_value("shuffle") or self._is_party: if self._albums: track = self.__get_tracks_random() # All tracks done # Try to get another one track after reseting history if track.id is None: self.__to_play_albums = list(self._albums) shuffle(self.__to_play_albums) repeat = App().settings.get_enum("repeat") # Do not reset history if a new album is going to # be added if repeat not in [ Repeat.AUTO_SIMILAR, Repeat.AUTO_RANDOM ]: self.__history = [] self.__already_played_tracks = {} if repeat == Repeat.ALL: return self.__get_next() return track except Exception as e: Logger.error("ShufflePLayer::__get_next(): %s", e) return Track()
def get_tracks(self, playlist_id): """ Return availables tracks for playlist @param playlist_id as int @return [Track] """ return [Track(track_id) for track_id in self.get_track_ids(playlist_id)]
def prev(self): """ Prev track base on.current_track context @return track as Track """ repeat = App().settings.get_enum("repeat") # No album in playback if not self._albums: return Track() # User want us to repeat current track elif repeat == Repeat.TRACK: return self._current_track album = self._current_track.album track = self.__fallback_track_if_album_missing(album) # Current album missing, go to fallback track if track is not None: return track new_track_position = self._current_track.position - 1 # Previous album if new_track_position < 0: try: pos = self.albums.index(album) albums_count = len(self._albums) new_pos = 0 # Search for a prev album for idx in chain(reversed(range(0, pos)), reversed(range(pos, albums_count))): if self._albums[idx].tracks: new_pos = idx break if new_pos == albums_count - 1: if repeat == Repeat.ALL: pos = new_pos else: return Track() else: pos = new_pos except Exception as e: Logger.error("LinearPlayer::prev(): %s", e) pos = 0 # Happens if current album has been removed track = self._albums[pos].tracks[-1] # Previous track else: track = album.tracks[new_track_position] return track
def start_party(*ignore): if self._albums: # Start a new song if not playing if self._current_track.id is None: track = self.__get_tracks_random() self.load(track) elif not self.is_playing: self.play() emit_signal(self, "loading-changed", False, Track())
def next(self): """ Next track for current album or next album @return track as Track """ repeat = App().settings.get_enum("repeat") # No album in playback if not self.albums: return Track() # User want us to repeat current track elif repeat == Repeat.TRACK: return self._current_track album = self._current_track.album track = self.__fallback_track_if_album_missing(album) # Current album missing, go to fallback track if track is not None: return track new_track_position = self._current_track.position + 1 # next album if new_track_position >= len(album.track_ids): try: pos = self.albums.index(album) albums_count = len(self._albums) new_pos = 0 # Search for a next album for idx in chain(range(pos + 1, albums_count), range(0, pos)): if self._albums[idx].tracks: new_pos = idx break if new_pos == 0: if repeat == Repeat.ALL: pos = 0 else: return Track() else: pos = new_pos except Exception as e: Logger.error("LinearPlayer::next(): %s", e) pos = 0 # Happens if current album has been removed track = self._albums[pos].tracks[0] # next track else: track = album.tracks[new_track_position] return track
def play_radio_from_populars(self, artist_ids): """ Play a radio from artists popular tracks @param artist_ids as [int] """ track_ids = App().tracks.get_populars(artist_ids, StorageType.ALL, False, 100) shuffle(track_ids) albums = tracks_to_albums([Track(track_id) for track_id in track_ids]) App().player.play_albums(albums)
def play_radio_from_loved(self, artist_ids): """ Play a radio from artists loved tracks @param artist_ids as [int] """ track_ids = App().tracks.get_loved_track_ids(artist_ids, StorageType.ALL) shuffle(track_ids) albums = tracks_to_albums([Track(track_id) for track_id in track_ids]) App().player.play_albums(albums)
def load(): request = App().playlists.get_smart_sql(self.__playlist_id) # We need to inject skipped/storage_type storage_type = get_default_storage_type() split = request.split("ORDER BY") split[0] += " AND tracks.loved != %s" % Type.NONE split[0] += " AND tracks.storage_type&%s " % storage_type track_ids = App().db.execute("ORDER BY".join(split)) return tracks_to_albums( [Track(track_id) for track_id in track_ids])
def get_track(self, track_id): """ Get track @param track_id as int @return Track """ for track in self.tracks: if track.id == track_id: return track return Track()
def next(self): """ Get next track id @return Track """ track_id = None if self.__queue: track_id = self.__queue[0] if self._queue_current_track is None: self._queue_current_track = self._current_track return Track(track_id)
def append_track(self, track, clone=True): """ Append track to album, do not disable clone if you know self is already used @param track as Track @param clone as bool """ if clone: self._tracks.append(Track(track.id, self)) else: self._tracks.append(track) track.set_album(self)
def _on_match_track(self, search, track_id, storage_type): """ Add a new track to view @param search as *Search @param track_id as int @param storage_type as StorageType """ if storage_type & StorageType.SEARCH: track = Track(track_id) self.__stack.current_child.search_tracks_view.show() self.__stack.current_child.search_tracks_view.append_row(track) self.show_placeholder(False)
def next_album(self): """ Get next album to add @return Album """ genre_ids = App().artists.get_genre_ids(self.current_track.artist_ids, StorageType.COLLECTION) track_ids = App().tracks.get_randoms(genre_ids, StorageType.COLLECTION, 1) if track_ids: return Track(track_ids[0]).album return None
def play_radio_from_collection(self, artist_ids): """ Play a radio from collection for artist ids @param artist_ids as [int] """ genre_ids = App().artists.get_genre_ids(artist_ids, StorageType.COLLECTION) track_ids = App().tracks.get_randoms(genre_ids, StorageType.COLLECTION, False, 100) albums = tracks_to_albums([Track(track_id) for track_id in track_ids], False) self.play_albums(albums)
def __fallback_track_if_album_missing(self, album): """ Get a fallback track if album not in player @param album as Album @return Track/None """ if album not in self._albums: album = self._albums[0] if album.tracks: return album.tracks[0] else: return Track() return None
def tracks(self): """ Get disc tracks @return [Track] """ if not self.__tracks and self.album.id is not None: self.__tracks = [ Track(track_id, self.album) for track_id in self.db.get_disc_track_ids( self.album.id, self.album.genre_ids, self.album.artist_ids, self.number, self.__storage_type, self.__skipped) ] return self.__tracks
def _on_playlist_track_added(self, playlists, playlist_id, uri): """ Append track to album list @param playlists as Playlists @param playlist_id as int @param uri as str """ if playlist_id == self.__playlist_id: track = Track(App().tracks.get_id_by_uri(uri)) album = Album(track.album.id) album.set_tracks([track]) self.add_reveal_albums([album]) self.add_value(album)
def __on_match_track(self, similars, track_id, storage_type): """ Load/Play track album @param similars as Similars @param track_id as int @param storage_type as StorageType """ track = Track(track_id) album = track.album if self.albums: self.add_album(album) else: self.play_album(album)
def next(self): """ Next shuffle track @return Track """ repeat = App().settings.get_enum("repeat") if repeat == Repeat.TRACK: return self._current_track if self.shuffle_has_next: track = self.__history.next.value elif self._albums: track = self.__get_next() else: track = Track() return track
def load(): track_ids = [] if self.__playlist_id == Type.LOVED: for track_id in App().tracks.get_loved_track_ids( [], self.storage_type): if track_id not in track_ids: track_ids.append(track_id) else: for track_id in App().playlists.get_track_ids( self.__playlist_id): if track_id not in track_ids: track_ids.append(track_id) return tracks_to_albums( [Track(track_id) for track_id in track_ids])
def populate(self): """ Populate view """ if App().player.queue: tracks = [Track(track_id) for track_id in App().player.queue] albums = tracks_to_albums(tracks) else: albums = App().player.albums if albums: if len(albums) == 1: self.add_reveal_albums(albums) AlbumsListView.populate(self, albums) self.show_placeholder(False) else: self.show_placeholder(True)
def _on_playlist_track_removed(self, playlists, playlist_id, uri): """ Remove track from album list @param playlists as Playlists @param playlist_id as int @param uri as str """ if playlist_id == self.__playlist_id: track = Track(App().tracks.get_id_by_uri(uri)) for album_row in self.children: if album_row.album.id == track.album.id: for track_row in album_row.children: if track_row.track.id == track.id: track_row.destroy() if len(self.children) == 1: album_row.destroy() break
def set_party_ids(self): """ Set party mode ids """ if not self._is_party: return party_ids = App().settings.get_value("party-ids") storage_type = get_default_storage_type() album_ids = App().albums.get_ids(party_ids, [], storage_type, False) emit_signal(self, "playback-setted", []) self._albums = [] if album_ids: emit_signal(self, "loading-changed", True, Track()) for album_id in album_ids: album = Album(album_id, [], [], False) self._albums.append(album) emit_signal(self, "playback-setted", list(self._albums))
def __write_playlists(self, playlist_ids): """ Write playlists on disk @param playlist_ids as [int] """ for playlist_id in playlist_ids: if self.__cancellable.is_cancelled(): break try: # Get tracks if App().playlists.get_smart(playlist_id): request = App().playlists.get_smart_sql(playlist_id) track_ids = App().db.execute(request) else: track_ids = App().playlists.get_track_ids(playlist_id) # Build tracklist tracklist = "#EXTM3U\n" for track_id in track_ids: if self.__cancellable.is_cancelled(): break track = Track(track_id) f = Gio.File.new_for_uri(track.uri) filename = f.get_basename() album_uri = self.__get_album_name(track) uri = "%s/%s" % (album_uri, escape(filename)) (convertion_needed, uri) = self.__is_convertion_needed(track.uri, uri) tracklist += "%s\n" % uri # Write playlist file playlist_name = escape(App().playlists.get_name(playlist_id)) playlist_uri = "%s/%s.m3u" % (self.__uri, playlist_name) Logger.debug("MtpSync::__write_playlists(): %s" % playlist_uri) temp_uri = os.path.join(tempfile.gettempdir(), "lollypop_%s.m3u" % playlist_name) m3u_temp = Gio.File.new_for_path(temp_uri) m3u_temp.replace_contents( tracklist.encode("utf-8"), None, False, Gio.FileCreateFlags.REPLACE_DESTINATION, self.__cancellable) m3u = Gio.File.new_for_uri(playlist_uri) m3u_temp.move(m3u, Gio.FileCopyFlags.OVERWRITE, None, None) except Exception as e: Logger.error("MtpSync::__write_playlists(): %s", e)