Example #1
0
class PlaylistsWidget(Gtk.Bin):
    """
        Show playlist tracks/albums
    """

    def __init__(self, playlist_ids):
        """
            Init playlist Widget
            @param playlist ids as [int]
        """
        Gtk.Bin.__init__(self)
        self._playlist_ids = playlist_ids
        self._tracks1 = []
        self._tracks2 = []
        self._width = None
        self._orientation = None
        self._stop = False
        # Used to block widget2 populate while showing one column
        self._locked_widget2 = True

        self._box = Gtk.Grid()
        self._box.set_column_homogeneous(True)
        self._box.set_property('valign', Gtk.Align.START)
        self._box.show()

        self.connect('size-allocate', self._on_size_allocate)

        self._tracks_widget_left = TracksWidget()
        self._tracks_widget_right = TracksWidget()
        self._tracks_widget_left.connect('activated',
                                         self._on_activated)
        self._tracks_widget_right.connect('activated',
                                          self._on_activated)
        self._tracks_widget_left.show()
        self._tracks_widget_right.show()

        self.add(self._box)

    def get_id(self):
        """
            Return playlist widget id
            @return int
        """
        return Type.PLAYLISTS

    def show_overlay(self, bool):
        """
            No overlay here now
        """
        pass

    def update_state(self):
        """
            No state to update
        """
        pass

    def update_cover(self):
        """
            No update cover for now
        """
        pass

    def get_current_ordinate(self):
        """
            If current track in widget, return it ordinate,
            @return y as int
        """
        ordinate = None
        for child in self._tracks_widget_left.get_children() + \
                self._tracks_widget_right.get_children():
            if child.get_id() == Lp().player.current_track.id:
                ordinate = child.translate_coordinates(self._box, 0, 0)[1]
        return ordinate

    def populate_list_left(self, tracks, pos):
        """
            Populate left list
            @param track's ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        # We reset width here to allow size allocation code to run
        self._width = None
        self._tracks1 = list(tracks)
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget_left,
                      pos)

    def populate_list_right(self, tracks, pos):
        """
            Populate right list
            @param track's ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        self._tracks2 = list(tracks)
        # If we are showing only one column, wait for widget1
        if self._orientation == Gtk.Orientation.VERTICAL and\
           self._locked_widget2:
            GLib.timeout_add(100, self.populate_list_right, tracks, pos)
        else:
            # We reset width here to allow size allocation code to run
            self._width = None
            GLib.idle_add(self._add_tracks,
                          tracks,
                          self._tracks_widget_right,
                          pos)

    def update_playing_indicator(self):
        """
            Update playing indicator
        """
        self._tracks_widget_left.update_playing(Lp().player.current_track.id)
        self._tracks_widget_right.update_playing(Lp().player.current_track.id)

    def stop(self):
        """
            Stop loading
        """
        self._stop = True

    def append(self, track_id):
        """
            Add track to widget
            @param track id as int
        """
        self._add_tracks([track_id],
                         self._tracks_widget_right,
                         -1)
        self._update_tracks()
        self._update_position()
        self._update_headers()
        self._tracks_widget_left.update_indexes(1)
        self._tracks_widget_right.update_indexes(len(self._tracks1) + 1)

    def remove(self, track_id):
        """
            Del track from widget
            @param track id as int
        """
        children = self._tracks_widget_left.get_children() + \
            self._tracks_widget_right.get_children()
        # Clear the widget
        if track_id is None:
            for child in children:
                child.destroy()
            self._update_tracks()
        else:
            for child in children:
                if child.get_id() == track_id:
                    child.destroy()
                    break
            self._update_tracks()
            self._update_position()
            self._update_headers()
            self._tracks_widget_left.update_indexes(1)
            self._tracks_widget_right.update_indexes(len(self._tracks1) + 1)

#######################
# PRIVATE             #
#######################
    def _add_tracks(self, tracks, widget, pos, previous_album_id=None):
        """
            Add tracks to list
            @param tracks id as array of [int]
            @param widget TracksWidget
            @param track position as int
            @param pos as int
            @param previous album id as int
        """
        if not tracks or self._stop:
            if widget == self._tracks_widget_right:
                self._stop = False
            else:
                self._locked_widget2 = False
            return

        track = Track(tracks.pop(0))
        row = PlaylistRow(track.id, pos,
                          track.album.id != previous_album_id)
        row.connect('track-moved', self._on_track_moved)
        row.show()
        widget.insert(row, pos)
        GLib.idle_add(self._add_tracks, tracks, widget,
                      pos + 1, track.album.id)

    def _update_tracks(self):
        """
            Update tracks based on current widget
        """
        # Recalculate tracks
        self._tracks1 = []
        self._tracks2 = []
        for child in self._tracks_widget_left.get_children():
            self._tracks1.append(child.get_id())
        for child in self._tracks_widget_right.get_children():
            self._tracks2.append(child.get_id())

    def _update_position(self):
        """
            Update widget position
        """
        len_tracks1 = len(self._tracks1)
        len_tracks2 = len(self._tracks2)
        # Take first track from tracks2 and put it at the end of tracks1
        if len_tracks2 > len_tracks1:
            src = self._tracks2[0]
            if self._tracks1:
                dst = self._tracks1[-1]
            else:
                dst = -1
            self._move_track(dst, src, False)
        # Take last track of tracks1 and put it at the bottom of tracks2
        elif len_tracks1 - 1 > len_tracks2:
            src = self._tracks1[-1]
            if self._tracks2:
                dst = self._tracks2[0]
            else:
                dst = -1
            self._move_track(dst, src, True)
        self._update_tracks()

    def _update_headers(self):
        """
            Update headers for all tracks
        """
        self._tracks_widget_left.update_headers()
        prev_album_id = None
        if self._orientation == Gtk.Orientation.VERTICAL:
            if self._tracks1:
                prev_album_id = Track(self._tracks1[-1]).album.id
        self._tracks_widget_right.update_headers(prev_album_id)

    def _move_track(self, dst, src, up):
        """
            Move track from src to row
            @param dst as int
            @param src as int
            @param up as bool
            @return (dst_widget as TracksWidget,
                     src index as int, dst index as int)
        """
        tracks1_len = len(self._tracks1)
        tracks2_len = len(self._tracks2)
        if src in self._tracks1:
            src_widget = self._tracks_widget_left
            src_index = self._tracks1.index(src) - 1
        else:
            src_widget = self._tracks_widget_right
            src_index = self._tracks2.index(src) - 1
        if tracks1_len == 0 or dst in self._tracks1:
            dst_widget = self._tracks_widget_left
            dst_tracks = self._tracks1
        elif tracks2_len == 0 or dst in self._tracks2:
            dst_widget = self._tracks_widget_right
            dst_tracks = self._tracks2
        else:
            return
        # Remove src from src_widget
        for child in src_widget.get_children():
            if child.get_id() == src:
                child.destroy()
                break
        src_track = Track(src)
        prev_track = Track()
        name = escape(src_track.name)
        index = 0
        # Get previous track
        if dst != -1:
            for child in dst_widget.get_children():
                if child.get_id() == dst:
                    break
                index += 1
            if not up:
                index += 1
            # Get previous track (in dst context)
            prev_index = dst_tracks.index(dst)
            if up:
                prev_index -= 1
            prev_track = Track(dst_tracks[prev_index])
            # If we are listening to a compilation, prepend artist name
            if (src_track.album.artist_id == Type.COMPILATIONS or
                    len(src_track.artist_ids) > 1 or
                    src_track.album.artist_id not in src_track.artist_ids):
                name = "<b>%s</b>\n%s" % (escape(", ".join(src_track.artists)),
                                          name)
            self._tracks1.insert(index, src_track.id)
        row = PlaylistRow(src_track.id,
                          index,
                          index == 0 or
                          src_track.album.id != prev_track.album.id)
        row.connect('track-moved', self._on_track_moved)
        row.show()
        dst_widget.insert(row, index)
        return (src_widget, dst_widget, src_index, index)

    def _on_track_moved(self, widget, dst, src, up):
        """
            Move track from src to row
            Recalculate track position
            @param widget as TracksWidget
            @param dst as int
            @param src as int
            @param up as bool
        """
        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._tracks1 + self._tracks2:
                    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._tracks1 + self._tracks2)

        (src_widget, dst_widget, src_index, dst_index) = \
            self._move_track(dst, src, up)
        self._update_tracks()
        self._update_position()
        self._update_headers()
        self._tracks_widget_left.update_indexes(1)
        self._tracks_widget_right.update_indexes(len(self._tracks1) + 1)
        t = Thread(target=update_playlist)
        t.daemon = True
        t.start()

    def _on_size_allocate(self, widget, allocation):
        """
            Change box max/min children
            @param widget as Gtk.Widget
            @param allocation as Gtk.Allocation
        """
        if self._width == allocation.width:
            return
        self._width = allocation.width
        redraw = False
        if allocation.width < WindowSize.MONSTER:
            orientation = Gtk.Orientation.VERTICAL
        else:
            orientation = Gtk.Orientation.HORIZONTAL
        if orientation != self._orientation:
            self._orientation = orientation
            redraw = True
        self._box.set_orientation(orientation)
        if redraw:
            for child in self._box.get_children():
                self._box.remove(child)
            GLib.idle_add(self._box.add, self._tracks_widget_left)
            GLib.idle_add(self._box.add, self._tracks_widget_right)
        self._update_headers()

    def _on_activated(self, widget, track_id):
        """
            On track activation, play track
            @param widget as TracksWidget
            @param track as Track
        """
        Lp().player.load(Track(track_id))
        if not Lp().player.is_party():
            Lp().player.populate_user_playlist_by_tracks(self._tracks1 +
                                                         self._tracks2,
                                                         self._playlist_ids)
Example #2
0
class PlaylistsWidget(Gtk.Bin):
    """
        Show playlist tracks/albums
    """
    def __init__(self, playlist_ids):
        """
            Init playlist Widget
            @param playlist ids as [int]
        """
        Gtk.Bin.__init__(self)
        self._playlist_ids = playlist_ids
        self._tracks1 = []
        self._tracks2 = []
        self._width = None
        self._orientation = None
        self._stop = False
        # Used to block widget2 populate while showing one column
        self._locked_widget2 = True

        self._box = Gtk.Grid()
        self._box.set_column_homogeneous(True)
        self._box.set_property('valign', Gtk.Align.START)
        self._box.show()

        self.connect('size-allocate', self._on_size_allocate)

        self._tracks_widget_left = TracksWidget()
        self._tracks_widget_right = TracksWidget()
        self._tracks_widget_left.connect('activated', self._on_activated)
        self._tracks_widget_right.connect('activated', self._on_activated)
        self._tracks_widget_left.show()
        self._tracks_widget_right.show()

        self.add(self._box)

    def get_id(self):
        """
            Return playlist widget id
            @return int
        """
        return Type.PLAYLISTS

    def update_overlay(self):
        """
            No overlay here now
        """
        pass

    def update_state(self):
        """
            No state to update
        """
        pass

    def update_cover(self):
        """
            No update cover for now
        """
        pass

    def get_current_ordinate(self):
        """
            If current track in widget, return it ordinate,
            @return y as int
        """
        ordinate = None
        for child in self._tracks_widget_left.get_children() + \
                self._tracks_widget_right.get_children():
            if child.get_id() == Lp().player.current_track.id:
                ordinate = child.translate_coordinates(self._box, 0, 0)[1]
        return ordinate

    def populate_list_left(self, tracks, pos):
        """
            Populate left list
            @param track's ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        # We reset width here to allow size allocation code to run
        self._width = None
        self._tracks1 = list(tracks)
        GLib.idle_add(self._add_tracks, tracks, self._tracks_widget_left, pos)

    def populate_list_right(self, tracks, pos):
        """
            Populate right list
            @param track's ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        self._tracks2 = list(tracks)
        # If we are showing only one column, wait for widget1
        if self._orientation == Gtk.Orientation.VERTICAL and\
           self._locked_widget2:
            GLib.timeout_add(100, self.populate_list_right, tracks, pos)
        else:
            # We reset width here to allow size allocation code to run
            self._width = None
            GLib.idle_add(self._add_tracks, tracks, self._tracks_widget_right,
                          pos)

    def update_playing_indicator(self):
        """
            Update playing indicator
        """
        self._tracks_widget_left.update_playing(Lp().player.current_track.id)
        self._tracks_widget_right.update_playing(Lp().player.current_track.id)

    def stop(self):
        """
            Stop loading
        """
        self._stop = True

    def append(self, track_id):
        """
            Add track to widget
            @param track id as int
        """
        self._add_tracks([track_id], self._tracks_widget_right, -1)
        self._update_tracks()
        self._update_position()
        self._update_headers()
        self._tracks_widget_left.update_indexes(1)
        self._tracks_widget_right.update_indexes(len(self._tracks1) + 1)

    def remove(self, track_id):
        """
            Del track from widget
            @param track id as int
        """
        children = self._tracks_widget_left.get_children() + \
            self._tracks_widget_right.get_children()
        # Clear the widget
        if track_id is None:
            for child in children:
                child.destroy()
            self._update_tracks()
        else:
            for child in children:
                if child.get_id() == track_id:
                    child.destroy()
                    break
            self._update_tracks()
            self._update_position()
            self._update_headers()
            self._tracks_widget_left.update_indexes(1)
            self._tracks_widget_right.update_indexes(len(self._tracks1) + 1)


#######################
# PRIVATE             #
#######################

    def _add_tracks(self, tracks, widget, pos, previous_album_id=None):
        """
            Add tracks to list
            @param tracks id as array of [int]
            @param widget TracksWidget
            @param track position as int
            @param pos as int
            @param previous album id as int
        """
        if not tracks or self._stop:
            if widget == self._tracks_widget_right:
                self._stop = False
            else:
                self._locked_widget2 = False
            return

        track = Track(tracks.pop(0))
        row = PlaylistRow(track.id, pos, track.album.id != previous_album_id)
        row.connect('track-moved', self._on_track_moved)
        row.show()
        widget.insert(row, pos)
        GLib.idle_add(self._add_tracks, tracks, widget, pos + 1,
                      track.album.id)

    def _update_tracks(self):
        """
            Update tracks based on current widget
        """
        # Recalculate tracks
        self._tracks1 = []
        self._tracks2 = []
        for child in self._tracks_widget_left.get_children():
            self._tracks1.append(child.get_id())
        for child in self._tracks_widget_right.get_children():
            self._tracks2.append(child.get_id())

    def _update_position(self):
        """
            Update widget position
        """
        len_tracks1 = len(self._tracks1)
        len_tracks2 = len(self._tracks2)
        # Take first track from tracks2 and put it at the end of tracks1
        if len_tracks2 > len_tracks1:
            src = self._tracks2[0]
            if self._tracks1:
                dst = self._tracks1[-1]
            else:
                dst = -1
            self._move_track(dst, src, False)
        # Take last track of tracks1 and put it at the bottom of tracks2
        elif len_tracks1 - 1 > len_tracks2:
            src = self._tracks1[-1]
            if self._tracks2:
                dst = self._tracks2[0]
            else:
                dst = -1
            self._move_track(dst, src, True)
        self._update_tracks()

    def _update_headers(self):
        """
            Update headers for all tracks
        """
        self._tracks_widget_left.update_headers()
        prev_album_id = None
        if self._orientation == Gtk.Orientation.VERTICAL:
            if self._tracks1:
                prev_album_id = Track(self._tracks1[-1]).album.id
        self._tracks_widget_right.update_headers(prev_album_id)

    def _move_track(self, dst, src, up):
        """
            Move track from src to row
            @param dst as int
            @param src as int
            @param up as bool
            @return (dst_widget as TracksWidget,
                     src index as int, dst index as int)
        """
        tracks1_len = len(self._tracks1)
        tracks2_len = len(self._tracks2)
        if src in self._tracks1:
            src_widget = self._tracks_widget_left
            src_index = self._tracks1.index(src) - 1
        else:
            src_widget = self._tracks_widget_right
            src_index = self._tracks2.index(src) - 1
        if tracks1_len == 0 or dst in self._tracks1:
            dst_widget = self._tracks_widget_left
            dst_tracks = self._tracks1
        elif tracks2_len == 0 or dst in self._tracks2:
            dst_widget = self._tracks_widget_right
            dst_tracks = self._tracks2
        else:
            return
        # Remove src from src_widget
        for child in src_widget.get_children():
            if child.get_id() == src:
                child.destroy()
                break
        src_track = Track(src)
        prev_track = Track()
        name = escape(src_track.name)
        index = 0
        # Get previous track
        if dst != -1:
            for child in dst_widget.get_children():
                if child.get_id() == dst:
                    break
                index += 1
            if not up:
                index += 1
            # Get previous track (in dst context)
            prev_index = dst_tracks.index(dst)
            if up:
                prev_index -= 1
            prev_track = Track(dst_tracks[prev_index])
            # If we are listening to a compilation, prepend artist name
            if (src_track.album.artist_id == Type.COMPILATIONS
                    or len(src_track.artist_ids) > 1
                    or src_track.album.artist_id not in src_track.artist_ids):
                name = "<b>%s</b>\n%s" % (escape(", ".join(
                    src_track.artists)), name)
            self._tracks1.insert(index, src_track.id)
        row = PlaylistRow(
            src_track.id, index, index == 0
            or src_track.album.id != prev_track.album.id)
        row.connect('track-moved', self._on_track_moved)
        row.show()
        dst_widget.insert(row, index)
        return (src_widget, dst_widget, src_index, index)

    def _on_track_moved(self, widget, dst, src, up):
        """
            Move track from src to row
            Recalculate track position
            @param widget as TracksWidget
            @param dst as int
            @param src as int
            @param up as bool
        """
        (src_widget, dst_widget, src_index, dst_index) = \
            self._move_track(dst, src, up)
        self._update_tracks()
        self._update_position()
        self._update_headers()
        self._tracks_widget_left.update_indexes(1)
        self._tracks_widget_right.update_indexes(len(self._tracks1) + 1)
        # 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._tracks1 + self._tracks2:
                tracks.append(Track(track_id))
            Lp().playlists.add_tracks(self._playlist_ids[0], tracks, False)
        if Lp().player.get_user_playlist_ids() == self._playlist_ids:
            Lp().player.populate_user_playlist_by_tracks(
                self._tracks1 + self._tracks2, self._playlist_ids)

    def _on_size_allocate(self, widget, allocation):
        """
            Change box max/min children
            @param widget as Gtk.Widget
            @param allocation as Gtk.Allocation
        """
        if self._width == allocation.width:
            return
        self._width = allocation.width
        redraw = False
        if allocation.width < WindowSize.MONSTER:
            orientation = Gtk.Orientation.VERTICAL
        else:
            orientation = Gtk.Orientation.HORIZONTAL
        if orientation != self._orientation:
            self._orientation = orientation
            redraw = True
        self._box.set_orientation(orientation)
        if redraw:
            for child in self._box.get_children():
                self._box.remove(child)
            GLib.idle_add(self._box.add, self._tracks_widget_left)
            GLib.idle_add(self._box.add, self._tracks_widget_right)
        self._update_headers()

    def _on_activated(self, widget, track_id):
        """
            On track activation, play track
            @param widget as TracksWidget
            @param track as Track
        """
        Lp().player.load(Track(track_id))
        if not Lp().player.is_party():
            Lp().player.populate_user_playlist_by_tracks(
                self._tracks1 + self._tracks2, self._playlist_ids)
Example #3
0
class PlaylistWidget(Gtk.Bin):
    """
        Init playlist Widget
        @param playlist name as str
    """
    def __init__(self, playlist_name):
        Gtk.Bin.__init__(self)
        self._playlist_name = playlist_name
        self._tracks = []
        self._stop = False

        self._main_widget = Gtk.Grid()
        self._main_widget.set_property('margin', 10)
        self._main_widget.set_property('column-spacing', 10)
        self._main_widget.show()

        self._tracks_widget1 = TracksWidget(False)
        self._tracks_widget2 = TracksWidget(False)
        self._tracks_widget1.connect('activated',
                                     self._on_activated, playlist_name)
        self._tracks_widget2.connect('activated',
                                     self._on_activated, playlist_name)
        self._tracks_widget1.show()
        self._tracks_widget2.show()

        size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
        size_group.add_widget(self._tracks_widget1)
        size_group.add_widget(self._tracks_widget2)

        self._main_widget.add(self._tracks_widget1)
        self._main_widget.add(self._tracks_widget2)

        self.add(self._main_widget)

    """
        Populate list one, thread safe
        @param track's ids as array of int
        @param track position as int
    """
    def populate_list_one(self, tracks, pos):
        self._stop = False
        self._tracks = list(tracks)
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget1,
                      pos)

    """
        Populate list two, thread safe
        @param track's ids as array of int
        @param track position as int
    """
    def populate_list_two(self, tracks, pos):
        self._stop = False
        self._tracks += list(tracks)
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget2,
                      pos)

    """
        Update playing indicator
    """
    def update_playing_indicator(self):
        self._tracks_widget1.update_playing(Lp.player.current_track.id)
        self._tracks_widget2.update_playing(Lp.player.current_track.id)

    """
        Stop loading
    """
    def stop(self):
        self._stop = True

    """
        Clear tracks
    """
    def clear(self):
        self._tracks = []
        for child in self._tracks_widget1.get_children():
            child.destroy()
        for child in self._tracks_widget2.get_children():
            child.destroy()

#######################
# PRIVATE             #
#######################
    """
        Add tracks to list
        @param tracks id as array of [int]
        @param widget TracksWidget
        @param track position as int
        @param previous album id as int
    """
    def _add_tracks(self, tracks, widget, pos, previous_album_id=None):
        if not tracks or self._stop:
            return

        track_id = tracks.pop(0)
        if track_id == -1:
            return

        (title, filepath, length, album_id) =\
            Lp.tracks.get_infos(track_id)
        if title is None:
            return
        else:
            title = escape(title)

        artist_id = Lp.albums.get_artist_id(album_id)
        artist_ids = Lp.tracks.get_artist_ids(track_id)
        # If we are listening to a compilation, prepend artist name
        if artist_id == Type.COMPILATIONS or\
           len(artist_ids) > 1 or\
           artist_id not in artist_ids:
            artist_name = ""
            for artist_id in artist_ids:
                artist_name += translate_artist_name(
                    Lp.artists.get_name(artist_id)) + ", "
            title = "<b>%s</b>\n%s" % (escape(artist_name[:-2]),
                                       title)

        if album_id != previous_album_id:
            widget.add_album(track_id, album_id, pos, title, length, None)
        else:
            widget.add_track(track_id, pos, title, length, None)
        GLib.idle_add(self._add_tracks, tracks, widget, pos+1, album_id)

    """
        On track activation, play track
        @param widget as TracksWidget
        @param track id as int
        @param playlist name as str
    """
    def _on_activated(self, widget, track_id, playlist_name):
        if not Lp.player.is_party():
            track = Lp.player.set_user_playlist(self._tracks, track_id)
            if track is not None:
                Lp.player.load(track)
        else:
            Lp.player.load(Track(track_id))
Example #4
0
class PlaylistWidget(Gtk.Bin):
    """
        Show playlist tracks/albums
    """

    def __init__(self, playlist_id):
        """
            Init playlist Widget
            @param playlist id as int
            @param playlist name as str
        """
        Gtk.Bin.__init__(self)
        self._playlist_id = playlist_id
        self._tracks1 = []
        self._tracks2 = []
        self._stop = False

        main_widget = Gtk.Grid()
        main_widget.set_property('margin', 10)
        main_widget.set_property('column-spacing', 10)
        main_widget.show()

        loved = playlist_id != Type.LOVED
        self._tracks_widget1 = TracksWidget(False, loved)
        self._tracks_widget2 = TracksWidget(False, loved)
        self._tracks_widget1.connect('activated',
                                     self._on_activated)
        self._tracks_widget2.connect('activated',
                                     self._on_activated)
        self._tracks_widget1.show()
        self._tracks_widget2.show()

        size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
        size_group.add_widget(self._tracks_widget1)
        size_group.add_widget(self._tracks_widget2)

        main_widget.add(self._tracks_widget1)
        main_widget.add(self._tracks_widget2)
        self.add(main_widget)

    def populate_list_left(self, tracks, pos):
        """
            Populate left list
            @param track's ids as array of int
            @param track position as int
            @thread safe
        """
        self._stop = False
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget1,
                      pos)

    def populate_list_right(self, tracks, pos):
        """
            Populate right list
            @param track's ids as array of int
            @param track position as int
            @thread safe
        """
        self._stop = False
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget2,
                      pos)

    def update_playing_indicator(self):
        """
            Update playing indicator
        """
        self._tracks_widget1.update_playing(Lp.player.current_track.id)
        self._tracks_widget2.update_playing(Lp.player.current_track.id)

    def stop(self):
        """
            Stop loading
        """
        self._stop = True

    def clear(self):
        """
            Clear tracks
        """
        self._tracks = []
        for child in self._tracks_widget1.get_children():
            child.destroy()
        for child in self._tracks_widget2.get_children():
            child.destroy()

#######################
# PRIVATE             #
#######################
    def _add_tracks(self, tracks, widget, pos, previous_album_id=None):
        """
            Add tracks to list
            @param tracks id as array of [int]
            @param widget TracksWidget
            @param track position as int
            @param previous album id as int
        """
        if not tracks or self._stop:
            return

        track = Track(tracks.pop(0))
        name = escape(track.name)
        album = track.album

        if track.id is None:
            GLib.idle_add(self._add_tracks, tracks,
                          widget, pos + 1, previous_album_id)
            return

        # If we are listening to a compilation, prepend artist name
        if (album.artist_id == Type.COMPILATIONS or
                len(track.artist_ids) > 1 or
                album.artist_id not in track.artist_ids):
            name = "<b>%s</b>\n%s" % (escape(track.artist_names), name)

        if widget == self._tracks_widget1:
            self._tracks1.append(track)
        else:
            self._tracks2.append(track)

        if album.id != previous_album_id:
            widget.add_album(track.id, album, pos,
                             name, track.duration, None)
        else:
            widget.add_album(track.id, None, pos, name, track.duration, None)
        GLib.idle_add(self._add_tracks, tracks, widget, pos + 1, album.id)

    def _on_activated(self, widget, track_id):
        """
            On track activation, play track
            @param widget as TracksWidget
            @param track as Track
        """
        if Lp.player.is_party():
            Lp.player.load(Track(track_id))
        else:
            track = Lp.player.set_user_playlist(self._tracks1 + self._tracks2,
                                                track_id)
            Lp.player.set_user_playlist_id(self._playlist_id)
            if track is not None:
                Lp.player.load(track)
Example #5
0
class PlaylistWidget(Gtk.Bin):
    """
        Show playlist tracks/albums
    """
    def __init__(self, playlist_id, playlist_name):
        """
            Init playlist Widget
            @param playlist id as int
            @param playlist name as str
        """
        Gtk.Bin.__init__(self)
        self._playlist_id = playlist_id
        self._playlist_name = playlist_name
        self._tracks1 = []
        self._tracks2 = []
        self._stop = False

        self._main_widget = Gtk.Grid()
        self._main_widget.set_property('margin', 10)
        self._main_widget.set_property('column-spacing', 10)
        self._main_widget.show()

        self._tracks_widget1 = TracksWidget(False)
        self._tracks_widget2 = TracksWidget(False)
        self._tracks_widget1.connect('activated', self._on_activated,
                                     playlist_name)
        self._tracks_widget2.connect('activated', self._on_activated,
                                     playlist_name)
        self._tracks_widget1.show()
        self._tracks_widget2.show()

        size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
        size_group.add_widget(self._tracks_widget1)
        size_group.add_widget(self._tracks_widget2)

        self._main_widget.add(self._tracks_widget1)
        self._main_widget.add(self._tracks_widget2)

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

        spinner = Gtk.Spinner()
        spinner.start()
        spinner.show()
        self._stack.add(spinner)
        self._stack.add(self._main_widget)
        self._stack.set_visible_child(spinner)

        self.add(self._stack)

    def populate_list_left(self, tracks, pos):
        """
            Populate left list
            @param track's ids as array of int
            @param track position as int
            @thread safe
        """
        GLib.idle_add(self._stack.set_visible_child, self._main_widget)
        self._stop = False
        GLib.idle_add(self._add_tracks, tracks, self._tracks_widget1, pos)

    def populate_list_right(self, tracks, pos):
        """
            Populate right list
            @param track's ids as array of int
            @param track position as int
            @thread safe
        """
        self._stop = False
        GLib.idle_add(self._add_tracks, tracks, self._tracks_widget2, pos)

    def update_playing_indicator(self):
        """
            Update playing indicator
        """
        self._tracks_widget1.update_playing(Lp.player.current_track.id)
        self._tracks_widget2.update_playing(Lp.player.current_track.id)

    def stop(self):
        """
            Stop loading
        """
        self._stop = True

    def clear(self):
        """
            Clear tracks
        """
        self._tracks = []
        for child in self._tracks_widget1.get_children():
            child.destroy()
        for child in self._tracks_widget2.get_children():
            child.destroy()


#######################
# PRIVATE             #
#######################

    def _add_tracks(self, tracks, widget, pos, previous_album_id=None):
        """
            Add tracks to list
            @param tracks id as array of [int]
            @param widget TracksWidget
            @param track position as int
            @param previous album id as int
        """
        if not tracks or self._stop:
            return

        track = Track(tracks.pop(0))
        name = escape(track.name)
        album = track.album

        if track.id is None:
            GLib.idle_add(self._add_tracks, tracks, widget, pos + 1,
                          previous_album_id)
            return

        # If we are listening to a compilation, prepend artist name
        if (album.artist_id == Type.COMPILATIONS or len(track.artist_ids) > 1
                or album.artist_id not in track.artist_ids):
            name = "<b>%s</b>\n%s" % (escape(track.artist_names), name)

        if widget == self._tracks_widget1:
            self._tracks1.append(track)
        else:
            self._tracks2.append(track)

        if album.id != previous_album_id:
            widget.add_album(track.id, album.id, pos, name, track.duration,
                             None)
        else:
            widget.add_album(track.id, None, pos, name, track.duration, None)
        GLib.idle_add(self._add_tracks, tracks, widget, pos + 1, album.id)

    def _on_activated(self, widget, track_id, playlist_name):
        """
            On track activation, play track
            @param widget as TracksWidget
            @param track as Track
            @param playlist name as str
        """
        if Lp.player.is_party():
            Lp.player.load(Track(track_id))
        else:
            track = Lp.player.set_user_playlist(self._tracks1 + self._tracks2,
                                                track_id)
            Lp.player.set_user_playlist_id(self._playlist_id)
            if track is not None:
                Lp.player.load(track)
Example #6
0
class PlaylistsWidget(Gtk.Grid):
    """
        Show playlist tracks/albums
    """
    __gsignals__ = {
        'populated': (GObject.SignalFlags.RUN_FIRST, None, ())
    }

    def __init__(self, playlist_ids):
        """
            Init playlist Widget
            @param playlist ids as [int]
        """
        Gtk.Grid.__init__(self)
        self.set_row_spacing(5)
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self._playlist_ids = playlist_ids
        self._tracks_left = []
        self._tracks_right = []
        self._width = None
        self._orientation = None
        self._loading = Loading.NONE
        # Used to block widget2 populate while showing one column
        self._locked_widget_right = True

        self._box = Gtk.Grid()
        self._box.set_vexpand(True)
        self._box.set_column_homogeneous(True)
        self._box.show()

        self.connect('size-allocate', self._on_size_allocate)

        self._tracks_widget_left = TracksWidget(True)
        self._tracks_widget_left.set_vexpand(True)
        self._tracks_widget_right = TracksWidget(True)
        self._tracks_widget_right.set_vexpand(True)
        self._tracks_widget_left.connect('activated',
                                         self._on_activated)
        self._tracks_widget_right.connect('activated',
                                          self._on_activated)
        self._tracks_widget_left.show()
        self._tracks_widget_right.show()

        self.drag_dest_set(Gtk.DestDefaults.DROP | Gtk.DestDefaults.MOTION,
                           [], Gdk.DragAction.MOVE)
        self.drag_dest_add_text_targets()
        self.connect('drag-data-received', self._on_drag_data_received)

        self.add(self._box)

    def get_id(self):
        """
            Return playlist widget id
            @return int
        """
        return Type.PLAYLISTS

    def show_overlay(self, bool):
        """
            No overlay here now
        """
        pass

    def update_state(self):
        """
            No state to update
        """
        pass

    def update_cover(self):
        """
            No update cover for now
        """
        pass

    def get_current_ordinate(self):
        """
            If current track in widget, return it ordinate,
            @return y as int
        """
        ordinate = None
        for child in self._tracks_widget_left.get_children() + \
                self._tracks_widget_right.get_children():
            if child.get_id() == Lp().player.current_track.id:
                ordinate = child.translate_coordinates(self._box, 0, 0)[1]
        return ordinate

    def populate_list_left(self, tracks, pos):
        """
            Populate left list
            @param track's ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        # We reset width here to allow size allocation code to run
        self._width = None
        self._tracks_left = list(tracks)
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget_left,
                      pos)

    def populate_list_right(self, tracks, pos):
        """
            Populate right list
            @param track's ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        self._tracks_right = list(tracks)
        # If we are showing only one column, wait for widget1
        if self._orientation == Gtk.Orientation.VERTICAL and\
           self._locked_widget_right:
            GLib.timeout_add(100, self.populate_list_right, tracks, pos)
        else:
            # We reset width here to allow size allocation code to run
            self._width = None
            GLib.idle_add(self._add_tracks,
                          tracks,
                          self._tracks_widget_right,
                          pos)

    def update_playing_indicator(self):
        """
            Update playing indicator
        """
        self._tracks_widget_left.update_playing(Lp().player.current_track.id)
        self._tracks_widget_right.update_playing(Lp().player.current_track.id)

    def stop(self):
        """
            Stop loading
        """
        self._loading = Loading.STOP

    def append(self, track_id):
        """
            Add track to widget
            @param track id as int
        """
        self._add_tracks([track_id],
                         self._tracks_widget_right,
                         -1)
        self._update_tracks()
        self._update_position()
        self._update_headers()
        self._tracks_widget_left.update_indexes(1)
        self._tracks_widget_right.update_indexes(len(self._tracks_left) + 1)

    def remove(self, track_id):
        """
            Del track from widget
            @param track id as int
        """
        children = self._tracks_widget_left.get_children() + \
            self._tracks_widget_right.get_children()
        # Clear the widget
        if track_id is None:
            for child in children:
                child.destroy()
            self._update_tracks()
        else:
            for child in children:
                if child.get_id() == track_id:
                    child.destroy()
                    break
            self._update_tracks()
            self._update_position()
            self._update_headers()
            self._tracks_widget_left.update_indexes(1)
            self._tracks_widget_right.update_indexes(
                                                    len(self._tracks_left) + 1)

#######################
# PRIVATE             #
#######################
    def _add_tracks(self, tracks, widget, pos, previous_album_id=None):
        """
            Add tracks to list
            @param tracks id as array of [int]
            @param widget TracksWidget
            @param track position as int
            @param pos as int
            @param previous album id as int
        """
        if self._loading == Loading.STOP:
            self._loading = Loading.NONE
            return
        if not tracks:
            if widget == self._tracks_widget_right:
                self._loading |= Loading.RIGHT
            elif widget == self._tracks_widget_left:
                self._loading |= Loading.LEFT
            if self._loading == Loading.ALL:
                self.emit('populated')
            self._locked_widget_right = False
            return

        track = Track(tracks.pop(0))
        row = PlaylistRow(track.id, pos,
                          track.album.id != previous_album_id)
        row.connect('track-moved', self._on_track_moved)
        row.show()
        widget.insert(row, pos)
        GLib.idle_add(self._add_tracks, tracks, widget,
                      pos + 1, track.album.id)

    def _update_tracks(self):
        """
            Update tracks based on current widget
        """
        # Recalculate tracks
        self._tracks_left = []
        self._tracks_right = []
        for child in self._tracks_widget_left.get_children():
            self._tracks_left.append(child.get_id())
        for child in self._tracks_widget_right.get_children():
            self._tracks_right.append(child.get_id())

    def _update_position(self):
        """
            Update widget position
        """
        len_tracks1 = len(self._tracks_left)
        len_tracks2 = len(self._tracks_right)
        # Take first track from tracks2 and put it at the end of tracks1
        if len_tracks2 > len_tracks1:
            src = self._tracks_right[0]
            if self._tracks_left:
                dst = self._tracks_left[-1]
            else:
                dst = -1
            self._move_track(dst, src, False)
        # Take last track of tracks1 and put it at the bottom of tracks2
        elif len_tracks1 - 1 > len_tracks2:
            src = self._tracks_left[-1]
            if self._tracks_right:
                dst = self._tracks_right[0]
            else:
                dst = -1
            self._move_track(dst, src, True)
        self._update_tracks()

    def _update_headers(self):
        """
            Update headers for all tracks
        """
        self._tracks_widget_left.update_headers()
        prev_album_id = None
        if self._orientation == Gtk.Orientation.VERTICAL:
            if self._tracks_left:
                prev_album_id = Track(self._tracks_left[-1]).album.id
        self._tracks_widget_right.update_headers(prev_album_id)

    def _move_track(self, dst, src, up):
        """
            Move track from src to row
            @param dst as int
            @param src as int
            @param up as bool
            @return (dst_widget as TracksWidget,
                     src index as int, dst index as int)
        """
        tracks1_len = len(self._tracks_left)
        tracks2_len = len(self._tracks_right)
        if src in self._tracks_left:
            src_widget = self._tracks_widget_left
            src_index = self._tracks_left.index(src) - 1
        else:
            src_widget = self._tracks_widget_right
            src_index = self._tracks_right.index(src) - 1
        if tracks1_len == 0 or dst in self._tracks_left:
            dst_widget = self._tracks_widget_left
            dst_tracks = self._tracks_left
        elif tracks2_len == 0 or dst in self._tracks_right:
            dst_widget = self._tracks_widget_right
            dst_tracks = self._tracks_right
        else:
            return
        # Remove src from src_widget
        for child in src_widget.get_children():
            if child.get_id() == src:
                child.destroy()
                break
        src_track = Track(src)
        prev_track = Track()
        name = escape(src_track.name)
        index = 0
        # Get previous track
        if dst != -1:
            for child in dst_widget.get_children():
                if child.get_id() == dst:
                    break
                index += 1
            if not up:
                index += 1
            # Get previous track (in dst context)
            prev_index = dst_tracks.index(dst)
            if up:
                prev_index -= 1
            prev_track = Track(dst_tracks[prev_index])
            # If we are listening to a compilation, prepend artist name
            if (src_track.album.artist_id == Type.COMPILATIONS or
                    len(src_track.artist_ids) > 1 or
                    src_track.album.artist_id not in src_track.artist_ids):
                name = "<b>%s</b>\n%s" % (escape(", ".join(src_track.artists)),
                                          name)
            self._tracks_left.insert(index, src_track.id)
        row = PlaylistRow(src_track.id,
                          index,
                          index == 0 or
                          src_track.album.id != prev_track.album.id)
        row.connect('track-moved', self._on_track_moved)
        row.show()
        dst_widget.insert(row, index)
        return (src_widget, dst_widget, src_index, index)

    def _on_drag_data_received(self, widget, context, x, y, data, info, time):
        """
            ONLY HAPPEN IN VERTICAL ORIENTATION
            Horizontal orientation is handled by TracksWidget
            @param widget as Gtk.Widget
            @param context as Gdk.DragContext
            @param x as int
            @param y as int
            @param data as Gtk.SelectionData
            @param info as int
            @param time as int
        """
        try:
            try:
                child = self._tracks_widget_right.get_children()[-1]
            except:
                child = self._tracks_widget_left.get_children()[-1]
            self._on_track_moved(widget, child.get_id(),
                                 int(data.get_text()), False)
        except:
            pass

    def _on_track_moved(self, widget, dst, src, up):
        """
            Move track from src to row
            Recalculate track position
            @param widget as TracksWidget
            @param dst as int
            @param src as int
            @param up as bool
        """
        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)

        (src_widget, dst_widget, src_index, dst_index) = \
            self._move_track(dst, src, up)
        self._update_tracks()
        self._update_position()
        self._update_headers()
        self._tracks_widget_left.update_indexes(1)
        self._tracks_widget_right.update_indexes(len(self._tracks_left) + 1)
        t = Thread(target=update_playlist)
        t.daemon = True
        t.start()

    def _on_size_allocate(self, widget, allocation):
        """
            Change box max/min children
            @param widget as Gtk.Widget
            @param allocation as Gtk.Allocation
        """
        if self._width == allocation.width:
            return
        self._width = allocation.width
        redraw = False
        if allocation.width < WindowSize.MONSTER:
            self._box.set_property('valign', Gtk.Align.START)
            orientation = Gtk.Orientation.VERTICAL
        else:
            self._box.set_property('valign', Gtk.Align.FILL)
            orientation = Gtk.Orientation.HORIZONTAL
        if orientation != self._orientation:
            self._orientation = orientation
            redraw = True
        self._box.set_orientation(orientation)
        if redraw:
            for child in self._box.get_children():
                self._box.remove(child)
            GLib.idle_add(self._box.add, self._tracks_widget_left)
            GLib.idle_add(self._box.add, self._tracks_widget_right)
        self._update_headers()

    def _on_activated(self, widget, track_id):
        """
            On track activation, play track
            @param widget as TracksWidget
            @param track as Track
        """
        # Add to queue by default
        if Lp().player.locked or Lp().player.queued:
            if track_id in Lp().player.get_queue():
                Lp().player.del_from_queue(track_id)
            else:
                Lp().player.append_to_queue(track_id)
        else:
            Lp().player.load(Track(track_id))
            if not Lp().player.is_party():
                Lp().player.populate_user_playlist_by_tracks(
                                                            self._tracks_left +
                                                            self._tracks_right,
                                                            self._playlist_ids)
Example #7
0
class PlaylistsWidget(Gtk.Grid):
    """
        Show playlist tracks/albums
    """
    __gsignals__ = {"populated": (GObject.SignalFlags.RUN_FIRST, None, ())}

    def __init__(self, playlist_ids):
        """
            Init playlist Widget
            @param playlist ids as [int]
        """
        Gtk.Grid.__init__(self)
        self.set_row_spacing(5)
        self.set_orientation(Gtk.Orientation.VERTICAL)
        self.__playlist_ids = playlist_ids
        self.__tracks_left = []
        self.__tracks_right = []
        self.__width = None
        self.__orientation = None
        self.__loading = Loading.NONE
        # Used to block widget2 populate while showing one column
        self.__locked_widget_right = True

        self.__grid = Gtk.Grid()
        self.__grid.set_vexpand(True)
        self.__grid.set_column_homogeneous(True)
        self.__grid.show()

        self.connect("size-allocate", self.__on_size_allocate)

        self.__tracks_widget_left = TracksWidget(True)
        self.__tracks_widget_left.set_vexpand(True)
        self.__tracks_widget_right = TracksWidget(True)
        self.__tracks_widget_right.set_vexpand(True)
        self.__tracks_widget_left.connect("activated", self.__on_activated)
        self.__tracks_widget_right.connect("activated", self.__on_activated)
        self.__tracks_widget_left.show()
        self.__tracks_widget_right.show()

        self.drag_dest_set(Gtk.DestDefaults.DROP | Gtk.DestDefaults.MOTION, [],
                           Gdk.DragAction.MOVE)
        self.drag_dest_add_text_targets()
        self.connect("drag-data-received", self.__on_drag_data_received)

        self.add(self.__grid)

    @property
    def id(self):
        """
            Return playlist widget id
            @return int
        """
        return Type.PLAYLISTS

    @property
    def boxes(self):
        """
            @return [Gtk.ListBox]
        """
        return [self.__tracks_widget_left, self.__tracks_widget_right]

    def set_filter_func(self, func):
        """
            Set filter function
        """
        self.__tracks_widget_left.set_filter_func(func)
        self.__tracks_widget_right.set_filter_func(func)

    def show_overlay(self, bool):
        """
            No overlay here now
        """
        pass

    def update_state(self):
        """
            No state to update
        """
        pass

    def update_cover(self):
        """
            No update cover for now
        """
        pass

    def update_allocation(self):
        """
            Update widget allocation
        """
        self.__width = 0
        self.__on_size_allocate(self, self.get_allocation())

    def get_current_ordinate(self):
        """
            If current track in widget, return it ordinate,
            @return y as int
        """
        ordinate = None
        for child in self.__tracks_widget_left.get_children() + \
                self.__tracks_widget_right.get_children():
            if child.id == Lp().player.current_track.id:
                ordinate = child.translate_coordinates(self.__grid, 0, 0)[1]
        return ordinate

    def populate_list_left(self, tracks, pos):
        """
            Populate left list
            @param track"s ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        # We reset width here to allow size allocation code to run
        self.__width = None
        self.__tracks_left = list(tracks)
        GLib.idle_add(self.__add_tracks, tracks, self.__tracks_widget_left,
                      pos)

    def populate_list_right(self, tracks, pos):
        """
            Populate right list
            @param track"s ids as array of int (not null)
            @param track position as int
            @thread safe
        """
        self.__tracks_right = list(tracks)
        # If we are showing only one column, wait for widget1
        if self.__orientation == Gtk.Orientation.VERTICAL and\
           self.__locked_widget_right:
            GLib.timeout_add(100, self.populate_list_right, tracks, pos)
        else:
            # We reset width here to allow size allocation code to run
            self.__width = None
            GLib.idle_add(self.__add_tracks, tracks,
                          self.__tracks_widget_right, pos)

    def update_playing_indicator(self):
        """
            Update playing indicator
        """
        self.__tracks_widget_left.update_playing(Lp().player.current_track.id)
        self.__tracks_widget_right.update_playing(Lp().player.current_track.id)

    def update_duration(self, track_id):
        """
            Update duration for current track
            @param track id as int
        """
        self.__tracks_widget_left.update_duration(track_id)
        self.__tracks_widget_right.update_duration(track_id)

    def stop(self):
        """
            Stop loading
        """
        self.__loading = Loading.STOP

    def insert(self, track_id, pos=-1):
        """
            Add track to widget
            @param track id as int
            @param pos as int
        """
        children_len = len(self.__tracks_widget_left.get_children() +
                           self.__tracks_widget_right.get_children())
        if pos > children_len / 2:
            widget = self.__tracks_widget_right
            pos -= len(self.__tracks_widget_left.get_children())
        else:
            widget = self.__tracks_widget_left
        self.__add_tracks([track_id], widget, pos)
        self.__update_tracks()
        self.__update_position()
        self.__update_headers()
        self.__tracks_widget_left.update_indexes(1)
        self.__tracks_widget_right.update_indexes(len(self.__tracks_left) + 1)

    def remove(self, track_id):
        """
            Del track from widget
            @param track id as int
        """
        children = self.__tracks_widget_left.get_children() + \
            self.__tracks_widget_right.get_children()
        # Clear the widget
        if track_id is None:
            for child in children:
                child.destroy()
            self.__update_tracks()
        else:
            for child in children:
                if child.id == track_id:
                    child.destroy()
                    break
            self.__update_tracks()
            self.__update_position()
            self.__update_headers()
            self.__tracks_widget_left.update_indexes(1)
            self.__tracks_widget_right.update_indexes(
                len(self.__tracks_left) + 1)


#######################
# PRIVATE             #
#######################

    def __add_tracks(self, tracks, widget, pos, previous_album_id=None):
        """
            Add tracks to list
            @param tracks id as array of [int]
            @param widget TracksWidget
            @param track position as int
            @param pos as int
            @param previous album id as int
        """
        if self.__loading == Loading.STOP:
            self.__loading = Loading.NONE
            return
        if not tracks:
            if widget == self.__tracks_widget_right:
                self.__loading |= Loading.RIGHT
            elif widget == self.__tracks_widget_left:
                self.__loading |= Loading.LEFT
            if self.__loading == Loading.ALL:
                self.emit("populated")
            self.__locked_widget_right = False
            return

        track = Track(tracks.pop(0))
        row = PlaylistRow(track.id, pos, track.album.id != previous_album_id)
        row.connect("track-moved", self.__on_track_moved)
        row.show()
        widget.insert(row, pos)
        GLib.idle_add(self.__add_tracks, tracks, widget, pos + 1,
                      track.album.id)

    def __update_tracks(self):
        """
            Update tracks based on current widget
        """
        # Recalculate tracks
        self.__tracks_left = []
        self.__tracks_right = []
        for child in self.__tracks_widget_left.get_children():
            self.__tracks_left.append(child.id)
        for child in self.__tracks_widget_right.get_children():
            self.__tracks_right.append(child.id)

    def __update_position(self):
        """
            Update widget position
        """
        len_tracks1 = len(self.__tracks_left)
        len_tracks2 = len(self.__tracks_right)
        # Take first track from tracks2 and put it at the end of tracks1
        if len_tracks2 > len_tracks1:
            src = self.__tracks_right[0]
            if self.__tracks_left:
                dst = self.__tracks_left[-1]
            else:
                dst = -1
            self.__move_track(dst, src, False)
        # Take last track of tracks1 and put it at the bottom of tracks2
        elif len_tracks1 - 1 > len_tracks2:
            src = self.__tracks_left[-1]
            if self.__tracks_right:
                dst = self.__tracks_right[0]
            else:
                dst = -1
            self.__move_track(dst, src, True)
        self.__update_tracks()

    def __update_headers(self):
        """
            Update headers for all tracks
        """
        self.__tracks_widget_left.update_headers()
        prev_album_id = None
        if self.__orientation == Gtk.Orientation.VERTICAL:
            if self.__tracks_left:
                prev_album_id = Track(self.__tracks_left[-1]).album.id
        self.__tracks_widget_right.update_headers(prev_album_id)

    def __show_spinner(self, widget, track_id):
        """
            Show spinner for widget
            @param widget as TracksWidget
            @param track id as int
        """
        track = Track(track_id)
        if track.is_web:
            widget.show_spinner(track_id)

    def __move_track(self, dst, src, up):
        """
            Move track from src to row
            @param dst as int
            @param src as int
            @param up as bool
            @return (dst_widget as TracksWidget,
                     src index as int, dst index as int)
        """
        tracks1_len = len(self.__tracks_left)
        tracks2_len = len(self.__tracks_right)
        if src in self.__tracks_left:
            src_widget = self.__tracks_widget_left
            src_index = self.__tracks_left.index(src) - 1
        else:
            src_widget = self.__tracks_widget_right
            src_index = self.__tracks_right.index(src) - 1
        if tracks1_len == 0 or dst in self.__tracks_left:
            dst_widget = self.__tracks_widget_left
            dst_tracks = self.__tracks_left
        elif tracks2_len == 0 or dst in self.__tracks_right:
            dst_widget = self.__tracks_widget_right
            dst_tracks = self.__tracks_right
        else:
            return
        # Remove src from src_widget
        for child in src_widget.get_children():
            if child.id == src:
                child.destroy()
                break
        src_track = Track(src)
        prev_track = Track()
        name = GLib.markup_escape_text(src_track.name)
        index = 0
        # Get previous track
        if dst != -1:
            for child in dst_widget.get_children():
                if child.id == dst:
                    break
                index += 1
            if not up:
                index += 1
            # Get previous track (in dst context)
            prev_index = dst_tracks.index(dst)
            if up:
                prev_index -= 1
            prev_track = Track(dst_tracks[prev_index])
            # If we are listening to a compilation, prepend artist name
            if (src_track.album.artist_id == Type.COMPILATIONS
                    or len(src_track.artist_ids) > 1
                    or src_track.album.artist_id not in src_track.artist_ids):
                name = "<b>%s</b>\n%s" % (GLib.markup_escape_text(", ".join(
                    src_track.artists)), name)
            self.__tracks_left.insert(index, src_track.id)
        row = PlaylistRow(
            src_track.id, index, index == 0
            or src_track.album.id != prev_track.album.id)
        row.connect("track-moved", self.__on_track_moved)
        row.show()
        dst_widget.insert(row, index)
        return (src_widget, dst_widget, src_index, index)

    def __on_drag_data_received(self, widget, context, x, y, data, info, time):
        """
            ONLY HAPPEN IN VERTICAL ORIENTATION
            Horizontal orientation is handled by TracksWidget
            @param widget as Gtk.Widget
            @param context as Gdk.DragContext
            @param x as int
            @param y as int
            @param data as Gtk.SelectionData
            @param info as int
            @param time as int
        """
        try:
            value = int(data.get_text())
            try:
                child = self.__tracks_widget_right.get_children()[-1]
            except:
                child = self.__tracks_widget_left.get_children()[-1]
            self.__on_track_moved(widget, child.id, value, False)
        except:
            if len(self.__playlist_ids) == 1:
                Lp().playlists.import_uri(self.__playlist_ids[0],
                                          data.get_text())

    def __on_track_moved(self, widget, dst, src, up):
        """
            Move track from src to row
            Recalculate track position
            @param widget as TracksWidget
            @param dst as int
            @param src as int
            @param up as bool
        """
        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)

        (src_widget, dst_widget, src_index, dst_index) = \
            self.__move_track(dst, src, up)
        self.__update_tracks()
        self.__update_position()
        self.__update_headers()
        self.__tracks_widget_left.update_indexes(1)
        self.__tracks_widget_right.update_indexes(len(self.__tracks_left) + 1)
        t = Thread(target=update_playlist)
        t.daemon = True
        t.start()

    def __on_size_allocate(self, widget, allocation):
        """
            Change box max/min children
            @param widget as Gtk.Widget
            @param allocation as Gtk.Allocation
        """
        if self.__width == allocation.width:
            return
        self.__width = allocation.width
        redraw = False
        if allocation.width < WindowSize.MONSTER or\
                not Lp().settings.get_value("split-view"):
            self.__grid.set_property("valign", Gtk.Align.START)
            orientation = Gtk.Orientation.VERTICAL
        else:
            self.__grid.set_property("valign", Gtk.Align.FILL)
            orientation = Gtk.Orientation.HORIZONTAL
        if orientation != self.__orientation:
            self.__orientation = orientation
            redraw = True
        self.__grid.set_orientation(orientation)
        if redraw:
            for child in self.__grid.get_children():
                self.__grid.remove(child)
            GLib.idle_add(self.__grid.add, self.__tracks_widget_left)
            GLib.idle_add(self.__grid.add, self.__tracks_widget_right)
        self.__update_headers()

    def __on_activated(self, widget, track_id):
        """
            On track activation, play track
            @param widget as TracksWidget
            @param track as Track
        """
        # Add to queue by default
        if Lp().player.locked:
            if track_id in Lp().player.queue:
                Lp().player.del_from_queue(track_id)
            else:
                Lp().player.append_to_queue(track_id)
        else:
            self.__show_spinner(widget, track_id)
            Lp().player.load(Track(track_id))
            if not Lp().player.is_party:
                Lp().player.populate_user_playlist_by_tracks(
                    self.__tracks_left + self.__tracks_right,
                    self.__playlist_ids)
Example #8
0
class PlaylistWidget(Gtk.Bin):
    """
        Init playlist Widget
        @param playlist name as str
    """
    def __init__(self, playlist_name):
        Gtk.Bin.__init__(self)
        self._playlist_name = playlist_name
        self._tracks = []

        self._main_widget = Gtk.Grid()
        self._main_widget.show()

        self._tracks_widget1 = TracksWidget(False)
        self._tracks_widget2 = TracksWidget(False)
        self._tracks_widget1.connect('activated',
                                     self._on_activated, playlist_name)
        self._tracks_widget2.connect('activated',
                                     self._on_activated, playlist_name)
        self._tracks_widget1.show()
        self._tracks_widget2.show()

        size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
        size_group.add_widget(self._tracks_widget1)
        size_group.add_widget(self._tracks_widget2)

        self._main_widget.add(self._tracks_widget1)
        self._main_widget.add(self._tracks_widget2)

        self.add(self._main_widget)

    """
        Populate list one, thread safe
        @param track's ids as array of int
        @param track position as int
    """
    def populate_list_one(self, tracks, pos):
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget1,
                      pos)

    """
        Populate list two, thread safe
        @param track's ids as array of int
        @param track position as int
    """
    def populate_list_two(self, tracks, pos):
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget2,
                      pos)

    """
        Update playing indicator
    """
    def update_playing_indicator(self):
        self._tracks_widget1.update_playing(Objects.player.current.id)
        self._tracks_widget2.update_playing(Objects.player.current.id)

    """
        Clear tracks
    """
    def clear(self):
        self._tracks = []
        for child in self._tracks_widget1.get_children():
            child.destroy()
        for child in self._tracks_widget2.get_children():
            child.destroy()

#######################
# PRIVATE             #
#######################
    """
        Add tracks to list
        @param tracks id as array of [int]
        @param widget TracksWidget
        @param track position as int
    """
    def _add_tracks(self, tracks, widget, pos):
        if not tracks:
            return

        track_id = tracks.pop(0)
        if track_id == -1:
            return

        (title, filepath, length, album_id) =\
            Objects.tracks.get_infos(track_id)

        artist_name = ""
        for artist_id in Objects.tracks.get_artist_ids(track_id):
            artist_name += translate_artist_name(
                            Objects.artists.get_name(artist_id)) + ", "
        title = "<b>%s</b>\n%s" % (escape(artist_name[:-2]),
                                   escape(title))

        widget.add_track(track_id, pos, title, length, None, True)
        GLib.idle_add(self._add_tracks, tracks, widget, pos+1)

    """
        On track activation, play track
        @param widget as TracksWidget
        @param track id as int
        @param playlist name as str
    """
    def _on_activated(self, widget, track_id, playlist_name):
        if not Objects.player.is_party():
            if not self._tracks:
                self._tracks = Objects.playlists.get_tracks_id(playlist_name)
            Objects.player.set_user_playlist(self._tracks, track_id)
        Objects.player.load(track_id)
Example #9
0
class PlaylistWidget(Gtk.Bin):
    """
        Show playlist tracks/albums
    """

    def __init__(self, playlist_id, playlist_name):
        """
            Init playlist Widget
            @param playlist id as int
            @param playlist name as str
        """
        Gtk.Bin.__init__(self)
        self._playlist_id = playlist_id
        self._playlist_name = playlist_name
        self._tracks1 = []
        self._tracks2 = []
        self._stop = False

        self._main_widget = Gtk.Grid()
        self._main_widget.set_property('margin', 10)
        self._main_widget.set_property('column-spacing', 10)
        self._main_widget.show()

        self._tracks_widget1 = TracksWidget(False)
        self._tracks_widget2 = TracksWidget(False)
        self._tracks_widget1.connect('activated',
                                     self._on_activated, playlist_name)
        self._tracks_widget2.connect('activated',
                                     self._on_activated, playlist_name)
        self._tracks_widget1.show()
        self._tracks_widget2.show()

        size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
        size_group.add_widget(self._tracks_widget1)
        size_group.add_widget(self._tracks_widget2)

        self._main_widget.add(self._tracks_widget1)
        self._main_widget.add(self._tracks_widget2)

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

        spinner = Gtk.Spinner()
        spinner.start()
        spinner.show()
        self._stack.add(spinner)
        self._stack.add(self._main_widget)
        self._stack.set_visible_child(spinner)

        self.add(self._stack)

    def populate_list_left(self, tracks, pos):
        """
            Populate left list
            @param track's ids as array of int
            @param track position as int
            @thread safe
        """
        GLib.idle_add(self._stack.set_visible_child, self._main_widget)
        self._stop = False
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget1,
                      pos)

    def populate_list_right(self, tracks, pos):
        """
            Populate right list
            @param track's ids as array of int
            @param track position as int
            @thread safe
        """
        self._stop = False
        GLib.idle_add(self._add_tracks,
                      tracks,
                      self._tracks_widget2,
                      pos)

    def update_playing_indicator(self):
        """
            Update playing indicator
        """
        self._tracks_widget1.update_playing(Lp.player.current_track.id)
        self._tracks_widget2.update_playing(Lp.player.current_track.id)

    def stop(self):
        """
            Stop loading
        """
        self._stop = True

    def clear(self):
        """
            Clear tracks
        """
        self._tracks = []
        for child in self._tracks_widget1.get_children():
            child.destroy()
        for child in self._tracks_widget2.get_children():
            child.destroy()

#######################
# PRIVATE             #
#######################
    def _add_tracks(self, tracks, widget, pos, previous_album_id=None):
        """
            Add tracks to list
            @param tracks id as array of [int]
            @param widget TracksWidget
            @param track position as int
            @param previous album id as int
        """
        if not tracks or self._stop:
            return

        track_id = tracks.pop(0)
        if track_id == -1:
            return

        (title, filepath, length, album_id) =\
            Lp.tracks.get_infos(track_id)
        if title is None:
            return
        else:
            title = escape(title)

        artist_id = Lp.albums.get_artist_id(album_id)
        artist_ids = Lp.tracks.get_artist_ids(track_id)
        # If we are listening to a compilation, prepend artist name
        if artist_id == Type.COMPILATIONS or\
           len(artist_ids) > 1 or\
           artist_id not in artist_ids:
            artist_name = ""
            for artist_id in artist_ids:
                if artist_name != "":
                    artist_name += ", "
                artist_name += Lp.artists.get_name(artist_id)
            title = "<b>%s</b>\n%s" % (escape(artist_name),
                                       title)

        if widget == self._tracks_widget1:
            self._tracks1.append(Track(track_id))
        else:
            self._tracks2.append(Track(track_id))

        if album_id != previous_album_id:
            widget.add_album(track_id, album_id, pos, title, length, None)
        else:
            widget.add_album(track_id, None, pos, title, length, None)
        GLib.idle_add(self._add_tracks, tracks, widget, pos+1, album_id)

    def _on_activated(self, widget, track_id, playlist_name):
        """
            On track activation, play track
            @param widget as TracksWidget
            @param track as Track
            @param playlist name as str
        """
        if Lp.player.is_party():
            Lp.player.load(Track(track_id))
        else:
            track = Lp.player.set_user_playlist(self._tracks1 + self._tracks2,
                                                track_id)
            Lp.player.set_user_playlist_id(self._playlist_id)
            if track is not None:
                Lp.player.load(track)
Example #10
0
class PlaylistWidget(Gtk.Bin):
    """
        Init playlist Widget
        @param playlist name as str
    """
    def __init__(self, playlist_name):
        Gtk.Bin.__init__(self)
        self._playlist_name = playlist_name
        self._tracks = []

        self._main_widget = Gtk.Grid()
        self._main_widget.show()

        self._tracks_widget1 = TracksWidget(False)
        self._tracks_widget2 = TracksWidget(False)
        self._tracks_widget1.connect('activated', self._on_activated,
                                     playlist_name)
        self._tracks_widget2.connect('activated', self._on_activated,
                                     playlist_name)
        self._tracks_widget1.show()
        self._tracks_widget2.show()

        size_group = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL)
        size_group.add_widget(self._tracks_widget1)
        size_group.add_widget(self._tracks_widget2)

        self._main_widget.add(self._tracks_widget1)
        self._main_widget.add(self._tracks_widget2)

        self.add(self._main_widget)

    """
        Populate list one, thread safe
        @param track's ids as array of int
        @param track position as int
    """

    def populate_list_one(self, tracks, pos):
        GLib.idle_add(self._add_tracks, tracks, self._tracks_widget1, pos)

    """
        Populate list two, thread safe
        @param track's ids as array of int
        @param track position as int
    """

    def populate_list_two(self, tracks, pos):
        GLib.idle_add(self._add_tracks, tracks, self._tracks_widget2, pos)

    """
        Update playing indicator
    """

    def update_playing_indicator(self):
        self._tracks_widget1.update_playing(Objects.player.current.id)
        self._tracks_widget2.update_playing(Objects.player.current.id)

    """
        Clear tracks
    """

    def clear(self):
        self._tracks = []
        for child in self._tracks_widget1.get_children():
            child.destroy()
        for child in self._tracks_widget2.get_children():
            child.destroy()

#######################
# PRIVATE             #
#######################

    """
        Add tracks to list
        @param tracks id as array of [int]
        @param widget TracksWidget
        @param track position as int
    """
    def _add_tracks(self, tracks, widget, pos):
        if not tracks:
            return

        track_id = tracks.pop(0)
        if track_id == -1:
            return

        (title, filepath, length, album_id) =\
            Objects.tracks.get_infos(track_id)

        artist_name = ""
        for artist_id in Objects.tracks.get_artist_ids(track_id):
            artist_name += translate_artist_name(
                Objects.artists.get_name(artist_id)) + ", "
        title = "<b>%s</b>\n%s" % (escape(artist_name[:-2]), escape(title))

        widget.add_track(track_id, pos, title, length, None, True)
        GLib.idle_add(self._add_tracks, tracks, widget, pos + 1)

    """
        On track activation, play track
        @param widget as TracksWidget
        @param track id as int
        @param playlist name as str
    """

    def _on_activated(self, widget, track_id, playlist_name):
        if not Objects.player.is_party():
            if not self._tracks:
                self._tracks = Objects.playlists.get_tracks_id(playlist_name)
            Objects.player.set_user_playlist(self._tracks, track_id)
        Objects.player.load(track_id)