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)
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)
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))
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)
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)
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)
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)
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)
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)
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)