Added artist grid

This commit is contained in:
Sumner Evans
2019-07-06 02:06:57 -06:00
parent 127550f057
commit 76bd690e2f
4 changed files with 140 additions and 14 deletions

View File

@@ -146,6 +146,8 @@ class LibremsonicApp(Gtk.Application):
or self.state.config.current_server < 0):
self.show_configure_servers_dialog()
self.update_window()
# ########## ACTION HANDLERS ########## #
def on_configure_servers(self, action, param):
self.show_configure_servers_dialog()

View File

@@ -13,7 +13,13 @@ from typing import Dict, List, Optional, Union, Callable, Set
from libremsonic.config import AppConfiguration, ServerConfiguration
from libremsonic.server import Server
from libremsonic.server.api_object import APIObject
from libremsonic.server.api_objects import Playlist, PlaylistWithSongs, Child
from libremsonic.server.api_objects import (
Playlist,
PlaylistWithSongs,
Child,
ArtistID3,
ArtistWithAlbumsID3,
)
class Singleton(type):
@@ -53,9 +59,13 @@ class CacheManager(metaclass=Singleton):
return json.JSONEncoder.default(self, obj)
class __CacheManagerInternal:
# NOTE: you need to add custom load/store logic for everything you add
# here!
server: Server
playlists: Optional[List[Playlist]] = None
artists: Optional[List[ArtistID3]] = None
playlist_details: Dict[int, PlaylistWithSongs] = {}
arist_details: Dict[int, ArtistWithAlbumsID3] = {}
permanently_cached_paths: Set[str] = set()
song_details: Dict[int, Child] = {}
@@ -100,10 +110,17 @@ class CacheManager(metaclass=Singleton):
Playlist.from_json(p)
for p in meta_json.get('playlists') or []
]
self.artists = [
ArtistID3.from_json(a) for a in meta_json.get('artists') or []
]
self.playlist_details = {
id: PlaylistWithSongs.from_json(v)
for id, v in (meta_json.get('playlist_details') or {}).items()
}
self.artist_details = {
id: ArtistWithAlbumsID3.from_json(a)
for id, a in (meta_json.get('artist_details') or {}).items()
}
self.song_details = {
id: Child.from_json(v)
for id, v in (meta_json.get('song_details') or {}).items()
@@ -118,7 +135,9 @@ class CacheManager(metaclass=Singleton):
with open(cache_meta_file, 'w+') as f, self.cache_lock:
cache_info = dict(
playlists=self.playlists,
artists=self.artists,
playlist_details=self.playlist_details,
artist_details=self.artist_details,
song_details=self.song_details,
permanently_cached_paths=list(
self.permanently_cached_paths),
@@ -189,8 +208,9 @@ class CacheManager(metaclass=Singleton):
def do_get_playlists() -> List[Playlist]:
if not self.playlists or force:
before_download()
playlists = self.server.get_playlists().playlist
with self.cache_lock:
self.playlists = self.server.get_playlists().playlist
self.playlists = playlists
self.save_cache_info()
return self.playlists
@@ -209,8 +229,8 @@ class CacheManager(metaclass=Singleton):
with self.cache_lock:
self.playlist_details[playlist_id] = playlist
# Playlists also have song details, so save that as
# well.
# Playlists also have the song details, so save those
# as well.
for song in (playlist.entry or []):
self.song_details[song.id] = song
@@ -228,6 +248,29 @@ class CacheManager(metaclass=Singleton):
return CacheManager.executor.submit(do_get_playlist)
def get_artists(
self,
before_download: Callable[[], None] = lambda: None,
force: bool = False,
) -> Future:
def do_get_artists() -> List[ArtistID3]:
if not self.artists or force:
before_download()
raw_artists = self.server.get_artists()
artists = []
for index in raw_artists.index:
artists.extend(index.artist)
with self.cache_lock:
self.artists = artists
self.save_cache_info()
return self.artists
return CacheManager.executor.submit(do_get_artists)
def batch_download_songs(
self,
song_ids: List[int],
@@ -281,9 +324,9 @@ class CacheManager(metaclass=Singleton):
def do_get_song_details() -> Child:
if not self.song_details.get(song_id) or force:
before_download()
song_details = self.server.get_song(song_id)
with self.cache_lock:
self.song_details[song_id] = self.server.get_song(
song_id)
self.song_details[song_id] = song_details
self.save_cache_info()
return self.song_details[song_id]

View File

@@ -27,12 +27,12 @@
margin: 5px 10px 10px 0;
}
#playlist-artwork-spinner {
#playlist-artwork-spinner, #artist-artwork-spinner {
min-height: 35px;
min-width: 35px;
}
#playlist-album-artwork {
#playlist-album-artwork, #artist-artwork {
min-height: 200px;
min-width: 200px;
margin: 10px;
@@ -97,6 +97,13 @@
margin: 10px;
}
/* General */
.menu-button {
padding: 5px;
}
/* ********** Artists ********** */
.artist-box {
min-width: 200px;
min-height: 230px;
}

View File

@@ -1,9 +1,15 @@
from typing import List
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject
from gi.repository import Gtk, GObject, Gio, Pango
from libremsonic.state_manager import ApplicationState
from libremsonic.cache_manager import CacheManager
from libremsonic.ui import util
from libremsonic.server.api_objects import ArtistID3
class ArtistsPanel(Gtk.ScrolledWindow):
@@ -24,23 +30,91 @@ class ArtistsPanel(Gtk.ScrolledWindow):
self.child.update(state)
class ArtistModel(GObject.Object):
def __init__(self, name, cover_art):
self.name = name
self.cover_art = cover_art
super().__init__()
class ArtistsGrid(Gtk.FlowBox):
"""Defines the albums panel."""
"""Defines the artists panel."""
def __init__(self):
Gtk.FlowBox.__init__(
self,
vexpand=True,
hexpand=True,
row_spacing=12,
column_spacing=12,
row_spacing=10,
column_spacing=10,
margin_top=12,
margin_bottom=12,
homogeneous=True,
valign=Gtk.Align.START,
halign=Gtk.Align.CENTER,
selection_mode=Gtk.SelectionMode.NONE,
selection_mode=Gtk.SelectionMode.BROWSE,
)
self.artist_model = Gio.ListStore()
self.bind_model(self.artist_model, self.create_artist_widget)
def update(self, state: ApplicationState):
pass
self.update_grid()
@util.async_callback(
lambda *a, **k: CacheManager.get_artists(*a, **k),
before_download=lambda self: print('ohea'),
on_failure=lambda self, e: print('fail', e),
)
def update_grid(self, artists: List[ArtistID3]):
# TODO do the diff thing eventually?
self.artist_model.remove_all()
for artist in artists:
self.artist_model.append(ArtistModel(artist.name, artist.coverArt))
def create_artist_widget(self, item):
artist_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
artwork_overlay = Gtk.Overlay()
artist_artwork = Gtk.Image(name='artist-artwork')
artwork_overlay.add(artist_artwork)
artwork_spinner = Gtk.Spinner(name='artist-artwork-spinner',
active=False,
halign=Gtk.Align.CENTER,
valign=Gtk.Align.CENTER)
artwork_overlay.add_overlay(artwork_spinner)
artist_box.pack_start(artwork_overlay, False, False, 0)
def artwork_downloaded(f):
filename = f.result()
artist_artwork.set_from_file(filename)
artwork_spinner.active = False
def before_download():
artwork_spinner.active = True
cover_art_filename_future = CacheManager.get_cover_art_filename(
item.cover_art, before_download=before_download)
cover_art_filename_future.add_done_callback(artwork_downloaded)
name_label = Gtk.Label(
name='artist-name-label',
label=item.name,
ellipsize=Pango.EllipsizeMode.END,
max_width_chars=20,
)
artist_box.pack_end(name_label, False, False, 0)
artist_box.show_all()
return artist_box
@util.async_callback(
lambda *a, **k: CacheManager.get_cover_art_filename(*a, **k),
before_download=lambda self: self.set_playlist_art_loading(True),
on_failure=lambda self, e: self.set_playlist_art_loading(False),
)
def update_playlist_artwork(self, cover_art_filename):
self.playlist_artwork.set_from_file(cover_art_filename)
self.set_playlist_art_loading(False)