Resolves #26 Playlist list now updates when the playlist is edited/deleted

This commit is contained in:
Sumner Evans
2019-10-04 19:56:51 -06:00
parent 5d9df23b7d
commit 465309de62
9 changed files with 98 additions and 47 deletions

View File

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

View File

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

View 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

View File

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

View File

@@ -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',

View File

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

View File

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

View File

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

View File

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