Resolves #26 Playlist list now updates when the playlist is edited/deleted
This commit is contained in:
@@ -134,7 +134,7 @@ class LibremsonicApp(Gtk.Application):
|
||||
self.on_stack_change,
|
||||
)
|
||||
self.window.connect('song-clicked', self.on_song_clicked)
|
||||
self.window.connect('force-refresh', self.on_force_refresh)
|
||||
self.window.connect('refresh-window', self.on_refresh_window)
|
||||
self.window.player_controls.connect('song-scrub', self.on_song_scrub)
|
||||
self.window.player_controls.connect('device-update',
|
||||
self.on_device_update)
|
||||
@@ -152,6 +152,8 @@ class LibremsonicApp(Gtk.Application):
|
||||
if (self.state.config.current_server is None
|
||||
or self.state.config.current_server < 0):
|
||||
self.show_configure_servers_dialog()
|
||||
if self.state.config.current_server is None:
|
||||
self.window.close()
|
||||
|
||||
self.update_window()
|
||||
|
||||
@@ -171,6 +173,16 @@ class LibremsonicApp(Gtk.Application):
|
||||
self.save_play_queue()
|
||||
|
||||
def on_track_end():
|
||||
current_idx = self.state.play_queue.index(
|
||||
self.state.current_song.id)
|
||||
|
||||
if (current_idx == len(self.state.play_queue) - 1
|
||||
and self.state.repeat_type == RepeatType.NO_REPEAT):
|
||||
self.state.playing = False
|
||||
self.state.current_song = None
|
||||
GLib.idle_add(self.update_window)
|
||||
return
|
||||
|
||||
GLib.idle_add(self.on_next_track)
|
||||
|
||||
def on_player_event(event: PlayerEvent):
|
||||
@@ -210,10 +222,10 @@ class LibremsonicApp(Gtk.Application):
|
||||
self.update_play_state_from_server(prompt_confirm=True)
|
||||
|
||||
# ########## ACTION HANDLERS ########## #
|
||||
def on_force_refresh(self, _, state_updates):
|
||||
def on_refresh_window(self, _, state_updates, force=False):
|
||||
for k, v in state_updates.items():
|
||||
setattr(self.state, k, v)
|
||||
self.update_window()
|
||||
self.update_window(force=force)
|
||||
|
||||
def on_configure_servers(self, action, param):
|
||||
self.show_configure_servers_dialog()
|
||||
@@ -480,8 +492,8 @@ class LibremsonicApp(Gtk.Application):
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
def update_window(self):
|
||||
GLib.idle_add(self.window.update, self.state)
|
||||
def update_window(self, force=False):
|
||||
GLib.idle_add(lambda: self.window.update(self.state, force=force))
|
||||
|
||||
def update_play_state_from_server(self, prompt_confirm=False):
|
||||
# TODO need to make the up next list loading for the duration here
|
||||
@@ -499,6 +511,7 @@ class LibremsonicApp(Gtk.Application):
|
||||
if prompt_confirm:
|
||||
# If there's not a significant enough difference, don't prompt.
|
||||
if (self.state.play_queue == new_play_queue
|
||||
and self.state.current_song
|
||||
and self.state.current_song.id == new_current_song_id
|
||||
and abs(self.state.song_progress - new_song_progress) <
|
||||
15):
|
||||
|
@@ -54,6 +54,7 @@ class ApplicationState:
|
||||
selected_album_id: str = None
|
||||
selected_artist_id: str = None
|
||||
selected_playlist_id: str = None
|
||||
current_album_sort: str = 'random'
|
||||
|
||||
def to_json(self):
|
||||
current_song = (self.current_song.id if
|
||||
@@ -74,6 +75,7 @@ class ApplicationState:
|
||||
'selected_artist_id': getattr(self, 'selected_artist_id', None),
|
||||
'selected_playlist_id': getattr(self, 'selected_playlist_id',
|
||||
None),
|
||||
'current_album_sort': getattr(self, 'current_album_sort', None),
|
||||
}
|
||||
|
||||
def load_from_json(self, json_object):
|
||||
@@ -97,6 +99,8 @@ class ApplicationState:
|
||||
self.selected_artist_id = json_object.get('selected_artist_id', None)
|
||||
self.selected_playlist_id = json_object.get('selected_playlist_id',
|
||||
None)
|
||||
self.current_album_sort = json_object.get('current_album_sort',
|
||||
'random')
|
||||
|
||||
def load(self):
|
||||
self.config = self.get_config(self.config_file)
|
||||
|
@@ -21,14 +21,13 @@ class AlbumsPanel(Gtk.Box):
|
||||
GObject.TYPE_NONE,
|
||||
(str, object, object),
|
||||
),
|
||||
'force-refresh': (
|
||||
'refresh-window': (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
(object, ),
|
||||
(object, bool),
|
||||
),
|
||||
}
|
||||
|
||||
currently_active_sort: str = 'random'
|
||||
currently_active_alphabetical_sort: str = 'name'
|
||||
currently_active_genre: str = 'Rock'
|
||||
currently_active_from_year: int = 2010
|
||||
@@ -136,8 +135,10 @@ class AlbumsPanel(Gtk.Box):
|
||||
lambda f: GLib.idle_add(get_genres_done, f))
|
||||
|
||||
def update(self, state: ApplicationState = None, force: bool = False):
|
||||
if state:
|
||||
self.sort_type_combo.set_active_id(state.current_album_sort)
|
||||
|
||||
# TODO store this in state
|
||||
self.sort_type_combo.set_active_id(self.currently_active_sort)
|
||||
self.alphabetical_type_combo.set_active_id(
|
||||
self.currently_active_alphabetical_sort)
|
||||
self.from_year_entry.set_text(str(self.currently_active_from_year))
|
||||
@@ -148,7 +149,7 @@ class AlbumsPanel(Gtk.Box):
|
||||
# Show/hide the combo boxes.
|
||||
def show_if(sort_type, *elements):
|
||||
for element in elements:
|
||||
if self.currently_active_sort == sort_type:
|
||||
if state.current_album_sort == sort_type:
|
||||
element.show()
|
||||
else:
|
||||
element.hide()
|
||||
@@ -166,10 +167,13 @@ class AlbumsPanel(Gtk.Box):
|
||||
return combo.get_model()[tree_iter][0]
|
||||
|
||||
def on_type_combo_changed(self, combo):
|
||||
self.currently_active_sort = self.get_id(combo)
|
||||
if self.grid.type_ != self.currently_active_sort:
|
||||
self.grid.update_params(type_=self.currently_active_sort)
|
||||
self.update(force=True)
|
||||
new_active_sort = self.get_id(combo)
|
||||
self.grid.update_params(type_=new_active_sort)
|
||||
self.emit(
|
||||
'refresh-window',
|
||||
{'current_album_sort': new_active_sort},
|
||||
False,
|
||||
)
|
||||
|
||||
def on_alphabetical_type_change(self, combo):
|
||||
self.currently_active_alphabetical_sort = self.get_id(combo)
|
||||
@@ -190,6 +194,7 @@ class AlbumsPanel(Gtk.Box):
|
||||
try:
|
||||
year = int(entry.get_text())
|
||||
except:
|
||||
# TODO
|
||||
print('failed, should do something to prevent non-numeric input')
|
||||
return
|
||||
|
||||
@@ -216,7 +221,7 @@ class AlbumModel(GObject.Object):
|
||||
|
||||
class AlbumsGrid(CoverArtGrid):
|
||||
"""Defines the albums panel."""
|
||||
type_: str = 'random'
|
||||
type_: str
|
||||
alphabetical_type: str = 'name'
|
||||
from_year: int = 2010
|
||||
to_year: int = 2020
|
||||
|
@@ -27,10 +27,10 @@ class ArtistsPanel(Gtk.Paned):
|
||||
GObject.TYPE_NONE,
|
||||
(str, object, object),
|
||||
),
|
||||
'force-refresh': (
|
||||
'refresh-window': (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
(object, ),
|
||||
(object, bool),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class ArtistsPanel(Gtk.Paned):
|
||||
)
|
||||
self.pack2(self.artist_detail_panel, True, False)
|
||||
|
||||
def update(self, state: ApplicationState):
|
||||
def update(self, state: ApplicationState, force=False):
|
||||
self.artist_list.update(state=state)
|
||||
self.artist_detail_panel.update(state=state)
|
||||
|
||||
@@ -313,6 +313,7 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
cover_art_filename,
|
||||
state: ApplicationState,
|
||||
):
|
||||
print(cover_art_filename)
|
||||
self.artist_artwork.set_from_file(cover_art_filename)
|
||||
self.artist_artwork.set_loading(False)
|
||||
|
||||
|
@@ -72,6 +72,7 @@ class AlbumWithSongs(Gtk.Box):
|
||||
label=album.get('name', album.get('title')),
|
||||
name='artist-album-list-album-name',
|
||||
halign=Gtk.Align.START,
|
||||
ellipsize=Pango.EllipsizeMode.END,
|
||||
))
|
||||
|
||||
self.play_btn = IconButton('media-playback-start-symbolic',
|
||||
|
@@ -103,9 +103,10 @@ class ConfigureServersDialog(Gtk.Dialog):
|
||||
flowbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
|
||||
# Server List
|
||||
self.server_list = Gtk.ListBox()
|
||||
self.server_list = Gtk.ListBox(activate_on_single_click=False)
|
||||
self.server_list.connect('selected-rows-changed',
|
||||
self.server_list_on_selected_rows_changed)
|
||||
self.server_list.connect('row-activated', self.on_server_list_activate)
|
||||
flowbox.pack_start(self.server_list, True, True, 10)
|
||||
|
||||
button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||
@@ -152,12 +153,21 @@ class ConfigureServersDialog(Gtk.Dialog):
|
||||
self.server_list.remove(el)
|
||||
|
||||
# Add all of the rows for each of the servers.
|
||||
for config in self.server_configs:
|
||||
row = Gtk.ListBoxRow()
|
||||
for i, config in enumerate(self.server_configs):
|
||||
box = Gtk.Box()
|
||||
image = Gtk.Image(margin=5)
|
||||
if i == self.selected_server_index:
|
||||
image.set_from_icon_name(
|
||||
'network-transmit-receive-symbolic',
|
||||
Gtk.IconSize.SMALL_TOOLBAR,
|
||||
)
|
||||
|
||||
box.add(image)
|
||||
|
||||
server_name_label = Gtk.Label(label=config.name)
|
||||
server_name_label.set_halign(Gtk.Align.START)
|
||||
row.add(server_name_label)
|
||||
self.server_list.add(row)
|
||||
box.add(server_name_label)
|
||||
self.server_list.add(box)
|
||||
|
||||
# Show them, and select the current server.
|
||||
self.show_all()
|
||||
@@ -206,6 +216,9 @@ class ConfigureServersDialog(Gtk.Dialog):
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
def on_server_list_activate(self, *args):
|
||||
self.on_connect_clicked(None)
|
||||
|
||||
def on_connect_clicked(self, event):
|
||||
selected_index = self.server_list.get_selected_row().get_index()
|
||||
self.emit('connected-server-changed', selected_index)
|
||||
|
@@ -14,10 +14,10 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
GObject.TYPE_NONE,
|
||||
(str, object, object),
|
||||
),
|
||||
'force-refresh': (
|
||||
'refresh-window': (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
(object, ),
|
||||
(object, bool),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
flowbox.pack_start(self.player_controls, False, True, 0)
|
||||
self.add(flowbox)
|
||||
|
||||
def update(self, state: ApplicationState):
|
||||
def update(self, state: ApplicationState, force=False):
|
||||
# Update the Connected to label on the popup menu.
|
||||
if state.config.current_server >= 0:
|
||||
server_name = state.config.servers[
|
||||
@@ -61,7 +61,7 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
|
||||
active_panel = self.stack.get_visible_child()
|
||||
if hasattr(active_panel, 'update'):
|
||||
active_panel.update(state)
|
||||
active_panel.update(state, force=force)
|
||||
|
||||
self.player_controls.update(state)
|
||||
|
||||
@@ -73,8 +73,8 @@ class MainWindow(Gtk.ApplicationWindow):
|
||||
lambda _, *args: self.emit('song-clicked', *args),
|
||||
)
|
||||
child.connect(
|
||||
'force-refresh',
|
||||
lambda _, *args: self.emit('force-refresh', *args),
|
||||
'refresh-window',
|
||||
lambda _, *args: self.emit('refresh-window', *args),
|
||||
)
|
||||
stack.add_titled(child, name.lower(), name)
|
||||
return stack
|
||||
|
@@ -59,9 +59,10 @@ class PlayerControls(Gtk.ActionBar):
|
||||
self.pack_end(play_queue_volume)
|
||||
|
||||
def update(self, state: ApplicationState):
|
||||
if hasattr(state, 'current_song') and state.current_song is not None:
|
||||
self.update_scrubber(state.song_progress,
|
||||
state.current_song.duration)
|
||||
self.update_scrubber(
|
||||
getattr(state, 'song_progress', None),
|
||||
getattr(state.current_song, 'duration', None),
|
||||
)
|
||||
|
||||
icon = 'pause' if state.playing else 'start'
|
||||
self.play_button.set_icon(f"media-playback-{icon}-symbolic")
|
||||
@@ -120,10 +121,13 @@ class PlayerControls(Gtk.ActionBar):
|
||||
self.song_title.set_text(util.esc(state.current_song.title))
|
||||
self.album_name.set_text(util.esc(state.current_song.album))
|
||||
artist_name = util.esc(state.current_song.artist)
|
||||
if artist_name:
|
||||
self.artist_name.set_text(artist_name)
|
||||
self.artist_name.set_text(artist_name or '')
|
||||
else:
|
||||
# TODO should probably clear out the cover art display if no song??
|
||||
# Clear out the cover art and song tite if no song
|
||||
self.album_art.set_from_file(None)
|
||||
self.song_title.set_text('')
|
||||
self.album_name.set_text('')
|
||||
self.artist_name.set_text('')
|
||||
self.album_art.set_loading(False)
|
||||
|
||||
# Set the Play Queue button popup.
|
||||
@@ -137,7 +141,8 @@ class PlayerControls(Gtk.ActionBar):
|
||||
f'<b>Play Queue:</b> {play_queue_len} {song_label}')
|
||||
|
||||
new_model = [
|
||||
PlayerControls.PlayQueueSong(s, s == state.current_song.id)
|
||||
PlayerControls.PlayQueueSong(
|
||||
s, has_current_song and s == state.current_song.id)
|
||||
for s in state.play_queue
|
||||
]
|
||||
util.diff_model_store(self.play_queue_store, new_model)
|
||||
@@ -152,6 +157,12 @@ class PlayerControls(Gtk.ActionBar):
|
||||
self.album_art.set_loading(False)
|
||||
|
||||
def update_scrubber(self, current, duration):
|
||||
if current is None and duration is None:
|
||||
self.song_duration_label.set_text('-:--')
|
||||
self.song_progress_label.set_text('-:--')
|
||||
self.song_scrubber.set_value(0)
|
||||
return
|
||||
|
||||
current = current or 0
|
||||
percent_complete = current / duration * 100
|
||||
|
||||
|
@@ -35,10 +35,10 @@ class PlaylistsPanel(Gtk.Paned):
|
||||
GObject.TYPE_NONE,
|
||||
(str, object, object),
|
||||
),
|
||||
'force-refresh': (
|
||||
'refresh-window': (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
(object, ),
|
||||
(object, bool),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -54,22 +54,22 @@ class PlaylistsPanel(Gtk.Paned):
|
||||
lambda _, *args: self.emit('song-clicked', *args),
|
||||
)
|
||||
self.playlist_detail_panel.connect(
|
||||
'force-refresh',
|
||||
lambda _, *args: self.emit('force-refresh', *args),
|
||||
'refresh-window',
|
||||
lambda _, *args: self.emit('refresh-window', *args),
|
||||
)
|
||||
self.pack2(self.playlist_detail_panel, True, False)
|
||||
|
||||
def update(self, state: ApplicationState = None):
|
||||
def update(self, state: ApplicationState = None, force=False):
|
||||
self.playlist_list.update(state=state)
|
||||
self.playlist_detail_panel.update(state=state)
|
||||
|
||||
|
||||
class PlaylistList(Gtk.Box):
|
||||
__gsignals__ = {
|
||||
'force-refresh': (
|
||||
'refresh-window': (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
(object, ),
|
||||
(object, bool),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -238,10 +238,10 @@ class PlaylistDetailPanel(Gtk.Overlay):
|
||||
GObject.TYPE_NONE,
|
||||
(str, object, object),
|
||||
),
|
||||
'force-refresh': (
|
||||
'refresh-window': (
|
||||
GObject.SignalFlags.RUN_FIRST,
|
||||
GObject.TYPE_NONE,
|
||||
(object, ),
|
||||
(object, bool),
|
||||
),
|
||||
}
|
||||
|
||||
@@ -540,11 +540,14 @@ class PlaylistDetailPanel(Gtk.Overlay):
|
||||
CacheManager.delete_cached_cover_art(self.playlist_id)
|
||||
CacheManager.invalidate_playlists_cache()
|
||||
self.emit(
|
||||
'force-refresh', {
|
||||
'refresh-window',
|
||||
{
|
||||
'selected_playlist_id':
|
||||
None if result == Gtk.ResponseType.DELETE_EVENT else
|
||||
self.playlist_id
|
||||
})
|
||||
},
|
||||
True,
|
||||
)
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
|
Reference in New Issue
Block a user