示例#1
0
    def next(self):
        """
            Return next radio name, uri
            @return Track
        """
        track = Track()
        if self.current_track.id != Type.RADIOS:
            return track

        radios_manager = RadiosManager()
        radios = radios_manager.get()
        i = 0
        for (radio_id, name) in radios:
            i += 1
            if self.current_track.album_artist == name:
                break

        # Get next radio
        if i >= len(radios):
            i = 0

        name = radios[i][1]
        uris = radios_manager.get_tracks(name)
        if len(uris) > 0:
            track.set_radio(name, uris[0])
        return track
示例#2
0
    def prev(self):
        """
            Return prev radio name, uri
            @return Track
        """
        track = Track()
        if self.current_track.id != Type.RADIOS:
            return track

        radios_manager = RadiosManager()
        radios = radios_manager.get()
        i = len(radios) - 1
        for (radio_id, name) in reversed(radios):
            i -= 1
            if self.current_track.album_artist == name:
                break

        # Get prev radio
        if i < 0:
            i = len(radios) - 1

        name = radios[i][1]
        uris = radios_manager.get_tracks(name)
        if len(uris) > 0:
            track.set_radio(name, uris[0])
        return track
示例#3
0
    def __init__(self, name, radios_manager):
        """
            Init Popover
            @param name as string
            @param radios_manager as RadiosManager
        """
        Gtk.Popover.__init__(self)
        self.connect('map', self.__on_map)
        self.connect('unmap', self.__on_unmap)
        self.__name = name
        self.__radios_manager = radios_manager
        self.__start = 0
        self.__orig_pixbufs = {}

        self.__stack = Gtk.Stack()
        self.__stack.set_transition_duration(1000)
        self.__stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
        self.__stack.show()

        builder = Gtk.Builder()
        builder.add_from_resource('/org/gnome/Lollypop/RadioPopover.ui')
        builder.connect_signals(self)

        self.__view = Gtk.FlowBox()
        self.__view.set_selection_mode(Gtk.SelectionMode.NONE)
        self.__view.connect('child-activated', self.__on_activate)
        self.__view.set_max_children_per_line(100)
        self.__view.set_property('row-spacing', 10)
        self.__view.show()

        builder.get_object('viewport').add(self.__view)

        self.__name_entry = builder.get_object('name')
        self.__uri_entry = builder.get_object('uri')
        self.__btn_add_modify = builder.get_object('btn_add_modify')
        self.__spinner = builder.get_object('spinner')
        self.__stack.add_named(builder.get_object('spinner-grid'), 'spinner')
        self.__stack.add_named(builder.get_object('notfound'), 'notfound')
        self.__stack.add_named(builder.get_object('logo'), 'logo')
        self.__stack.add_named(builder.get_object('widget'), 'widget')
        self.__stack.set_visible_child_name('widget')
        self.add(self.__stack)

        track = Track()
        track.set_radio(name, '')
        rating = RatingWidget(track)
        rating.show()
        builder.get_object('widget').attach(rating, 0, 2, 2, 1)

        if self.__name == '':
            # Translators: radio context
            builder.get_object('btn_add_modify').set_label(_("Add"))
        else:
            # Translators: radio context
            builder.get_object('btn_add_modify').set_label(_("Modify"))
            builder.get_object('btn_delete').show()
            self.__name_entry.set_text(self.__name)
            url = self.__radios_manager.get_url(self.__name)
            if url:
                self.__uri_entry.set_text(url)
示例#4
0
    def next(self):
        """
            Return next radio name, uri
            @return Track
        """
        track = Track()
        if self.current_track.id != Type.RADIOS:
            return track

        radios_manager = Radios()
        radios = radios_manager.get()
        i = 0
        for (name, url) in radios:
            i += 1
            if self.current_track.album_artist == name:
                break

        # Get next radio
        if i >= len(radios):
            i = 0

        name = radios[i][0]
        url = radios[i][1]
        if url:
            track.set_radio(name, url)
        return track
示例#5
0
文件: player.py 项目: kmf/lollypop
    def set_albums(self, track_id, artist_ids, genre_ids):
        """
            Set album list (for next/prev)
            @param track id as int
            @param artist id as int
            @param genre id as int
        """
        # Invalid track
        if track_id is None:
            return
        album = Track(track_id).album
        self._albums = []
        ShufflePlayer.reset_history(self)

        # We are not playing a user playlist anymore
        self._user_playlist = []
        self._user_playlist_id = None
        # We are in all artists
        if (genre_ids and genre_ids[0] == Type.ALL) or\
           (artist_ids and artist_ids[0] == Type.ALL):
            self._albums = Lp().albums.get_compilations()
            self._albums += Lp().albums.get_ids()
        # We are in populars view, add popular albums
        elif genre_ids and genre_ids[0] == Type.POPULARS:
            if self._shuffle in [Shuffle.TRACKS_ARTIST, Shuffle.ALBUMS_ARTIST]:
                self._albums = []
                self.next_track = Track()
                for album_id in Lp().albums.get_populars():
                    if Lp().albums.get_artist_id(album_id) == \
                            album.artist_id:
                        self._albums.append(album_id)
            else:
                self._albums = Lp().albums.get_populars()
        # We are in recents view, add recent albums
        elif genre_ids and genre_ids[0] == Type.RECENTS:
            self._albums = Lp().albums.get_recents()
        # We are in randoms view, add random albums
        elif genre_ids and genre_ids[0] == Type.RANDOMS:
            self._albums = Lp().albums.get_cached_randoms()
        # We are in compilation view without genre
        elif genre_ids and genre_ids[0] == Type.COMPILATIONS:
            self._albums = Lp().albums.get_compilations()
        # Random tracks/albums for artist
        elif self._shuffle in [Shuffle.TRACKS_ARTIST, Shuffle.ALBUMS_ARTIST]:
            self._albums = Lp().albums.get_ids([album.artist_id], genre_ids)
        # Add all albums for genre
        else:
            if not artist_ids:
                self._albums = Lp().albums.get_compilations(genre_ids)
            self._albums += Lp().albums.get_ids(artist_ids, genre_ids)

        album.set_genre(genre_ids)
        if track_id in album.tracks_ids:
            self.context.artist_ids = artist_ids
            self.context.genre_ids = genre_ids
            # Shuffle album list if needed
            self._shuffle_albums()
        else:  # Error
            self.stop()
示例#6
0
    def __init__(self, name, radios_manager):
        """
            Init Popover
            @param name as string
            @param radios_manager as RadiosManager
        """
        Gtk.Popover.__init__(self)
        self.connect("map", self._on_map)
        self.connect("unmap", self._on_unmap)
        self._name = name
        self._radios_manager = radios_manager
        self._start = 0
        self._orig_pixbufs = {}

        self._stack = Gtk.Stack()
        self._stack.set_transition_duration(1000)
        self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
        self._stack.show()

        builder = Gtk.Builder()
        builder.add_from_resource("/org/gnome/Lollypop/RadioPopover.ui")
        builder.connect_signals(self)

        self._view = Gtk.FlowBox()
        self._view.set_selection_mode(Gtk.SelectionMode.NONE)
        self._view.connect("child-activated", self._on_activate)
        self._view.set_max_children_per_line(100)
        self._view.set_property("row-spacing", 10)
        self._view.show()

        builder.get_object("viewport").add(self._view)

        self._name_entry = builder.get_object("name")
        self._uri_entry = builder.get_object("uri")
        self._btn_add_modify = builder.get_object("btn_add_modify")
        self._spinner = builder.get_object("spinner")
        self._stack.add_named(builder.get_object("spinner-grid"), "spinner")
        self._stack.add_named(builder.get_object("notfound"), "notfound")
        self._stack.add_named(builder.get_object("logo"), "logo")
        self._stack.add_named(builder.get_object("widget"), "widget")
        self._stack.set_visible_child_name("widget")
        self.add(self._stack)

        track = Track()
        track.set_radio(name, "")
        rating = RatingWidget(track)
        rating.show()
        builder.get_object("widget").attach(rating, 0, 2, 2, 1)

        if self._name == "":
            builder.get_object("btn_add_modify").set_label(_("Add"))
        else:
            builder.get_object("btn_add_modify").set_label(_("Modify"))
            builder.get_object("btn_delete").show()
            self._name_entry.set_text(self._name)
            url = self._radios_manager.get_url(self._name)
            if url:
                self._uri_entry.set_text(url)
示例#7
0
 def restore_state(self):
     """
         Restore player state
     """
     if Lp().settings.get_value('save-state'):
         track_id = Lp().settings.get_value('track-id').get_int32()
         playlist_ids = Lp().settings.get_value('playlist-ids')
         if playlist_ids and playlist_ids[0] == Type.RADIOS:
             radios = Radios()
             track = Track()
             name = radios.get_name(track_id)
             url = radios.get_url(name)
             track.set_radio(name, url)
             self.load(track)
         elif Lp().tracks.get_path(track_id) != "":
             track = Track(track_id)
             self._load_track(track)
             if playlist_ids:
                 try:
                     pids = []
                     for playlist_id in playlist_ids:
                         pids.append(int(playlist_id))
                     track_ids = []
                     for playlist_id in playlist_ids:
                         if playlist_id == Type.POPULARS:
                             tracks = Lp().tracks.get_populars()
                         elif playlist_id == Type.RECENTS:
                             tracks = Lp().tracks.get_recently_listened_to()
                         elif playlist_id == Type.NEVER:
                             tracks = Lp().tracks.get_never_listened_to()
                         elif playlist_id == Type.RANDOMS:
                             tracks = Lp().tracks.get_randoms()
                         else:
                             tracks = Lp().playlists.get_tracks_ids(
                                                                playlist_id)
                         for track_id in tracks:
                             if track_id not in track_ids:
                                 track_ids.append(track_id)
                         self.populate_user_playlist_by_tracks(track_ids,
                                                               pids)
                 except:
                     pass  # User set non int in gsettings
             else:
                 self._albums = Lp().artists.get_albums(
                                                     track.album_artist_ids)
                 for album_id in self._albums:
                     self.context.genre_ids[album_id] = []
                     self.context.artist_ids[album_id] = []
             self.set_next()
             self.set_prev()
             if Lp().settings.get_value('repeat'):
                 self.context.next = NextContext.NONE
             else:
                 self.context.next = NextContext.STOP_ALL
             self.emit('current-changed')
         else:
             print("Player::restore_state(): track missing")
示例#8
0
 def _on_play_press_event(self, widget, event):
     """
         Play radio
         @param: widget as Gtk.EventBox
         @param: event as Gdk.Event
     """
     url = self._radios_manager.get_url(self._name)
     if url:
         track = Track()
         track.set_radio(self._name, url)
         Lp().player.load(track)
示例#9
0
 def _on_album_activated(self, flowbox, child):
     """
         Play album
         @param flowbox as Gtk.Flowbox
         @child as Gtk.FlowboxChild
     """
     name = child.get_child().get_name()
     url = self._radios_manager.get_url(name)
     if url:
         track = Track()
         track.set_radio(name, url)
         Lp().player.load(track)
示例#10
0
 def _on_album_activated(self, flowbox, child):
     """
         Play album
         @param flowbox as Gtk.Flowbox
         @child as Gtk.FlowboxChild
     """
     name = child.get_child().get_name()
     uris = self._radios_manager.get_tracks(name)
     if len(uris) > 0:
         track = Track()
         track.set_radio(name, uris[0])
         Lp.player.load(track)
示例#11
0
文件: player.py 项目: sebfag/lollypop
    def set_albums(self, track_id, artist_id, genre_id):
        """
            Set album list (for next/prev)
            @param track id as int
            @param artist id as int
            @param genre id as int
        """
        # Invalid track
        if track_id is None:
            return
        album = Track(track_id).album
        self._albums = None
        ShufflePlayer.reset_history(self)

        # We are not playing a user playlist anymore
        self._user_playlist = None
        self._user_playlist_id = None
        # We are in all artists
        if genre_id == Type.ALL or artist_id == Type.ALL:
            self._albums = Lp.albums.get_compilations(Type.ALL)
            self._albums += Lp.albums.get_ids()
        # We are in populars view, add popular albums
        elif genre_id == Type.POPULARS:
            self._albums = Lp.albums.get_populars()
        # We are in recents view, add recent albums
        elif genre_id == Type.RECENTS:
            self._albums = Lp.albums.get_recents()
        # We are in randoms view, add random albums
        elif genre_id == Type.RANDOMS:
            self._albums = Lp.albums.get_cached_randoms()
        # We are in compilation view without genre
        elif genre_id == Type.COMPILATIONS:
            self._albums = Lp.albums.get_compilations(None)
        # Random tracks/albums for artist
        elif self._shuffle in [Shuffle.TRACKS_ARTIST, Shuffle.ALBUMS_ARTIST]:
            self._albums = Lp.albums.get_ids(artist_id, genre_id)
        # Add all albums for genre
        else:
            self._albums = Lp.albums.get_compilations(genre_id)
            self._albums += Lp.albums.get_ids(None, genre_id)

        album.set_genre(genre_id)
        if track_id in album.tracks_ids:
            self.context.position = album.tracks_ids.index(track_id)
            self.context.genre_id = genre_id
            # Shuffle album list if needed
            self._shuffle_albums()
        else:  # Error
            self.stop()
示例#12
0
 def _load_web(self, track, play=True):
     """
         Load track url and play it
         @param track as Track
         @param play as bool
         @return True if loading
     """
     if not get_network_available():
         # Force widgets to update (spinners)
         self.emit('current-changed')
         return False
     try:
         from lollypop.web import Web
         if play:
             self.emit('loading-changed', True)
         t = Thread(target=Web.play_track,
                    args=(track, play, self.__set_gv_uri))
         t.daemon = True
         t.start()
         return True
     except Exception as e:
         self._current_track = Track()
         self.stop()
         self.emit('current-changed')
         if Lp().notify is not None:
             Lp().notify.send(str(e), track.uri)
         print("PlayerBin::_load_web()", e)
示例#13
0
 def __init__(self, rowid, num):
     """
         Init row widgets
         @param rowid as int
         @param num as int
         @param show loved as bool
     """
     # We do not use Gtk.Builder for speed reasons
     Gtk.ListBoxRow.__init__(self)
     self.set_sensitive(False)
     self._track = Track(rowid)
     self._number = num
     self._indicator = IndicatorWidget(self._track.id)
     self.set_indicator(Lp().player.current_track.id == self._track.id,
                        utils.is_loved(self._track.id))
     self.set_sensitive(True)
     self._row_widget = Gtk.EventBox()
     self._row_widget.connect('button-press-event', self._on_button_press)
     self._row_widget.connect('enter-notify-event', self._on_enter_notify)
     self._grid = Gtk.Grid()
     self._grid.set_column_spacing(5)
     self._row_widget.add(self._grid)
     self._title_label = Gtk.Label.new(self._track.formated_name())
     self._title_label.set_use_markup(True)
     self._title_label.set_property('has-tooltip', True)
     self._title_label.connect('query-tooltip',
                               self._on_title_query_tooltip)
     self._title_label.set_property('hexpand', True)
     self._title_label.set_property('halign', Gtk.Align.START)
     self._title_label.set_ellipsize(Pango.EllipsizeMode.END)
     self._duration_label = Gtk.Label.new(
                                    seconds_to_string(self._track.duration))
     self._duration_label.get_style_context().add_class('dim-label')
     self._num_label = Gtk.Label()
     self._num_label.set_ellipsize(Pango.EllipsizeMode.END)
     self._num_label.set_property('valign', Gtk.Align.CENTER)
     self._num_label.set_width_chars(4)
     self._num_label.get_style_context().add_class('dim-label')
     self.update_num_label()
     self._menu_image = Gtk.Image.new()
     self._menu_image.set_opacity(0.2)
     self._menu_button = Gtk.Button.new()
     self._menu_button.set_relief(Gtk.ReliefStyle.NONE)
     self._menu_button.get_style_context().add_class('menu-button')
     self._menu_button.get_style_context().add_class('track-menu-button')
     self._menu_button.set_image(self._menu_image)
     self._menu_button.connect('clicked', self._on_button_clicked)
     self._grid.add(self._num_label)
     self._grid.add(self._title_label)
     self._grid.add(self._duration_label)
     # TODO Remove this later
     if Gtk.get_minor_version() > 16:
         self._grid.add(self._menu_button)
     else:
         self.connect('map', self._on_map)
     self.add(self._row_widget)
     self.get_style_context().add_class('trackrow')
示例#14
0
 def load_external(self, uri, name=''):
     """
         Load external tracks
         @param uri as str
         @param name as string
     """
     track = Track()
     track.set_album_artists([name])
     track.set_uri(uri)
     if track.uri.startswith('file://'):
         track.id = Type.EXTERNALS
         track.name = GLib.path_get_basename(GLib.filename_from_uri(uri)[0])
     else:
         track.name = uri
         track.id = Type.RADIOS
     self._external_tracks.append(track)
示例#15
0
文件: mpd.py 项目: oleastre/lollypop
 def _sticker(self, cmd_args):
     """
         Send stickers
         @syntax sticker [get|set] song rating
         @param args as str
         @return msg as str
     """
     args = self._get_args(cmd_args)
     msg = ""
     if args[0].find("get song ") != -1 and\
             args[2].find("rating") != -1:
         track_id = Lp().tracks.get_id_by_path(args[1])
         track = Track(track_id)
         msg = "sticker: rating=%s\n" % int(track.get_popularity()*2)
     elif args[0].find("set song") != -1 and\
             args[2].find("rating") != -1:
         track_id = Lp().tracks.get_id_by_path(args[1])
         track = Track(track_id)
         track.set_popularity(int(args[3])/2)
     return msg
示例#16
0
 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)
示例#17
0
 def _on_activated(self, widget, track_id):
     """
         On track activation, play track
         @param widget as TracksWidget
         @param track id as int
     """
     # Play track with no album, force repeat on track
     if self._button_state & Gdk.ModifierType.SHIFT_MASK:
         Lp().player.clear_albums()
         Lp().player.load(Track(track_id))
     else:
         Lp().player.context.next = NextContext.NONE
         if not Lp().player.is_party():
             if len(self._artist_ids) > 1:
                 Lp().player.set_albums(track_id, self._artist_ids,
                                        self._album.genre_ids)
             else:
                 Lp().player.set_albums(track_id, [], self._album.genre_ids)
         Lp().player.load(Track(track_id))
         if self._button_state & Gdk.ModifierType.CONTROL_MASK:
             Lp().player.context.next = NextContext.STOP_TRACK
示例#18
0
文件: mpd.py 项目: goncalor/lollypop
 def _sticker(self, cmd_args):
     """
         Send stickers
         @syntax sticker [get|set] song rating
         @param args as str
         @return msg as str
     """
     args = self._get_args(cmd_args)
     msg = ""
     print(args)
     if args[0] == "get" and args[1] == "song" and\
             args[3] == "rating":
         track_id = Lp().tracks.get_id_by_path(args[2])
         track = Track(track_id)
         msg = "sticker: rating=%s\n" % int((track.get_popularity()-0.5)*2)
     elif args[0] == "set" and args[1] == "song" and\
             args[3] == "rating":
         track_id = Lp().tracks.get_id_by_path(args[2])
         track = Track(track_id)
         track.set_popularity(int(args[4])/2)
     return msg
示例#19
0
 def next(self, force):
     """
         Next Track
         @param force as bool
         @return Track
     """
     track = Track()
     if force:
         current_track = self._next_track
     else:
         current_track = self._current_track
     if self._user_playlist and\
        current_track.id in self._user_playlist:
         idx = self._user_playlist.index(current_track.id)
         if idx + 1 >= len(self._user_playlist):
             self._next_context = NextContext.STOP
             idx = 0
         else:
             idx += 1
         track = Track(self._user_playlist[idx])
     return track
示例#20
0
 def update_headers(self, prev_album_id=None):
     """
         Update headers
         @param previous album id as int
     """
     for child in self.get_children():
         track = Track(child.id)
         if track.album.id == prev_album_id:
             child.show_headers(False)
         else:
             child.show_headers(True)
         prev_album_id = track.album.id
示例#21
0
 def set_id(self, rowid, is_track):
     """
         Set row id
         @param rowid as int
         @param is track as bool
     """
     self._id = rowid
     self._is_track = is_track
     if self._is_track:
         self._title.set_text("♫ " + Track(self._id).name)
     else:
         self._title.set_text(Album(self._id).name)
示例#22
0
    def _remove_from_device(self, playlists):
        """
            Delete files not available in playlist
        """
        track_uris = []
        tracks_ids = []

        # Get tracks ids
        for playlist in playlists:
            tracks_ids += Lp().playlists.get_tracks_ids(playlist)

        # Get tracks uris
        for track_id in tracks_ids:
            if not self._syncing:
                self._fraction = 1.0
                self._in_thread = False
                return
            track = Track(track_id)
            album_name = GLib.uri_escape_string(track.album_name.lower(), "",
                                                False)
            artist_name = GLib.uri_escape_string(track.artist.lower(), "",
                                                 False)
            album_uri = "%s/tracks/%s_%s" % (self._uri, artist_name,
                                             album_name)
            track_name = GLib.uri_escape_string(GLib.basename(track.path), "",
                                                False)
            # Check extension, if not mp3, convert
            ext = os.path.splitext(track.path)[1]
            if ext != ".mp3" and self._convert:
                track_name = track_name.replace(ext, ".mp3")
            on_disk = Gio.File.new_for_path(track.path)
            info = on_disk.query_info('time::modified',
                                      Gio.FileQueryInfoFlags.NONE, None)
            # Prefix track with mtime to make sure updating it later
            mtime = info.get_attribute_as_string('time::modified')
            dst_uri = "%s/%s_%s" % (album_uri, mtime, track_name)
            track_uris.append(dst_uri)

        on_mtp_files = self._get_tracks_files()

        # Delete file on device and not in playlists
        for uri in on_mtp_files:
            if not self._syncing:
                self._fraction = 1.0
                self._in_thread = False
                return

            if uri not in track_uris and uri not in self._copied_art_uris:
                to_delete = Gio.File.new_for_uri(uri)
                self._retry(to_delete.delete, (None, ))
            self._done += 1
            self._fraction = self._done / self._total
 def next(self):
     """
         Next shuffle track
         @return Track
     """
     track = Track()
     if self._shuffle == Shuffle.TRACKS or self.__is_party:
         if self.shuffle_has_next:
             track = self.__history.next.value
         elif self._albums or (self._playlist_tracks
                               and self._shuffle == Shuffle.TRACKS):
             track = self.__get_next()
     return track
示例#24
0
 def update_playlist():
     # Save playlist in db only if one playlist visible
     if len(self.__playlist_ids) == 1 and self.__playlist_ids[0] >= 0:
         Lp().playlists.clear(self.__playlist_ids[0], False)
         tracks = []
         for track_id in self.__tracks_left + self.__tracks_right:
             tracks.append(Track(track_id))
         Lp().playlists.add_tracks(self.__playlist_ids[0], tracks,
                                   False)
     if not (set(self.__playlist_ids) -
             set(Lp().player.get_user_playlist_ids())):
         Lp().player.update_user_playlist(self.__tracks_left +
                                          self.__tracks_right)
示例#25
0
 def next(self):
     """
         Next Track
         @return Track
     """
     repeat = App().settings.get_enum("repeat")
     if repeat == Repeat.TRACK:
         return self._current_playback_track
     track = Track()
     current_track_id = self._current_playback_track.id
     track_ids = self.playlist_track_ids
     if track_ids and current_track_id in track_ids:
         idx = track_ids.index(current_track_id)
         if idx + 1 >= len(track_ids):
             if repeat == Repeat.ALL:
                 idx = 0
             else:
                 return Track()
         else:
             idx += 1
         track = self._playlist_tracks[idx]
     return track
示例#26
0
 def prev(self):
     """
         Prev track id
         @return Track
     """
     repeat = App().settings.get_enum("repeat")
     if repeat == Repeat.TRACK:
         return self._current_playback_track
     track = Track()
     current_track_id = self._current_playback_track.id
     track_ids = self.playlist_track_ids
     if track_ids and current_track_id in track_ids:
         idx = track_ids.index(current_track_id)
         if idx - 1 < 0:
             if repeat == Repeat.ALL:
                 idx = len(track_ids) - 1
             else:
                 return Track()
         else:
             idx -= 1
         track = self._playlist_tracks[idx]
     return track
示例#27
0
 def restore_state(self):
     """
         Restore player state
     """
     try:
         if Lp().settings.get_value('save-state'):
             track_id = load(open(DataPath + "/track_id.bin", "rb"))
             playlist_ids = load(open(DataPath + "/playlist_ids.bin", "rb"))
             if playlist_ids and playlist_ids[0] == Type.RADIOS:
                 radios = Radios()
                 track = Track()
                 name = radios.get_name(track_id)
                 url = radios.get_url(name)
                 track.set_radio(name, url)
                 self.load(track)
             elif Lp().tracks.get_uri(track_id) != "":
                 track = Track(track_id)
                 if Lp().notify is not None:
                     Lp().notify.inhibit()
                 self._load_track(track)
                 # We set this initial state
                 # because seek while failed otherwise
                 self.pause()
                 if playlist_ids:
                     pids = []
                     for playlist_id in playlist_ids:
                         pids.append(int(playlist_id))
                     track_ids = []
                     for playlist_id in playlist_ids:
                         if playlist_id == Type.POPULARS:
                             tracks = Lp().tracks.get_populars()
                         elif playlist_id == Type.RECENTS:
                             tracks = Lp().tracks.get_recently_listened_to()
                         elif playlist_id == Type.NEVER:
                             tracks = Lp().tracks.get_never_listened_to()
                         elif playlist_id == Type.RANDOMS:
                             tracks = Lp().tracks.get_randoms()
                         else:
                             tracks = Lp().playlists.get_track_ids(
                                 playlist_id)
                         for track_id in tracks:
                             if track_id not in track_ids:
                                 track_ids.append(track_id)
                         self.populate_user_playlist_by_tracks(
                             track_ids, pids)
                 else:
                     self._albums = load(
                         open(DataPath + "/albums.bin", "rb"))
                     self.shuffle_albums(True)
                     self._context.genre_ids = load(
                         open(DataPath + "/genre_ids.bin", "rb"))
                     self._context.artist_ids = load(
                         open(DataPath + "/artist_ids.bin", "rb"))
                 self.set_next()
                 self.set_prev()
             else:
                 print("Player::restore_state(): track missing")
     except Exception as e:
         print("Player::restore_state()", e)
示例#28
0
 def __init__(self, object):
     """
         Init edit menu
         @param object as Album/Track
     """
     # Ignore genre_ids/artist_ids
     if isinstance(object, Album):
         obj = Album(object.id)
     else:
         obj = Track(object.id)
     BaseMenu.__init__(self, obj)
     if App().art.tag_editor:
         self.__set_edit_actions()
示例#29
0
 def _findadd(self, cmd_args):
     """
         Find tracks and add them to playlist
         @syntax findadd filter value
         @param args as str
         @return msg as str
     """
     tracks = []
     for track_id in self._find_tracks(cmd_args):
         tracks.append(Track(track_id))
     if tracks:
         Lp().playlists.add_tracks(Type.MPD, tracks, False)
     return ""
示例#30
0
 def _clear(self, cmd_args):
     """
         Clear mpd playlist
         @syntax clear
         @param args as str
         @return msg as str
     """
     Lp().playlists.clear(Type.MPD, False)
     Lp().player.set_user_playlist_by_id(Type.NONE)
     GLib.idle_add(Lp().player.stop)
     Lp().player.current_track = Track()
     GLib.idle_add(Lp().player.emit, 'current-changed')
     return ""
示例#31
0
 def load_external(self, filename, name=''):
     """
         Load external tracks
         @param filename as str
         @param name as string
     """
     try:
         uri = GLib.filename_to_uri(filename)
     except:
         uri = filename
     track = Track()
     track.name = GLib.path_get_basename(GLib.filename_from_uri(uri)[0])
     track.set_album_artists([name])
     track.set_uri(uri)
     if track.uri.startswith('file://'):
         track.id = Type.EXTERNALS
     else:
         track.id = Type.RADIOS
     self._external_tracks.append(track)
示例#32
0
 def _play_search(self, object_id=None, is_track=True):
     """
         Play tracks based on search
         @param started object id as int
         @param is track as bool
     """
     tracks = []
     track_id = None
     for child in self._view.get_children():
         if child.is_track:
             tracks.append(Track(child.id))
         else:
             album_tracks = Lp().albums.get_tracks(child.id, None)
             if not is_track and child.id == object_id and album_tracks:
                 track_id = album_tracks[0]
             for tid in album_tracks:
                 tracks.append(Track(tid))
     if tracks:
         if object_id is not None and is_track:
             track_id = object_id
         elif track_id is None:
             track_id = tracks[0].id
         GLib.idle_add(self._set_user_playlist, tracks, track_id)
 def __get_playlists_random(self):
     """
         Return a track from current playlist
         @return Track
     """
     for track in sorted(self._playlist_tracks,
                         key=lambda *args: random.random()):
         # Ignore current track, not an issue if playing one track
         # in shuffle because LinearPlayer will handle next()
         if track != App().player.current_track and (
                 track.album not in self.__already_played_tracks.keys()
                 or track not in self.__already_played_tracks[track.album]):
             return track
     return Track()
 def __on_loved_playlist_changed(self, widget, playlist_id, uri, *ignore):
     """
         Updates the loved icon
         @param playlist as Playlist
         @param playlist id as int
         @param track id as int
     """
     if playlist_id != Type.LOVED:
         return
     track_id = App().tracks.get_id_by_uri(uri)
     for row in self.get_children():
         if track_id == row.track.id:
             row.set_indicator(track_id == App().player.current_track.id,
                               Track(track_id).loved)
示例#35
0
 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)
示例#36
0
 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)
示例#37
0
 def set_row(self, row, track_id, pos, show_cover=False):
     """
         Set row content
         @param row as Row
         @param track id as int
         @param pos as position
         @param show cover as bool
     """
     track = Track(track_id)
     row.show_indicator(Lp().player.current_track.id == track_id,
                        utils.is_loved(track_id))
     row.set_number(pos)
     self._update_pos_label(row, track_id)
     row.set_title_label(track.formated_name())
     row.set_duration_label(seconds_to_string(track.duration))
     row.set_id(track_id)
     if show_cover:
         surface = Lp().art.get_album_artwork(
                     track.album,
                     ArtSize.MEDIUM*row.get_scale_factor())
         row.set_cover(surface, track.album.name)
         del surface
         row.show_header(True)
示例#38
0
 def _on_play_all_press_event(self, widget, event):
     """
         Play album with context
         @param: widget as Gtk.EventBox
         @param: event as Gdk.Event
     """
     self._show_append(False)
     if Lp().player.is_party():
         Lp().player.set_party(False)
     track = Track(self._album.tracks_ids[0])
     Lp().player.load(track)
     Lp().player.set_albums(track.id, self._artist_ids,
                            self._album.genre_ids)
     return True
示例#39
0
 def load_smart():
     tracks = []
     request = App().playlists.get_smart_sql(playlist_ids[0])
     ids = App().db.execute(request)
     for id in ids:
         track = Track(id)
         # Smart playlist may report invalid tracks
         # An album always have an artist so check
         # object is valid. Others Lollypop widgets assume
         # objects are valid
         if not track.album.artist_ids:
             continue
         tracks.append(track)
     return tracks
示例#40
0
 def __init__(self):
     """
         Init base player variables
     """
     # In case of multiple subclassing,
     # do not init variables for every subclass
     if not hasattr(self, '_albums'):
         GObject.GObject.__init__(self)
         self._base_init = True
         # A user playlist used as current playlist
         self._user_playlist = None
         # Used by shuffle tracks to restore user playlist before shuffle
         self._user_playlist_backup = None
         self.current_track = Track()
         self.next_track = Track()
         self.prev_track = Track()
         self.context = PlayContext()
         # Albums in current playlist
         self._albums = None
         # Current shuffle mode
         self._shuffle = Lp.settings.get_enum('shuffle')
         # For tracks from the cmd line
         self._external_tracks = []
示例#41
0
 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)
示例#42
0
 def restore_state(self):
     """
         Restore player state
     """
     if Lp().settings.get_value('save-state'):
         track_id = Lp().settings.get_value('track-id').get_int32()
         playlist_ids = Lp().settings.get_value('playlist-ids')
         if playlist_ids and playlist_ids[0] == Type.RADIOS:
             radios = Radios()
             track = Track()
             name = radios.get_name(track_id)
             url = radios.get_url(name)
             track.set_radio(name, url)
             self.load(track)
         elif Lp().tracks.get_path(track_id) != "":
             track = Track(track_id)
             self._load_track(track)
             if playlist_ids:
                 try:
                     pids = []
                     for playlist_id in playlist_ids:
                         pids.append(int(playlist_id))
                     track_ids = []
                     for playlist_id in playlist_ids:
                         if playlist_id == Type.POPULARS:
                             tracks = Lp().tracks.get_populars()
                         elif playlist_id == Type.RECENTS:
                             tracks = Lp().tracks.get_recently_listened_to()
                         elif playlist_id == Type.NEVER:
                             tracks = Lp().tracks.get_never_listened_to()
                         elif playlist_id == Type.RANDOMS:
                             tracks = Lp().tracks.get_randoms()
                         else:
                             tracks = Lp().playlists.get_tracks_ids(
                                                                playlist_id)
                         for track_id in tracks:
                             if track_id not in track_ids:
                                 track_ids.append(track_id)
                         self.populate_user_playlist_by_tracks(track_ids,
                                                               pids)
                 except:
                     pass  # User set non int in gsettings
             else:
                 self._albums = Lp().artists.get_albums(
                                                     track.album_artist_ids)
                 for album_id in self._albums:
                     self.context.genre_ids[album_id] = []
                     self.context.artist_ids[album_id] = []
             self.set_next()
             self.set_prev()
             if Lp().settings.get_value('repeat'):
                 self.context.next = NextContext.NONE
             else:
                 self.context.next = NextContext.STOP_ALL
             self.emit('current-changed')
         else:
             print("Player::restore_state(): track missing")
示例#43
0
 def _on_play_clicked(self, widget):
     """
         Play artist albums
     """
     try:
         if Lp().player.is_party:
             Lp().player.set_party(False)
         album_id = Lp().albums.get_ids(self._artist_ids,
                                        self._genre_ids)[0]
         track = Track(Album(album_id).track_ids[0])
         Lp().player.load(track)
         Lp().player.set_albums(track.id, self._artist_ids, self._genre_ids)
         self.__set_add_icon()
     except:
         pass  # Artist not available anymore for this context
示例#44
0
    def prev(self):
        """
            Return prev radio name, uri
            @return Track
        """
        track = Track()
        if self._current_track.id != Type.RADIOS or not self.__radios:
            return track

        i = len(self.__radios) - 1
        for (name, url) in reversed(self.__radios):
            i -= 1
            if self._current_track.album_artists[0] == name:
                break

        # Get prev radio
        if i < 0:
            i = len(self.__radios) - 1

        name = self.__radios[i][0]
        url = self.__radios[i][1]
        if url:
            track.set_radio(name, url)
        return track
示例#45
0
    def next(self):
        """
            Return next radio name, uri
            @return Track
        """
        track = Track()
        if self._current_track.id != Type.RADIOS or not self.__radios:
            return track

        i = 0
        for (name, url) in self.__radios:
            i += 1
            if self._current_track.album_artists[0] == name:
                break

        # Get next radio
        if i >= len(self.__radios):
            i = 0

        name = self.__radios[i][0]
        url = self.__radios[i][1]
        if url:
            track.set_radio(name, url)
        return track
示例#46
0
 def load():
     tracks = []
     all_ids = []
     for playlist_id in playlist_ids:
         if playlist_id == Type.LOVED:
             ids = App().tracks.get_loved_track_ids()
         else:
             ids = App().playlists.get_track_ids(playlist_id)
         for id in ids:
             if id in all_ids:
                 continue
             all_ids.append(id)
             track = Track(id)
             tracks.append(track)
     return tracks
示例#47
0
 def restore_state(self):
     """
         Restore player state
     """
     track_id = Lp().settings.get_value('track-id').get_int32()
     if Lp().settings.get_value('save-state') and track_id > 0:
         path = Lp().tracks.get_path(track_id)
         if path != "":
             self._load_track(Track(track_id))
             self.set_albums(track_id, [Type.ALL], [Type.ALL])
             self.set_next()
             self.set_prev()
             self.emit('current-changed')
         else:
             print("Player::restore_state(): track missing")
示例#48
0
 def load_external(self, uri, name=''):
     """
         Load external tracks
         @param uri as str
         @param name as string
     """
     try:
         uri = GLib.filename_to_uri(uri)
     except:
         pass
     track = Track()
     track.set_album_artists([name])
     track.set_uri(uri)
     if track.uri.startswith('file://'):
         track.id = Type.EXTERNALS
     else:
         track.id = Type.RADIOS
     self._external_tracks.append(track)
示例#49
0
文件: player.py 项目: sgnls/lollypop
    def set_albums(self, track_id, artist_ids, genre_ids):
        """
            Set album list (for next/prev)
            @param track id as int
            @param artist id as int
            @param genre id as int
        """
        # Invalid track
        if track_id is None:
            return
        album = Track(track_id).album
        album.set_genre(genre_ids)
        self._albums = []
        self.context.genre_ids = {}
        ShufflePlayer.reset_history(self)

        # We are not playing a user playlist anymore
        self._user_playlist = []
        self._user_playlist_ids = []
        # We are in all artists
        if (genre_ids and genre_ids[0] == Type.ALL) or\
           (artist_ids and artist_ids[0] == Type.ALL):
            if artist_ids and artist_ids[0] != Type.ALL:
                self._albums += Lp().albums.get_ids(artist_ids)
            else:
                if Lp().settings.get_value('show-compilations'):
                    self._albums = Lp().albums.get_compilations()
                self._albums += Lp().albums.get_ids()
        # We are in populars view, add popular albums
        elif genre_ids and genre_ids[0] == Type.POPULARS:
            self._albums = Lp().albums.get_populars()
        # We are in recents view, add recent albums
        elif genre_ids and genre_ids[0] == Type.RECENTS:
            self._albums = Lp().albums.get_recents()
        # We are in randoms view, add random albums
        elif genre_ids and genre_ids[0] == Type.RANDOMS:
            self._albums = Lp().albums.get_cached_randoms()
        # We are in compilation view without genre
        elif genre_ids and genre_ids[0] == Type.COMPILATIONS:
            self._albums = Lp().albums.get_compilations()
        # Add albums for artists/genres
        else:
            # If we are not in compilation view and show compilation is on,
            # add compilations
            if (not artist_ids or artist_ids[0] != Type.COMPILATIONS) and\
               Lp().settings.get_value('show-compilations'):
                self._albums += Lp().albums.get_compilations(genre_ids)
            self._albums += Lp().albums.get_ids(artist_ids, genre_ids)

        if Lp().settings.get_value('repeat'):
            self.context.next = NextContext.NONE
        else:
            self.context.next = NextContext.STOP_ALL
        # We do not store genre_ids for ALL/POPULARS/...
        if genre_ids and genre_ids[0] < 0:
            genre_ids = []
        # Set context for each album
        for album_id in self._albums:
            self.context.genre_ids[album_id] = genre_ids
        # Shuffle album list if needed
        self.shuffle_albums(True)
示例#50
0
class BinPlayer(BasePlayer):
    """
        Gstreamer bin player
    """

    def __init__(self):
        """
            Init playbin
        """
        Gst.init(None)
        BasePlayer.__init__(self)
        self.__codecs = Codecs()
        self._playbin = self.__playbin1 = Gst.ElementFactory.make(
                                                           'playbin', 'player')
        self.__playbin2 = Gst.ElementFactory.make('playbin', 'player')
        self.__preview = None
        self._plugins = self._plugins1 = PluginsPlayer(self.__playbin1)
        self._plugins2 = PluginsPlayer(self.__playbin2)
        self._playbin.connect('notify::volume', self.__on_volume_changed)
        for playbin in [self.__playbin1, self.__playbin2]:
            flags = playbin.get_property("flags")
            flags &= ~GstPlayFlags.GST_PLAY_FLAG_VIDEO
            playbin.set_property('flags', flags)
            playbin.set_property('buffer-size', 5 << 20)
            playbin.set_property('buffer-duration', 10 * Gst.SECOND)
            playbin.connect('about-to-finish',
                            self.__on_stream_about_to_finish)
            bus = playbin.get_bus()
            bus.add_signal_watch()
            bus.connect('message::error', self.__on_bus_error)
            bus.connect('message::eos', self.__on_bus_eos)
            bus.connect('message::element', self.__on_bus_element)
            bus.connect('message::stream-start', self._on_stream_start)
            bus.connect("message::tag", self.__on_bus_message_tag)
        self._start_time = 0

    @property
    def preview(self):
        """
            Get a preview bin
            @return Gst.Element
        """
        if self.__preview is None:
            self.__preview = Gst.ElementFactory.make('playbin', 'player')
            PluginsPlayer(self.__preview)
            self.set_preview_output()
        return self.__preview

    def set_preview_output(self):
        """
            Set preview output
        """
        if self.__preview is not None:
            output = Lp().settings.get_value('preview-output').get_string()
            pulse = Gst.ElementFactory.make('pulsesink', 'output')
            if pulse is None:
                pulse = Gst.ElementFactory.make('alsasink', 'output')
            if pulse is not None:
                pulse.set_property('device', output)
                self.__preview.set_property('audio-sink', pulse)

    def get_status(self):
        """
            Playback status
            @return Gstreamer state
        """
        ok, state, pending = self._playbin.get_state(Gst.CLOCK_TIME_NONE)
        if ok == Gst.StateChangeReturn.ASYNC:
            state = pending
        elif (ok != Gst.StateChangeReturn.SUCCESS):
            state = Gst.State.NULL
        return state

    def load(self, track):
        """
            Stop current track, load track id and play it
            @param track as Track
        """
        if self._crossfading and\
           self._current_track.id is not None and\
           self.is_playing and\
           self._current_track.id != Type.RADIOS:
            duration = Lp().settings.get_value('mix-duration').get_int32()
            self.__do_crossfade(duration, track, False)
        else:
            self.__load(track)

    def play(self):
        """
            Change player state to PLAYING
        """
        # No current playback, song in queue
        if self._current_track.id is None:
            if self._next_track.id is not None:
                self.load(self._next_track)
        else:
            self._playbin.set_state(Gst.State.PLAYING)
            self.emit("status-changed")

    def pause(self):
        """
            Change player state to PAUSED
        """
        if self._current_track.id == Type.RADIOS:
            self._playbin.set_state(Gst.State.NULL)
        else:
            self._playbin.set_state(Gst.State.PAUSED)
        self.emit("status-changed")

    def stop(self):
        """
            Change player state to STOPPED
        """
        self._playbin.set_state(Gst.State.NULL)
        self.emit("status-changed")

    def stop_all(self):
        """
            Stop all bins, lollypop should quit now
        """
        # Stop
        self.__playbin1.set_state(Gst.State.NULL)
        self.__playbin2.set_state(Gst.State.NULL)

    def play_pause(self):
        """
            Set playing if paused
            Set paused if playing
        """
        if self.is_playing:
            self.pause()
        else:
            self.play()

    def seek(self, position):
        """
            Seek current track to position
            @param position as seconds
        """
        if self.locked or self._current_track.id is None:
            return
        # Seems gstreamer doesn't like seeking to end, sometimes
        # doesn't go to next track
        if position >= self._current_track.duration:
            self.next()
        else:
            self._playbin.seek_simple(Gst.Format.TIME,
                                      Gst.SeekFlags.FLUSH |
                                      Gst.SeekFlags.KEY_UNIT,
                                      position * Gst.SECOND)
            self.emit("seeked", position)

    @property
    def is_playing(self):
        """
            True if player is playing
            @return bool
        """
        ok, state, pending = self._playbin.get_state(Gst.CLOCK_TIME_NONE)
        if ok == Gst.StateChangeReturn.ASYNC:
            return pending == Gst.State.PLAYING
        elif ok == Gst.StateChangeReturn.SUCCESS:
            return state == Gst.State.PLAYING
        else:
            return False

    @property
    def position(self):
        """
            Return bin playback position
            @HACK handle crossefade here, as we know we're going to be
            called every seconds
            @return position as int
        """
        position = self._playbin.query_position(Gst.Format.TIME)[1] / 1000
        if self._crossfading and self._current_track.duration > 0:
            duration = self._current_track.duration - position / 1000000
            if duration < Lp().settings.get_value('mix-duration').get_int32():
                self.__do_crossfade(duration)
        return position * 60

    @property
    def current_track(self):
        """
            Current track
        """
        return self._current_track

    @property
    def volume(self):
        """
            Return player volume rate
            @return rate as double
        """
        return self._playbin.get_volume(GstAudio.StreamVolumeFormat.CUBIC)

    def set_volume(self, rate):
        """
            Set player volume rate
            @param rate as double
        """
        self.__playbin1.set_volume(GstAudio.StreamVolumeFormat.CUBIC, rate)
        self.__playbin2.set_volume(GstAudio.StreamVolumeFormat.CUBIC, rate)
        self.emit('volume-changed')

    def next(self):
        """
            Go next track
        """
        pass

#######################
# PROTECTED           #
#######################
    def _load_track(self, track, init_volume=True):
        """
            Load track
            @param track as Track
            @param init volume as bool
            @return False if track not loaded
        """
        if self.__need_to_stop():
            return False
        if init_volume:
            self._plugins.volume.props.volume = 1.0
        debug("BinPlayer::_load_track(): %s" % track.uri)
        try:
            self._current_track = track
            if track.is_web:
                loaded = self._load_web(track)
                # If track not loaded, go next
                if not loaded:
                    self.set_next()
                    GLib.timeout_add(500, self.__load,
                                     self.next_track, init_volume)
                return False  # Return not loaded as handled by load_web()
            else:
                self._playbin.set_property('uri', track.uri)
        except Exception as e:  # Gstreamer error
            print("BinPlayer::_load_track(): ", e)
            return False
        return True

    def _load_web(self, track, play=True):
        """
            Load track url and play it
            @param track as Track
            @param play as bool
            @return True if loading
        """
        if not get_network_available():
            # Force widgets to update (spinners)
            self.emit('current-changed')
            return False
        try:
            from lollypop.web import Web
            if play:
                self.emit('loading-changed', True)
            t = Thread(target=Web.play_track,
                       args=(track, play, self.__set_gv_uri))
            t.daemon = True
            t.start()
            return True
        except Exception as e:
            self._current_track = Track()
            self.stop()
            self.emit('current-changed')
            if Lp().notify is not None:
                Lp().notify.send(str(e), track.uri)
            print("PlayerBin::_load_web()", e)

    def _scrobble(self, finished, finished_start_time):
        """
            Scrobble on lastfm
            @param finished as Track
            @param finished_start_time as int
        """
        # Last.fm policy
        if finished.duration < 30:
            return
        # Scrobble on lastfm
        if Lp().lastfm is not None:
            artists = ", ".join(finished.artists)
            played = time() - finished_start_time
            # We can scrobble if the track has been played
            # for at least half its duration, or for 4 minutes
            if played >= finished.duration / 2 or played >= 240:
                Lp().lastfm.do_scrobble(artists,
                                        finished.album_name,
                                        finished.title,
                                        int(finished_start_time))

    def _on_stream_start(self, bus, message):
        """
            On stream start
            Emit "current-changed" to notify others components
            @param bus as Gst.Bus
            @param message as Gst.Message
        """
        self._start_time = time()
        debug("Player::_on_stream_start(): %s" % self._current_track.uri)
        self.emit('current-changed')
        # Update now playing on lastfm
        if Lp().lastfm is not None and self._current_track.id >= 0:
            artists = ", ".join(self._current_track.artists)
            Lp().lastfm.now_playing(artists,
                                    self._current_track.album_name,
                                    self._current_track.title,
                                    int(self._current_track.duration))
        if not Lp().scanner.is_locked():
            Lp().tracks.set_listened_at(self._current_track.id, int(time()))

#######################
# PRIVATE             #
#######################
    def __update_current_duration(self, reader, track):
        """
            Update current track duration
            @param reader as TagReader
            @param track id as int
        """
        try:
            duration = reader.get_info(track.uri).get_duration() / 1000000000
            if duration != track.duration and duration > 0:
                Lp().tracks.set_duration(track.id, duration)
                self._current_track.set_duration(duration)
                GLib.idle_add(self.emit, 'duration-changed', track.id)
        except:
            pass

    def __load(self, track, init_volume=True):
        """
            Stop current track, load track id and play it
            If was playing, do not use play as status doesn't changed
            @param track as Track
            @param init volume as bool
        """
        was_playing = self.is_playing
        self._playbin.set_state(Gst.State.NULL)
        if self._load_track(track, init_volume):
            if was_playing:
                self._playbin.set_state(Gst.State.PLAYING)
            else:
                self.play()

    def __volume_up(self, playbin, plugins, duration):
        """
            Make volume going up smoothly
            @param playbin as Gst.Bin
            @param plugins as PluginsPlayer
            @param duration as int
        """
        # We are not the active playbin, stop all
        if self._playbin != playbin:
            return
        if duration > 0:
            vol = plugins.volume.props.volume
            steps = duration / 0.25
            vol_up = (1.0 - vol) / steps
            rate = vol + vol_up
            if rate < 1.0:
                plugins.volume.props.volume = rate
                GLib.timeout_add(250, self.__volume_up,
                                 playbin, plugins, duration - 0.25)
            else:
                plugins.volume.props.volume = 1.0
        else:
            plugins.volume.props.volume = 1.0

    def __volume_down(self, playbin, plugins, duration):
        """
            Make volume going down smoothly
            @param playbin as Gst.Bin
            @param plugins as PluginsPlayer
            @param duration as int
        """
        # We are again the active playbin, stop all
        if self._playbin == playbin:
            return
        if duration > 0:
            vol = plugins.volume.props.volume
            steps = duration / 0.25
            vol_down = vol / steps
            rate = vol - vol_down
            if rate > 0:
                plugins.volume.props.volume = rate
                GLib.timeout_add(250, self.__volume_down,
                                 playbin, plugins, duration - 0.25)
            else:
                plugins.volume.props.volume = 0.0
                playbin.set_state(Gst.State.NULL)
        else:
            plugins.volume.props.volume = 0.0
            playbin.set_state(Gst.State.NULL)

    def __do_crossfade(self, duration, track=None, next=True):
        """
            Crossfade tracks
            @param duration as int
            @param track as Track
            @param next as bool
        """
        # No cossfading if we need to stop
        if self.__need_to_stop() and next:
            return

        if track is None:
            self._scrobble(self._current_track, self._start_time)
            # Increment popularity
            if not Lp().scanner.is_locked():
                Lp().tracks.set_more_popular(self._current_track.id)
                # In party mode, linear popularity
                if self.is_party:
                    pop_to_add = 1
                # In normal mode, based on tracks count
                else:
                    pop_to_add = int(Lp().albums.max_count /
                                     Lp().albums.get_tracks_count(
                                                 self._current_track.album_id))
                Lp().albums.set_more_popular(self._current_track.album_id,
                                             pop_to_add)

        GLib.idle_add(self.__volume_down, self._playbin,
                      self._plugins, duration)
        if self._playbin == self.__playbin2:
            self._playbin = self.__playbin1
            self._plugins = self._plugins1
        else:
            self._playbin = self.__playbin2
            self._plugins = self._plugins2

        if track is not None:
            self.__load(track, False)
            self._plugins.volume.props.volume = 0
            GLib.idle_add(self.__volume_up, self._playbin,
                          self._plugins, duration)
        elif next and self._next_track.id is not None:
            self.__load(self._next_track, False)
            self._plugins.volume.props.volume = 0
            GLib.idle_add(self.__volume_up, self._playbin,
                          self._plugins, duration)
        elif self._prev_track.id is not None:
            self.__load(self._prev_track, False)
            self._plugins.volume.props.volume = 0
            GLib.idle_add(self.__volume_up, self._playbin,
                          self._plugins, duration)

    def __need_to_stop(self):
        """
            Return True if playback needs to stop
            @return bool
        """
        stop = False
        playback = Lp().settings.get_enum('playback')
        if playback == NextContext.STOP:
            if not self._albums or playback == self._next_context:
                stop = True
        return stop and self.is_playing

    def __on_volume_changed(self, playbin, sink):
        """
            Update volume
            @param playbin as Gst.Bin
            @param sink as Gst.Sink
        """
        if playbin == self.__playbin1:
            vol = self.__playbin1.get_volume(GstAudio.StreamVolumeFormat.CUBIC)
            self.__playbin2.set_volume(GstAudio.StreamVolumeFormat.CUBIC, vol)
        else:
            vol = self.__playbin2.get_volume(GstAudio.StreamVolumeFormat.CUBIC)
            self.__playbin1.set_volume(GstAudio.StreamVolumeFormat.CUBIC, vol)
        self.emit('volume-changed')

    def __on_bus_message_tag(self, bus, message):
        """
            Read tags from stream
            @param bus as Gst.Bus
            @param message as Gst.Message
        """
        # Some radio streams send message tag every seconds!
        changed = False
        if self._current_track.persistent == DbPersistent.INTERNAL and\
            (self._current_track.id >= 0 or
             self._current_track.duration > 0.0):
            return
        debug("Player::__on_bus_message_tag(): %s" % self._current_track.uri)
        reader = TagReader()

        # Update duration of non internals
        if self._current_track.persistent != DbPersistent.INTERNAL:
            t = Thread(target=self.__update_current_duration,
                       args=(reader, self._current_track))
            t.daemon = True
            t.start()
            return

        tags = message.parse_tag()
        title = reader.get_title(tags, '')
        if title != '' and self._current_track.name != title:
            self._current_track.name = title
            changed = True
        if self._current_track.name == '':
            self._current_track.name = self._current_track.uri
            changed = True
        artists = reader.get_artists(tags)
        if artists != '' and self._current_track.artists != artists:
            self._current_track.artists = artists.split(',')
            changed = True
        if not self._current_track.artists:
            self._current_track.artists = self._current_track.album_artists
            changed = True

        if self._current_track.id == Type.EXTERNALS:
            (b, duration) = self._playbin.query_duration(Gst.Format.TIME)
            if b:
                self._current_track.duration = duration/1000000000
            # We do not use tagreader as we need to check if value is None
            self._current_track.album_name = tags.get_string_index('album',
                                                                   0)[1]
            if self._current_track.album_name is None:
                self._current_track.album_name = ''
            self._current_track.genres = reader.get_genres(tags).split(',')
            changed = True
        if changed:
            self.emit('current-changed')

    def __on_bus_element(self, bus, message):
        """
            Set elements for missings plugins
            @param bus as Gst.Bus
            @param message as Gst.Message
        """
        if GstPbutils.is_missing_plugin_message(message):
            self.__codecs.append(message)

    def __on_bus_error(self, bus, message):
        """
            Handle first bus error, ignore others
            @param bus as Gst.Bus
            @param message as Gst.Message
        """
        debug("Error playing: %s" % self._current_track.uri)
        Lp().window.pulse(False)
        if self.__codecs.is_missing_codec(message):
            self.__codecs.install()
            Lp().scanner.stop()
        elif Lp().notify is not None:
            Lp().notify.send(message.parse_error()[0].message)
        self.emit('current-changed')
        return True

    def __on_bus_eos(self, bus, message):
        """
            On end of stream, stop playback
            go next otherwise
        """
        debug("Player::__on_bus_eos(): %s" % self._current_track.uri)
        if self._playbin.get_bus() == bus:
            self.stop()
            self._next_context = NextContext.NONE
            if self._next_track.id is not None:
                self._load_track(self._next_track)
            self.emit('current-changed')

    def __on_stream_about_to_finish(self, playbin):
        """
            When stream is about to finish, switch to next track without gap
            @param playbin as Gst bin
        """
        debug("Player::__on_stream_about_to_finish(): %s" % playbin)
        # Don't do anything if crossfade on, track already changed
        if self._crossfading:
            return
        if self._current_track.id == Type.RADIOS:
            return
        self._scrobble(self._current_track, self._start_time)
        # Increment popularity
        if not Lp().scanner.is_locked() and self._current_track.id >= 0:
            Lp().tracks.set_more_popular(self._current_track.id)
            # In party mode, linear popularity
            if self.is_party:
                pop_to_add = 1
            # In normal mode, based on tracks count
            else:
                # Some users report an issue where get_tracks_count() return 0
                # See issue #886
                # Don't understand how this can happen!
                count = Lp().albums.get_tracks_count(
                                                 self._current_track.album_id)
                if count:
                    pop_to_add = int(Lp().albums.max_count / count)
                else:
                    pop_to_add = 1
            Lp().albums.set_more_popular(self._current_track.album_id,
                                         pop_to_add)
        if self._next_track.id is not None:
            self._load_track(self._next_track)

    def __set_gv_uri(self, uri, track, play):
        """
            Play uri for io
            @param uri as str
            @param track as Track
            @param play as bool
        """
        track.set_uri(uri)
        if play:
            self.load(track)
示例#51
0
class Row(Gtk.ListBoxRow):
    """
        A row
    """

    def __init__(self, rowid, num):
        """
            Init row widgets
            @param rowid as int
            @param num as int
            @param show loved as bool
        """
        # We do not use Gtk.Builder for speed reasons
        Gtk.ListBoxRow.__init__(self)
        self._track = Track(rowid)
        self._number = num
        self._indicator = IndicatorWidget(self._track.id)
        self.set_indicator(Lp().player.current_track.id == self._track.id,
                           utils.is_loved(self._track.id))
        self._row_widget = Gtk.EventBox()
        self._row_widget.connect('button-press-event', self._on_button_press)
        self._row_widget.connect('enter-notify-event', self._on_enter_notify)
        self._grid = Gtk.Grid()
        self._grid.set_column_spacing(5)
        self._row_widget.add(self._grid)
        self._title_label = Gtk.Label.new(self._track.formated_name())
        self._title_label.set_use_markup(True)
        self._title_label.set_property('has-tooltip', True)
        self._title_label.connect('query-tooltip',
                                  self._on_title_query_tooltip)
        self._title_label.set_property('hexpand', True)
        self._title_label.set_property('halign', Gtk.Align.START)
        self._title_label.set_ellipsize(Pango.EllipsizeMode.END)
        self._duration_label = Gtk.Label.new(
                                       seconds_to_string(self._track.duration))
        self._duration_label.get_style_context().add_class('dim-label')
        self._num_label = Gtk.Label()
        self._num_label.set_ellipsize(Pango.EllipsizeMode.END)
        self._num_label.set_property('valign', Gtk.Align.CENTER)
        self._num_label.set_width_chars(4)
        self._num_label.get_style_context().add_class('dim-label')
        self.update_num_label()
        self._menu_button = Gtk.Button.new()
        # Here a hack to make old Gtk version support min-height css attribute
        # min-height = 24px, borders = 2px, we set directly on stack
        # min-width = 24px, borders = 2px, padding = 8px
        self._menu_button.set_size_request(34, 26)
        self._menu_button.set_relief(Gtk.ReliefStyle.NONE)
        self._menu_button.get_style_context().add_class('menu-button')
        self._menu_button.get_style_context().add_class('track-menu-button')
        self._grid.add(self._num_label)
        self._grid.add(self._title_label)
        self._grid.add(self._duration_label)
        # TODO Remove this later
        if Gtk.get_minor_version() > 16:
            self._grid.add(self._menu_button)
        else:
            self.connect('map', self._on_map)
        self.add(self._row_widget)
        self.get_style_context().add_class('trackrow')

    def set_indicator(self, playing, loved):
        """
            Show indicator
            @param widget name as str
            @param playing as bool
            @param loved as bool
        """
        self._indicator.clear()
        if playing:
            self.get_style_context().remove_class('trackrow')
            self.get_style_context().add_class('trackrowplaying')
            if loved:
                self._indicator.play_loved()
            else:
                self._indicator.play()
        else:
            self.get_style_context().remove_class('trackrowplaying')
            self.get_style_context().add_class('trackrow')
            if loved:
                self._indicator.loved()
            else:
                self._indicator.empty()

    def set_number(self, num):
        """
            Set number
            @param number as int
        """
        self._number = num

    def update_num_label(self):
        """
            Update position label for row
        """
        if Lp().player.is_in_queue(self._track.id):
            self._num_label.get_style_context().add_class('queued')
            pos = Lp().player.get_track_position(self._track.id)
            self._num_label.set_text(str(pos))
        elif self._number > 0:
            self._num_label.get_style_context().remove_class('queued')
            self._num_label.set_text(str(self._number))
        else:
            self._num_label.get_style_context().remove_class('queued')
            self._num_label.set_text('')

    def get_id(self):
        """
            Get object id
            @return Current id as int
        """
        return self._track.id

#######################
# PRIVATE             #
#######################
    def _on_map(self, widget):
        """
            Fix for Gtk < 3.18,
            if we are in a popover, do not show menu button
        """
        widget = self.get_parent()
        while widget is not None:
            if isinstance(widget, Gtk.Popover):
                break
            widget = widget.get_parent()
        if widget is None:
            self._grid.add(self._menu_button)

    def _on_enter_notify(self, widget, event):
        """
            Set image on buttons now, speed reason
            @param widget as Gtk.Widget
            @param event as Gdk.Event
        """
        if self._menu_button.get_image() is None:
            image = Gtk.Image.new_from_icon_name('open-menu-symbolic',
                                                 Gtk.IconSize.MENU)
            image.set_opacity(0.2)
            self._menu_button.set_image(image)
            self._menu_button.connect('clicked', self._on_button_clicked)
            self._indicator.update_button()

    def _on_button_press(self, widget, event):
        """
            Popup menu for track relative to track row
            @param widget as Gtk.Widget
            @param event as Gdk.Event
        """
        if event.button == 3 and Gtk.get_minor_version() > 16:
            window = widget.get_window()
            if window == event.window:
                self._popup_menu(widget, event.x, event.y)
            # Happens when pressing button over menu btn
            else:
                self._popup_menu(self._menu_button)
            return True
        elif event.button == 2:
            if self._track.id in Lp().player.get_queue():
                Lp().player.del_from_queue(self._track.id)
            else:
                Lp().player.append_to_queue(self._track.id)

    def _on_button_clicked(self, widget):
        """
            Popup menu for track relative to button
            @param widget as Gtk.Button
        """
        self._popup_menu(widget)

    def _popup_menu(self, widget, xcoordinate=None, ycoordinate=None):
        """
            Popup menu for track
            @param widget as Gtk.Button
            @param xcoordinate as int (or None)
            @param ycoordinate as int (or None)
        """
        popover = TrackMenuPopover(self._track.id, TrackMenu(self._track.id))
        popover.set_relative_to(widget)
        if xcoordinate is not None and ycoordinate is not None:
            rect = widget.get_allocation()
            rect.x = xcoordinate
            rect.y = ycoordinate
            rect.width = rect.height = 1
            popover.set_pointing_to(rect)
        popover.connect('closed', self._on_closed)
        self.get_style_context().add_class('track-menu-selected')
        popover.show()

    def _on_closed(self, widget):
        """
            Remove selected style
            @param widget as Gtk.Popover
        """
        self.get_style_context().remove_class('track-menu-selected')

    def _on_title_query_tooltip(self, widget, x, y, keyboard, tooltip):
        """
            Show tooltip if needed
            @param widget as Gtk.Widget
            @param x as int
            @param y as int
            @param keyboard as bool
            @param tooltip as Gtk.Tooltip
        """
        layout = self._title_label.get_layout()
        if layout.is_ellipsized():
            self._title_label.set_tooltip_markup(self._track.formated_name())
        elif len(self._track.artists) > 1:
            self._title_label.set_tooltip_text(
                                           ", ".join(self._track.artists[1:]))
        else:
            self._title_label.set_tooltip_text('')
示例#52
0
 def restore_state(self):
     """
         Restore player state
     """
     try:
         if Lp().settings.get_value('save-state'):
             track_id = load(open(DataPath + "/track_id.bin", "rb"))
             playlist_ids = load(open(DataPath + "/playlist_ids.bin",
                                 "rb"))
             if playlist_ids and playlist_ids[0] == Type.RADIOS:
                 radios = Radios()
                 track = Track()
                 name = radios.get_name(track_id)
                 url = radios.get_url(name)
                 track.set_radio(name, url)
                 self.load(track)
             elif Lp().tracks.get_path(track_id) != "":
                 track = Track(track_id)
                 if Lp().notify is not None:
                     Lp().notify.inhibit()
                 self._load_track(track)
                 # We set this initial state
                 # because seek while failed otherwise
                 self.pause()
                 if playlist_ids:
                     pids = []
                     for playlist_id in playlist_ids:
                         pids.append(int(playlist_id))
                     track_ids = []
                     for playlist_id in playlist_ids:
                         if playlist_id == Type.POPULARS:
                             tracks = Lp().tracks.get_populars()
                         elif playlist_id == Type.RECENTS:
                             tracks = Lp().tracks.get_recently_listened_to()
                         elif playlist_id == Type.NEVER:
                             tracks = Lp().tracks.get_never_listened_to()
                         elif playlist_id == Type.RANDOMS:
                             tracks = Lp().tracks.get_randoms()
                         else:
                             tracks = Lp().playlists.get_track_ids(
                                                                playlist_id)
                         for track_id in tracks:
                             if track_id not in track_ids:
                                 track_ids.append(track_id)
                         self.populate_user_playlist_by_tracks(track_ids,
                                                               pids)
                 else:
                     self._albums = load(open(
                                         DataPath + "/albums.bin",
                                         "rb"))
                     self.shuffle_albums(True)
                     self._context.genre_ids = load(open(
                                         DataPath + "/genre_ids.bin",
                                         "rb"))
                     self._context.artist_ids = load(open(
                                         DataPath + "/artist_ids.bin",
                                         "rb"))
                 self.set_next()
                 self.set_prev()
                 if Lp().settings.get_value('repeat'):
                     self._context.next = NextContext.NONE
                 else:
                     self._context.next = NextContext.STOP_ALL
             else:
                 print("Player::restore_state(): track missing")
     except Exception as e:
         print("Player::restore_state()", e)