Expand/collapse for artists

This commit is contained in:
Sumner Evans
2020-05-20 07:45:37 -06:00
parent 7e626b0cc3
commit 4cf1d9d7e1
5 changed files with 124 additions and 66 deletions

View File

@@ -515,7 +515,7 @@ class FilesystemAdapter(CachingAdapter):
"year": getattr(api_album, "year", None), "year": getattr(api_album, "year", None),
"genre": ingest_genre_data(g) if (g := api_album.genre) else None, "genre": ingest_genre_data(g) if (g := api_album.genre) else None,
"artist": ingest_artist_data(ar) if (ar := api_album.artist) else None, "artist": ingest_artist_data(ar) if (ar := api_album.artist) else None,
"songs": [ "_songs": [
ingest_song_data(s, fill_album=False) for s in api_album.songs or [] ingest_song_data(s, fill_album=False) for s in api_album.songs or []
], ],
"_cover_art": self._do_ingest_new_data( "_cover_art": self._do_ingest_new_data(

View File

@@ -108,6 +108,15 @@ class Album(BaseModel):
except Exception: except Exception:
return None return None
@property
def songs(self) -> List["Song"]:
albums = Album.select()
artists = Album.select()
return sorted(
prefetch(self._songs, albums, artists),
key=lambda s: (s.disc_number or 1, s.track),
)
class AlbumQueryResult(BaseModel): class AlbumQueryResult(BaseModel):
query_hash = TextField(primary_key=True) query_hash = TextField(primary_key=True)
@@ -144,7 +153,7 @@ class Song(BaseModel):
duration = DurationField(null=True) duration = DurationField(null=True)
parent_id = TextField(null=True) parent_id = TextField(null=True)
album = ForeignKeyField(Album, null=True, backref="songs") album = ForeignKeyField(Album, null=True, backref="_songs")
artist = ForeignKeyField(Artist, null=True) artist = ForeignKeyField(Artist, null=True)
genre = ForeignKeyField(Genre, null=True, backref="songs") genre = ForeignKeyField(Genre, null=True, backref="songs")

View File

@@ -64,12 +64,17 @@
margin: 10px 15px 0 10px; margin: 10px 15px 0 10px;
} }
#artist-info-panel {
margin-bottom: 10px;
}
#playlist-name, #artist-detail-panel #artist-name { #playlist-name, #artist-detail-panel #artist-name {
font-size: 40px; font-size: 40px;
margin-bottom: 10px; margin-bottom: 10px;
} }
#playlist-name.collapsed { #playlist-name.collapsed,
#artist-detail-panel #artist-name.collapsed {
font-size: 30px; font-size: 30px;
} }
@@ -204,9 +209,7 @@
} }
#artist-album-artwork { #artist-album-artwork {
margin: 10px; margin: 10px 15px 0 10px;
min-width: 300px;
min-height: 300px;
} }
#artist-album-list-artwork { #artist-album-list-artwork {

View File

@@ -1,6 +1,6 @@
from datetime import timedelta from datetime import timedelta
from random import randint from random import randint
from typing import Any, List, Sequence from typing import List, Sequence
from gi.repository import Gio, GLib, GObject, Gtk, Pango from gi.repository import Gio, GLib, GObject, Gtk, Pango
@@ -36,6 +36,9 @@ class ArtistsPanel(Gtk.Paned):
self.artist_detail_panel.connect( self.artist_detail_panel.connect(
"song-clicked", lambda _, *args: self.emit("song-clicked", *args), "song-clicked", lambda _, *args: self.emit("song-clicked", *args),
) )
self.artist_detail_panel.connect(
"refresh-window", lambda _, *args: self.emit("refresh-window", *args),
)
self.pack2(self.artist_detail_panel, True, False) self.pack2(self.artist_detail_panel, True, False)
def update(self, app_config: AppConfiguration, force: bool = False): def update(self, app_config: AppConfiguration, force: bool = False):
@@ -145,7 +148,7 @@ class ArtistList(Gtk.Box):
self.loading_indicator.hide() self.loading_indicator.hide()
class ArtistDetailPanel(Gtk.ScrolledWindow): class ArtistDetailPanel(Gtk.Box):
"""Defines the artists list.""" """Defines the artists list."""
__gsignals__ = { __gsignals__ = {
@@ -154,19 +157,30 @@ class ArtistDetailPanel(Gtk.ScrolledWindow):
GObject.TYPE_NONE, GObject.TYPE_NONE,
(int, object, object), (int, object, object),
), ),
"refresh-window": (
GObject.SignalFlags.RUN_FIRST,
GObject.TYPE_NONE,
(object, bool),
),
} }
update_order_token = 0 update_order_token = 0
artist_details_expanded = False
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, name="artist-detail-panel", **kwargs) super().__init__(
*args,
name="artist-detail-panel",
orientation=Gtk.Orientation.VERTICAL,
**kwargs,
)
self.albums: Sequence[API.Album] = [] self.albums: Sequence[API.Album] = []
self.artist_id = None self.artist_id = None
artist_info_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# Artist info panel # Artist info panel
self.big_info_panel = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) self.big_info_panel = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL, name="artist-info-panel"
)
self.artist_artwork = SpinnerImage( self.artist_artwork = SpinnerImage(
loading=False, loading=False,
@@ -179,22 +193,6 @@ class ArtistDetailPanel(Gtk.ScrolledWindow):
# Action buttons, name, comment, number of songs, etc. # Action buttons, name, comment, number of songs, etc.
artist_details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) artist_details_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
# Action buttons (note we are packing end here, so we have to put them
# in right-to-left).
self.artist_action_buttons = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
view_refresh_button = IconButton("view-refresh-symbolic", "Refresh artist info")
view_refresh_button.connect("clicked", self.on_view_refresh_click)
self.artist_action_buttons.pack_end(view_refresh_button, False, False, 5)
download_all_btn = IconButton(
"folder-download-symbolic", "Download all songs by this artist"
)
download_all_btn.connect("clicked", self.on_download_all_click)
self.artist_action_buttons.pack_end(download_all_btn, False, False, 5)
artist_details_box.pack_start(self.artist_action_buttons, False, False, 5)
artist_details_box.pack_start(Gtk.Box(), True, False, 0) artist_details_box.pack_start(Gtk.Box(), True, False, 0)
self.artist_indicator = self.make_label(name="artist-indicator") self.artist_indicator = self.make_label(name="artist-indicator")
@@ -246,35 +244,55 @@ class ArtistDetailPanel(Gtk.ScrolledWindow):
self.big_info_panel.pack_start(artist_details_box, True, True, 10) self.big_info_panel.pack_start(artist_details_box, True, True, 10)
artist_info_box.pack_start(self.big_info_panel, False, True, 0) # Action buttons
action_buttons_container = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.artist_action_buttons = Gtk.Box(
orientation=Gtk.Orientation.HORIZONTAL, spacing=10
)
download_all_btn = IconButton(
"folder-download-symbolic", "Download all songs by this artist"
)
download_all_btn.connect("clicked", self.on_download_all_click)
self.artist_action_buttons.add(download_all_btn)
view_refresh_button = IconButton("view-refresh-symbolic", "Refresh artist info")
view_refresh_button.connect("clicked", self.on_view_refresh_click)
self.artist_action_buttons.add(view_refresh_button)
action_buttons_container.pack_start(
self.artist_action_buttons, False, False, 10
)
action_buttons_container.pack_start(Gtk.Box(), True, True, 0)
expand_button_container = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
self.expand_collapse_button = IconButton(
"pan-up-symbolic", "Expand playlist details"
)
self.expand_collapse_button.connect("clicked", self.on_expand_collapse_click)
expand_button_container.pack_end(self.expand_collapse_button, False, False, 0)
action_buttons_container.add(expand_button_container)
self.big_info_panel.pack_start(action_buttons_container, False, False, 5)
self.pack_start(self.big_info_panel, False, True, 0)
self.album_list_scrolledwindow = Gtk.ScrolledWindow()
self.albums_list = AlbumsListWithSongs() self.albums_list = AlbumsListWithSongs()
self.albums_list.connect( self.albums_list.connect(
"song-clicked", lambda _, *args: self.emit("song-clicked", *args), "song-clicked", lambda _, *args: self.emit("song-clicked", *args),
) )
artist_info_box.pack_start(self.albums_list, True, True, 0) self.album_list_scrolledwindow.add(self.albums_list)
self.pack_start(self.album_list_scrolledwindow, True, True, 0)
self.add(artist_info_box)
def update(self, app_config: AppConfiguration): def update(self, app_config: AppConfiguration):
self.artist_id = app_config.state.selected_artist_id self.artist_id = app_config.state.selected_artist_id
if app_config.state.selected_artist_id is None: if app_config.state.selected_artist_id is None:
self.artist_action_buttons.hide() self.hide()
self.artist_indicator.set_text("")
self.artist_name.set_markup("")
self.artist_stats.set_markup("")
self.artist_bio.set_markup("")
self.similar_artists_scrolledwindow.hide()
self.play_shuffle_buttons.hide()
self.artist_artwork.set_from_file(None)
self.albums = []
self.albums_list.update(None)
else: else:
self.update_order_token += 1 self.update_order_token += 1
self.artist_action_buttons.show() self.show()
self.update_artist_view( self.update_artist_view(
app_config.state.selected_artist_id, app_config.state.selected_artist_id,
app_config=app_config, app_config=app_config,
@@ -296,28 +314,50 @@ class ArtistDetailPanel(Gtk.ScrolledWindow):
if order_token != self.update_order_token: if order_token != self.update_order_token:
return return
self.artist_indicator.set_text("ARTIST") if app_config:
self.artist_details_expanded = app_config.state.artist_details_expanded
up_down = "up" if self.artist_details_expanded else "down"
self.expand_collapse_button.set_icon(f"pan-{up_down}-symbolic")
self.expand_collapse_button.set_tooltip_text(
"Collapse" if self.artist_details_expanded else "Expand"
)
self.artist_name.set_markup(util.esc(f"<b>{artist.name}</b>")) self.artist_name.set_markup(util.esc(f"<b>{artist.name}</b>"))
self.artist_stats.set_markup(self.format_stats(artist)) self.artist_name.set_tooltip_text(artist.name)
self.artist_bio.set_markup(util.esc(artist.biography)) if self.artist_details_expanded:
self.show_all()
self.artist_artwork.set_image_size(300)
self.artist_indicator.set_text("ARTIST")
self.artist_stats.set_markup(self.format_stats(artist))
if len(artist.similar_artists or []) > 0: self.artist_bio.set_markup(util.esc(artist.biography))
self.similar_artists_label.set_markup("<b>Similar Artists:</b> ")
for c in self.similar_artists_button_box.get_children():
self.similar_artists_button_box.remove(c)
for artist in (artist.similar_artists or [])[:5]: if len(artist.similar_artists or []) > 0:
self.similar_artists_button_box.add( self.similar_artists_label.set_markup("<b>Similar Artists:</b> ")
Gtk.LinkButton( for c in self.similar_artists_button_box.get_children():
label=artist.name, self.similar_artists_button_box.remove(c)
name="similar-artist-button",
action_name="app.go-to-artist", for artist in (artist.similar_artists or [])[:5]:
action_target=GLib.Variant("s", artist.id), self.similar_artists_button_box.add(
Gtk.LinkButton(
label=artist.name,
name="similar-artist-button",
action_name="app.go-to-artist",
action_target=GLib.Variant("s", artist.id),
)
) )
) self.similar_artists_scrolledwindow.show_all()
self.similar_artists_scrolledwindow.show_all() else:
self.similar_artists_scrolledwindow.hide()
else: else:
self.artist_name.get_style_context().add_class("collapsed")
self.show_all()
self.artist_artwork.set_image_size(70)
self.artist_indicator.hide()
self.artist_stats.hide()
self.artist_bio.hide()
self.similar_artists_scrolledwindow.hide() self.similar_artists_scrolledwindow.hide()
self.play_shuffle_buttons.show_all() self.play_shuffle_buttons.show_all()
@@ -353,7 +393,7 @@ class ArtistDetailPanel(Gtk.ScrolledWindow):
self.artist_id, force=True, order_token=self.update_order_token, self.artist_id, force=True, order_token=self.update_order_token,
) )
def on_download_all_click(self, btn: Any): def on_download_all_click(self, _):
AdapterManager.batch_download_songs( AdapterManager.batch_download_songs(
self.get_artist_song_ids(), self.get_artist_song_ids(),
before_download=lambda _: self.update_artist_view( before_download=lambda _: self.update_artist_view(
@@ -364,13 +404,13 @@ class ArtistDetailPanel(Gtk.ScrolledWindow):
), ),
) )
def on_play_all_clicked(self, btn: Any): def on_play_all_clicked(self, _):
songs = self.get_artist_song_ids() songs = self.get_artist_song_ids()
self.emit( self.emit(
"song-clicked", 0, songs, {"force_shuffle_state": False}, "song-clicked", 0, songs, {"force_shuffle_state": False},
) )
def on_shuffle_all_button(self, btn: Any): def on_shuffle_all_button(self, _):
songs = self.get_artist_song_ids() songs = self.get_artist_song_ids()
self.emit( self.emit(
"song-clicked", "song-clicked",
@@ -379,6 +419,13 @@ class ArtistDetailPanel(Gtk.ScrolledWindow):
{"force_shuffle_state": True}, {"force_shuffle_state": True},
) )
def on_expand_collapse_click(self, _):
self.emit(
"refresh-window",
{"artist_details_expanded": not self.artist_details_expanded},
False,
)
# Helper Methods # Helper Methods
# ========================================================================= # =========================================================================
def set_all_loading(self, loading_state: bool): def set_all_loading(self, loading_state: bool):

View File

@@ -673,7 +673,6 @@ class PlaylistDetailPanel(Gtk.Overlay):
) )
def on_expand_collapse_click(self, _): def on_expand_collapse_click(self, _):
# TODO
self.emit( self.emit(
"refresh-window", "refresh-window",
{"playlist_details_expanded": not self.playlist_details_expanded}, {"playlist_details_expanded": not self.playlist_details_expanded},