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_switch_state_set(self, widget, state, genre_id): """ Update party ids when use change a switch in dialog @param widget as Gtk.Switch @param state as bool, 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)) Lp().player.set_party_ids() Lp().player.set_next()
def __scan(self, uris): """ Scan music collection for music files @param uris as [string], uris to scan @thread safe """ gst_message = None 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: 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 GLib.idle_add(self.__update_progress, i, count) f = Gio.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[uri]: 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)) # 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) if uri.startswith('file:'): 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 GLib.GError as e: print("CollectionScanner::__scan:", e) if e.message != gst_message: gst_message = e.message if Lp().notify is not None: Lp().notify.send(gst_message) sql.commit() except Exception as e: print("CollectionScanner::__scan()", e) GLib.idle_add(self.__finish) del self.__history self.__history = None
class ShufflePlayer(BasePlayer): """ Shuffle player Manage shuffle tracks and party mode """ def __init__(self): """ Init shuffle player """ BasePlayer.__init__(self) self.reset_history() # Party mode self.__is_party = False Lp().settings.connect('changed::shuffle', self.__set_shuffle) def reset_history(self): """ Reset history """ # Tracks already played self.__history = [] # Used by shuffle albums to restore playlist before shuffle self._albums_backup = [] # Albums already played self.__already_played_albums = [] # Tracks already played for albums self.__already_played_tracks = {} # Reset user playlist self._user_playlist = [] self._user_playlist_ids = [] @property def shuffle_has_next(self): """ True if history provide a next track @return bool """ return self.__history and self.__history.has_next() @property def shuffle_has_prev(self): """ True if history provide a prev track @return bool """ return self.__history and self.__history.has_prev() def next(self): """ Next shuffle track @return Track """ track_id = None if self._shuffle == Shuffle.TRACKS or self.__is_party: if self.shuffle_has_next: track_id = self.__history.get_next().get_value() elif self._albums: track_id = self.__shuffle_next() else: track_id = self._current_track.id return Track(track_id) def prev(self): """ Prev track based on history @return Track """ track_id = None if self._shuffle == Shuffle.TRACKS or self.__is_party: if self.shuffle_has_prev: track_id = self.__history.get_prev().get_value() else: track_id = self._current_track.id return Track(track_id) 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 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.__is_party = party albums_backup = self._albums_backup self.reset_history() if self._plugins1.rgvolume is not None and\ self._plugins2.rgvolume is not None: if party: 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._albums_backup = self._albums self._external_tracks = [] self._context.genre_ids = {} self.set_party_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: self._albums = albums_backup # We want current album to continue playback if self._current_track.album.id not in self._albums: self._albums.insert(0, self._current_track.album.id) self.set_next() self.set_prev() self.emit('party-changed', party) @property def is_party(self): """ True if party mode on @return bool """ return self.__is_party def shuffle_albums(self, shuffle): """ Shuffle album list @param shuffle as bool """ if shuffle and self._shuffle == Shuffle.ALBUMS: if self._albums: self._albums_backup = list(self._albums) random.shuffle(self._albums) # In album shuffle, keep current album on top if self._current_track.album.id in self._albums: self._albums.remove(self._current_track.album.id) self._albums.insert(0, self._current_track.album.id) elif self._albums_backup: self._albums = self._albums_backup self._albums_backup = [] 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] = [] ####################### # PROTECTED # ####################### def _on_stream_start(self, bus, message): """ On stream start add to shuffle history """ # Add track to shuffle history if needed if self._shuffle == Shuffle.TRACKS or self.__is_party: if self.__history: next = self.__history.get_next() prev = self.__history.get_prev() # Next track if next is not None and\ self._current_track.id == next.get_value(): next = self.__history.get_next() next.set_prev(self.__history) self.__history = next # Previous track elif prev is not None and\ self._current_track.id == prev.get_value(): prev = self.__history.get_prev() prev.set_next(self.__history) self.__history = prev # New track elif self.__history.get_value() != self._current_track.id: new_list = LinkedList(self._current_track.id, None, self.__history) self.__history = new_list else: new_list = LinkedList(self._current_track.id) self.__history = new_list self.__add_to_shuffle_history(self._current_track) ####################### # PRIVATE # ####################### def __set_shuffle(self, settings, value): """ Set shuffle mode to gettings value @param settings as Gio.Settings, value as str """ self._shuffle = Lp().settings.get_enum('shuffle') if self._plugins1.rgvolume is not None and\ self._plugins2.rgvolume is not None: if self._shuffle == Shuffle.TRACKS or self._user_playlist: 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 self._user_playlist: self._shuffle_playlist() elif self._shuffle == Shuffle.NONE: self.shuffle_albums(False) elif self._shuffle == Shuffle.ALBUMS: self.shuffle_albums(True) if self._current_track.id is not None: self.set_next() def __shuffle_next(self): """ Next track in shuffle mode @return track id as int """ try: track_id = self.__get_random() # Need to clear history if track_id is None: self._albums = self.__already_played_albums self.reset_history() return self.__shuffle_next() return track_id except: # Recursion error return None def __get_random(self): """ Return a random track and make sure it has never been played """ for album_id in sorted(self._albums, key=lambda *args: random.random()): # We need to check this as in party mode, some items do not # have a valid genre (Populars, ...) if album_id in self._context.genre_ids.keys(): genre_ids = self._context.genre_ids[album_id] else: genre_ids = [] tracks = Album(album_id, genre_ids).track_ids for track in sorted(tracks, key=lambda *args: random.random()): if album_id not in self.__already_played_tracks.keys() or\ track not in self.__already_played_tracks[album_id]: return track # No new tracks for this album, remove it # If albums not in shuffle history, it's not present # in db anymore (update since shuffle set) if album_id in self.__already_played_tracks.keys(): self.__already_played_tracks.pop(album_id) self.__already_played_albums.append(album_id) self._albums.remove(album_id) self._next_context = NextContext.STOP return None def __add_to_shuffle_history(self, track): """ Add a track to shuffle history @param track as Track """ if track.album_id not in self.__already_played_tracks.keys(): self.__already_played_tracks[track.album_id] = [] if track.id not in self.__already_played_tracks[track.album_id]: self.__already_played_tracks[track.album_id].append(track.id)
class ShufflePlayer(BasePlayer): """ Shuffle player Manage shuffle tracks and party mode """ def __init__(self): """ Init shuffle player """ BasePlayer.__init__(self) # Party mode self.__is_party = False self.reset_history() Lp().settings.connect('changed::shuffle', self.__set_shuffle) def reset_history(self): """ Reset history """ # Tracks already played self.__history = [] # Used by shuffle albums to restore playlist before shuffle self._albums_backup = [] # Albums already played self.__already_played_albums = [] # Tracks already played for albums self.__already_played_tracks = {} # If we have tracks/albums to ignore in party mode, add them t = Thread(target=self.__init_party_blacklist) t.daemon = True t.start() # Reset user playlist self._user_playlist = [] self._user_playlist_ids = [] @property def shuffle_has_next(self): """ True if history provide a next track @return bool """ return self.__history and self.__history.has_next() @property def shuffle_has_prev(self): """ True if history provide a prev track @return bool """ return self.__history and self.__history.has_prev() def next(self): """ Next shuffle track @return Track """ track_id = None if self._shuffle == Shuffle.TRACKS or self.__is_party: if self.shuffle_has_next: track_id = self.__history.get_next().get_value() elif self._albums: track_id = self.__shuffle_next() else: track_id = self._current_track.id return Track(track_id) def prev(self): """ Prev track based on history @return Track """ track_id = None if self._shuffle == Shuffle.TRACKS or self.__is_party: if self.shuffle_has_prev: track_id = self.__history.get_prev().get_value() else: track_id = self._current_track.id return Track(track_id) 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 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.__is_party = party albums_backup = self._albums_backup self.reset_history() if self._plugins1.rgvolume is not None and\ self._plugins2.rgvolume is not None: if party: 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._albums_backup = self._albums self._external_tracks = [] self._context.genre_ids = {} self.set_party_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: self._albums = albums_backup # We want current album to continue playback if self._current_track.album.id not in self._albums: self._albums.insert(0, self._current_track.album.id) self.set_next() self.set_prev() self.emit('party-changed', party) @property def is_party(self): """ True if party mode on @return bool """ return self.__is_party def shuffle_albums(self, shuffle): """ Shuffle album list @param shuffle as bool """ if shuffle and self._shuffle == Shuffle.ALBUMS: if self._albums: self._albums_backup = list(self._albums) random.shuffle(self._albums) # In album shuffle, keep current album on top if self._current_track.album.id in self._albums: self._albums.remove(self._current_track.album.id) self._albums.insert(0, self._current_track.album.id) elif self._albums_backup: self._albums = self._albums_backup self._albums_backup = [] 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] = [] ####################### # PROTECTED # ####################### def _on_stream_start(self, bus, message): """ On stream start add to shuffle history """ # Add track to shuffle history if needed if self._shuffle == Shuffle.TRACKS or self.__is_party: if self.__history: next = self.__history.get_next() prev = self.__history.get_prev() # Next track if next is not None and\ self._current_track.id == next.get_value(): next = self.__history.get_next() next.set_prev(self.__history) self.__history = next # Previous track elif prev is not None and\ self._current_track.id == prev.get_value(): prev = self.__history.get_prev() prev.set_next(self.__history) self.__history = prev # New track elif self.__history.get_value() != self._current_track.id: new_list = LinkedList(self._current_track.id, None, self.__history) self.__history = new_list else: new_list = LinkedList(self._current_track.id) self.__history = new_list self.__add_to_shuffle_history(self._current_track) ####################### # PRIVATE # ####################### def __set_shuffle(self, settings, value): """ Set shuffle mode to gettings value @param settings as Gio.Settings, value as str """ self._shuffle = Lp().settings.get_enum('shuffle') if self._plugins1.rgvolume is not None and\ self._plugins2.rgvolume is not None: if self._shuffle == Shuffle.TRACKS or self._user_playlist: 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 self._user_playlist: self._shuffle_playlist() elif self._shuffle == Shuffle.NONE: self.shuffle_albums(False) elif self._shuffle == Shuffle.ALBUMS: self.shuffle_albums(True) if self._current_track.id is not None: self.set_next() def __shuffle_next(self): """ Next track in shuffle mode @return track id as int """ try: track_id = self.__get_random() # Need to clear history if track_id is None: self._albums = self.__already_played_albums self.reset_history() return self.__shuffle_next() return track_id except: # Recursion error return None def __get_random(self): """ Return a random track and make sure it has never been played """ for album_id in sorted(self._albums, key=lambda *args: random.random()): # We need to check this as in party mode, some items do not # have a valid genre (Populars, ...) if album_id in self._context.genre_ids.keys(): genre_ids = self._context.genre_ids[album_id] else: genre_ids = [] tracks = Album(album_id, genre_ids).track_ids for track in sorted(tracks, key=lambda *args: random.random()): if album_id not in self.__already_played_tracks.keys() or\ track not in self.__already_played_tracks[album_id]: return track # No new tracks for this album, remove it # If albums not in shuffle history, it's not present # in db anymore (update since shuffle set) if album_id in self.__already_played_tracks.keys(): self.__already_played_tracks.pop(album_id) self.__already_played_albums.append(album_id) self._albums.remove(album_id) self._next_context = NextContext.STOP return None def __add_to_shuffle_history(self, track): """ Add a track to shuffle history @param track as Track """ if track.album_id not in self.__already_played_tracks.keys(): self.__already_played_tracks[track.album_id] = [] if track.id not in self.__already_played_tracks[track.album_id]: self.__already_played_tracks[track.album_id].append(track.id) def __init_party_blacklist(self): """ Add party mode blacklist to already played tracks """ if self.__is_party: for track_id in Lp().playlists.get_track_ids(Type.NOPARTY): track = Track(track_id) self.__add_to_shuffle_history(track)
def __scan(self, paths): """ Scan music collection for music files @param paths as [string], paths to scan @thread safe """ gst_message = None if self.__history is None: self.__history = History() mtimes = Lp().tracks.get_mtimes() orig_tracks = Lp().tracks.get_uris() was_empty = len(orig_tracks) == 0 (new_tracks, new_dirs) = self.__get_objects_for_paths(paths) count = len(new_tracks) + len(orig_tracks) # Add monitors on dirs if self.__inotify is not None: for d in new_dirs: self.__inotify.add_monitor(d) with SqlCursor(Lp().db) as sql: i = 0 for uri in new_tracks: if self.__thread is None: return GLib.idle_add(self.__update_progress, i, count) try: f = Gio.File.new_for_uri(uri) info = f.query_info('time::modified', Gio.FileQueryInfoFlags.NONE, None) mtime = 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[uri]: i += 1 continue else: self.__del_from_db(uri) info = self.get_info(uri) # On first scan, use modification time # Else, use current time if not was_empty: mtime = int(time()) debug("Adding file: %s" % uri) self.__add2db(uri, info, mtime) except GLib.GError as e: print(e, uri) if e.message != gst_message: gst_message = e.message if Lp().notify is not None: Lp().notify.send(gst_message) except: pass i += 1 # Clean deleted files for uri in orig_tracks: i += 1 GLib.idle_add(self.__update_progress, i, count) if uri.startswith('file:'): self.__del_from_db(uri) sql.commit() GLib.idle_add(self.__finish) del self.__history self.__history = None
def _scan(self, paths): """ Scan music collection for music files @param paths as [string], paths to scan @thread safe """ if self._history is None: self._history = History() mtimes = Lp().tracks.get_mtimes() orig_tracks = Lp().tracks.get_paths() was_empty = len(orig_tracks) == 0 (new_tracks, new_dirs, count) = self._get_objects_for_paths(paths) count += len(orig_tracks) # Add monitors on dirs if self._inotify is not None: for d in new_dirs: self._inotify.add_monitor(d) with SqlCursor(Lp().db) as sql: i = 0 for filepath in new_tracks: if self._thread is None: return GLib.idle_add(self._update_progress, i, count) try: # If songs exists and mtime unchanged, continue, # else rescan if filepath in orig_tracks: orig_tracks.remove(filepath) i += 1 mtime = int(os.path.getmtime(filepath)) if mtime <= mtimes[filepath]: i += 1 continue else: self._del_from_db(filepath) infos = self.get_infos(filepath) # On first scan, use modification time # Else, use current time if was_empty: mtime = int(os.path.getmtime(filepath)) else: mtime = int(time()) debug("Adding file: %s" % filepath) self._add2db(filepath, infos, mtime) except Exception as e: debug("Error scanning: %s, %s" % (filepath, e)) string = "%s" % e if string.startswith('gst-core-error-quark'): self._missing_codecs = filepath i += 1 # Clean deleted files for filepath in orig_tracks: i += 1 GLib.idle_add(self._update_progress, i, count) self._del_from_db(filepath) sql.commit() GLib.idle_add(self._finish) del self._history self._history = None
def _scan(self, paths): """ Scan music collection for music files @param paths as [string], paths to scan @thread safe """ self._new_albums = [] mtimes = Lp().tracks.get_mtimes() orig_tracks = Lp().tracks.get_paths() is_empty = len(orig_tracks) == 0 # Add monitors on dirs (new_tracks, new_dirs, count) = self._get_objects_for_paths(paths) if self._inotify is not None: for d in new_dirs: self._inotify.add_monitor(d) with SqlCursor(Lp().db) as sql: i = 0 for filepath in new_tracks: if self._thread is None: return GLib.idle_add(self._update_progress, i, count) try: mtime = int(os.path.getmtime(filepath)) if filepath not in orig_tracks: try: debug("Adding file: %s" % filepath) infos = self.get_infos(filepath) self._add2db(filepath, mtime, infos) except Exception as e: debug("Error scanning: %s, %s" % (filepath, e)) string = "%s" % e if string.startswith('gst-core-error-quark'): self._missing_codecs = filepath else: # Update tags by removing song and readd it if mtime != mtimes[filepath]: debug("Adding file: %s" % filepath) infos = self.get_infos(filepath) if infos is not None: self._add2db(filepath, mtime, infos) else: print("Can't get infos for ", filepath) else: orig_tracks.remove(filepath) except Exception as e: print(ascii(filepath)) print("CollectionScanner::_scan(): %s" % e) i += 1 # Restore stats for new albums if not is_empty: for album_id in self._new_albums: duration = Lp().albums.get_duration(album_id, None) count = Lp().albums.get_count(album_id, None) value = Lp().albums.get_stats(duration, count) if value is not None: Lp().albums.set_popularity(album_id, value[0]) Lp().albums.set_mtime(album_id, value[1]) # Clean deleted files for filepath in orig_tracks: track_id = Lp().tracks.get_id_by_path(filepath) self._del_from_db(track_id) sql.commit() GLib.idle_add(self._finish)
class ShufflePlayer(BasePlayer): """ Shuffle player Manage shuffle tracks and party mode """ def __init__(self): """ Init shuffle player """ BasePlayer.__init__(self) self.reset_history() # Party mode self._is_party = False Lp().settings.connect('changed::shuffle', self._set_shuffle) def reset_history(self): """ Reset history """ # Tracks already played self._history = [] # Used by shuffle albums to restore playlist before shuffle self._albums_backup = [] # Albums already played self._already_played_albums = [] # Tracks already played for albums self._already_played_tracks = {} # Reset use playlist self._user_playlist = [] def next(self): """ Next shuffle track @return Track """ track_id = None if self._shuffle in [Shuffle.TRACKS, Shuffle.TRACKS_ARTIST] or\ self._is_party: if self._history and self._history.has_next(): track_id = self._history.get_next().get_value() elif self._albums: track_id = self._shuffle_next() return Track(track_id) def prev(self): """ Prev track based on history @return Track """ track_id = None if self._shuffle in [Shuffle.TRACKS, Shuffle.TRACKS_ARTIST] or\ self._is_party: if self._history and self._history.has_prev(): track_id = self._history.get_prev().get_value() else: track_id = self.current_track.id return Track(track_id) 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 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 is_party(self): """ True if party mode on @return bool """ return self._is_party ####################### # PRIVATE # ####################### def _set_shuffle(self, settings, value): """ Set shuffle mode to gettings value @param settings as Gio.Settings, value as str """ self._shuffle = Lp().settings.get_enum('shuffle') if self._rgvolume is not None: if self._shuffle in [Shuffle.TRACKS, Shuffle.TRACKS_ARTIST] or\ self._user_playlist: self._rgvolume.props.album_mode = 0 else: self._rgvolume.props.album_mode = 1 if self._user_playlist: self._shuffle_playlist() elif self.current_track.id is not None and self.current_track.id >= 0: self.set_albums(self.current_track.id, self.context.artist_ids, self.context.genre_ids) if self.current_track.id is not None: self.set_next() def _shuffle_albums(self): """ Shuffle album list """ if self._shuffle in [Shuffle.ALBUMS, Shuffle.ALBUMS_ARTIST]: if self._albums and not self._albums_backup: self._albums_backup = list(self._albums) random.shuffle(self._albums) elif self._shuffle == Shuffle.NONE: if self._albums_backup: self._albums = self._albums_backup self._albums_backup = [] def _shuffle_next(self): """ Next track in shuffle mode @return track id as int """ try: track_id = self._get_random() # Need to clear history if track_id is None: self._albums = self._already_played_albums self.reset_history() return self._shuffle_next() return track_id except: # Recursion error return None def _get_random(self): """ Return a random track and make sure it has never been played """ for album_id in sorted(self._albums, key=lambda *args: random.random()): tracks = Album(album_id, self.context.genre_ids).tracks_ids for track in sorted(tracks, key=lambda *args: random.random()): if album_id not in self._already_played_tracks.keys() or\ track not in self._already_played_tracks[album_id]: return track # No new tracks for this album, remove it # If albums not in shuffle history, it's not present # in db anymore (update since shuffle set) if album_id in self._already_played_tracks.keys(): self._already_played_tracks.pop(album_id) self._already_played_albums.append(album_id) self._albums.remove(album_id) return None def _add_to_shuffle_history(self, track): """ Add a track to shuffle history @param track as Track """ if track.album_id not in self._already_played_tracks.keys(): self._already_played_tracks[track.album_id] = [] if track.id not in self._already_played_tracks[track.album_id]: self._already_played_tracks[track.album_id].append(track.id) def _on_stream_start(self, bus, message): """ On stream start add to shuffle history """ # Add track to shuffle history if needed if self._shuffle != Shuffle.NONE or self._is_party: if self._history: next = self._history.get_next() prev = self._history.get_prev() # Next track if next is not None and\ self.current_track.id == next.get_value(): next = self._history.get_next() next.set_prev(self._history) self._history = next # Previous track elif prev is not None and\ self.current_track.id == prev.get_value(): prev = self._history.get_prev() prev.set_next(self._history) self._history = prev # New track elif self._history.get_value() != self.current_track.id: new_list = LinkedList(self.current_track.id, None, self._history) self._history = new_list else: new_list = LinkedList(self.current_track.id) self._history = new_list self._add_to_shuffle_history(self.current_track)
def __scan(self, uris): """ Scan music collection for music files @param uris as [string], uris to scan @thread safe """ gst_message = None 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: 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 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[uri]: 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)) # 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) if uri.startswith('file:'): 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 GLib.GError as e: print("CollectionScanner::__scan:", e, uri) if e.message != gst_message: gst_message = e.message if Lp().notify is not None: Lp().notify.send(gst_message, uri) sql.commit() except Exception as e: print("CollectionScanner::__scan()", e) GLib.idle_add(self.__finish) del self.__history self.__history = None
class ShufflePlayer(BasePlayer): """ Shuffle player Manage shuffle tracks and party mode """ def __init__(self): """ Init shuffle player """ BasePlayer.__init__(self) self.reset_history() # Party mode self._is_party = False Lp().settings.connect('changed::shuffle', self._set_shuffle) def reset_history(self): """ Reset history """ # Tracks already played self._history = None # Used by shuffle albums to restore playlist before shuffle self._albums_backup = None # Albums already played self._already_played_albums = [] # Tracks already played for albums self._already_played_tracks = {} # Reset use playlist self._user_playlist = [] def next(self): """ Next shuffle track @return Track """ track_id = None if self._shuffle in [Shuffle.TRACKS, Shuffle.TRACKS_ARTIST] or\ self._is_party: if self._history is not None and \ self._history.has_next(): track_id = self._history.get_next().get_value() elif self._albums is not None: track_id = self._shuffle_next() return Track(track_id) def prev(self): """ Prev track based on history @return Track """ track_id = None if self._shuffle in [Shuffle.TRACKS, Shuffle.TRACKS_ARTIST] or\ self._is_party: if self._history is not None and \ self._history.has_prev(): track_id = self._history.get_prev().get_value() else: track_id = self.current_track.id return Track(track_id) 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 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_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, None) self.emit('party-changed', party) Lp().window.update_view() def is_party(self): """ True if party mode on @return bool """ return self._is_party ####################### # PRIVATE # ####################### def _set_shuffle(self, settings, value): """ Set shuffle mode to gettings value @param settings as Gio.Settings, value as str """ self._shuffle = Lp().settings.get_enum('shuffle') if self._rgvolume is not None: if self._shuffle in [Shuffle.TRACKS, Shuffle.TRACKS_ARTIST] or\ self._user_playlist: self._rgvolume.props.album_mode = 0 else: self._rgvolume.props.album_mode = 1 if self._user_playlist: self._shuffle_playlist() elif self.current_track.id is not None and self.current_track.id >= 0: self.set_albums(self.current_track.id, self.current_track.album_artist_id, self.context.genre_id) if self.current_track.id is not None: self.set_next() def _shuffle_albums(self): """ Shuffle album list """ if self._shuffle in [Shuffle.ALBUMS, Shuffle.ALBUMS_ARTIST]: if self._albums is not None and self._albums_backup is None: self._albums_backup = list(self._albums) random.shuffle(self._albums) elif self._shuffle == Shuffle.NONE: if self._albums_backup is not None: self._albums = self._albums_backup self._albums_backup = None def _shuffle_next(self): """ Next track in shuffle mode @return track id as int """ try: track_id = self._get_random() # Need to clear history if not track_id: self._albums = self._already_played_albums self.reset_history() return self._shuffle_next() return track_id except: # Recursion error return None def _get_random(self): """ Return a random track and make sure it has never been played """ for album_id in sorted(self._albums, key=lambda *args: random.random()): tracks = Album(album_id, self.context.genre_id).tracks_ids for track in sorted(tracks, key=lambda *args: random.random()): if album_id not in self._already_played_tracks.keys() or\ track not in self._already_played_tracks[album_id]: return track # No new tracks for this album, remove it # If albums not in shuffle history, it's not present # in db anymore (update since shuffle set) if album_id in self._already_played_tracks.keys(): self._already_played_tracks.pop(album_id) self._already_played_albums.append(album_id) self._albums.remove(album_id) return None def _add_to_shuffle_history(self, track): """ Add a track to shuffle history @param track as Track """ if track.album_id not in self._already_played_tracks.keys(): self._already_played_tracks[track.album_id] = [] if track.id not in self._already_played_tracks[track.album_id]: self._already_played_tracks[track.album_id].append(track.id) def _on_stream_start(self, bus, message): """ On stream start add to shuffle history """ # Add track to shuffle history if needed if self._shuffle != Shuffle.NONE or self._is_party: if self._history is not None: next = self._history.get_next() prev = self._history.get_prev() # Next track if next is not None and\ self.current_track.id == next.get_value(): next = self._history.get_next() next.set_prev(self._history) self._history = next # Previous track elif prev is not None and\ self.current_track.id == prev.get_value(): prev = self._history.get_prev() prev.set_next(self._history) self._history = prev # New track elif self._history.get_value() != self.current_track.id: new_list = LinkedList(self.current_track.id, None, self._history) self._history = new_list else: new_list = LinkedList(self.current_track.id) self._history = new_list self._add_to_shuffle_history(self.current_track)