Esempio n. 1
0
 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))
Esempio n. 2
0
 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))
Esempio n. 3
0
 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()
Esempio n. 4
0
    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
Esempio n. 5
0
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)
Esempio n. 6
0
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)
Esempio n. 7
0
    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
Esempio n. 8
0
    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
Esempio n. 9
0
    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)
Esempio n. 10
0
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)
Esempio n. 11
0
    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
Esempio n. 12
0
    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)
Esempio n. 13
0
    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
Esempio n. 14
0
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)