non-ID3 browsing of Artists
This commit is contained in:
@@ -25,6 +25,7 @@ from libremsonic.server.api_objects import (
|
||||
# Non-ID3 versions
|
||||
Artist,
|
||||
ArtistInfo,
|
||||
Directory,
|
||||
|
||||
# ID3 versions
|
||||
ArtistID3,
|
||||
@@ -127,8 +128,8 @@ class CacheManager(metaclass=Singleton):
|
||||
|
||||
# Non-ID3 caches
|
||||
('albums', Child, list),
|
||||
('artists', Child, list),
|
||||
# ('artist_details', None, dict), TODO
|
||||
('artists', Artist, list),
|
||||
('artist_details', Directory, dict),
|
||||
('artist_infos', ArtistInfo, dict),
|
||||
|
||||
# ID3 caches
|
||||
@@ -302,7 +303,7 @@ class CacheManager(metaclass=Singleton):
|
||||
# TODO: implement the non-ID3 version
|
||||
cache_name = f"artist_details{'_id3' if self.browse_by_tags else ''}"
|
||||
server_fn = (self.server.get_artist if self.browse_by_tags else
|
||||
lambda: 'not implemented')
|
||||
self.server.get_music_directory)
|
||||
|
||||
if artist_id not in self.cache.get(cache_name, {}) or force:
|
||||
before_download()
|
||||
@@ -345,7 +346,7 @@ class CacheManager(metaclass=Singleton):
|
||||
|
||||
def get_artist_artwork(
|
||||
self,
|
||||
artist: ArtistID3,
|
||||
artist: Union[Artist, ArtistID3],
|
||||
before_download: Callable[[], None] = lambda: None,
|
||||
force: bool = False,
|
||||
) -> Future:
|
||||
@@ -356,8 +357,14 @@ class CacheManager(metaclass=Singleton):
|
||||
# If it is the placeholder LastFM URL, then just use the cover
|
||||
# art filename given by the server.
|
||||
if lastfm_url == 'https://lastfm-img2.akamaized.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png':
|
||||
if isinstance(artist, ArtistWithAlbumsID3):
|
||||
return CacheManager.get_cover_art_filename(
|
||||
artist.coverArt, size=300).result()
|
||||
elif (isinstance(artist, Directory)
|
||||
and len(artist.child) > 0):
|
||||
# Retrieve the first album's cover art
|
||||
return CacheManager.get_cover_art_filename(
|
||||
artist.child[0].coverArt, size=300).result()
|
||||
|
||||
url_hash = hashlib.md5(lastfm_url.encode('utf-8')).hexdigest()
|
||||
return self.return_cache_or_download(
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import gi
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
|
||||
gi.require_version('Gtk', '3.0')
|
||||
from gi.repository import Gio, Gtk, GObject, Pango
|
||||
@@ -47,7 +47,7 @@ class AlbumsGrid(CoverArtGrid):
|
||||
def get_header_text(self, item: AlbumModel) -> str:
|
||||
return item.title
|
||||
|
||||
def get_info_text(self, item: AlbumModel) -> str:
|
||||
def get_info_text(self, item: AlbumModel) -> Optional[str]:
|
||||
return util.dot_join(item.artist, item.year)
|
||||
|
||||
def get_model_list_future(self, before_download):
|
||||
|
@@ -1,5 +1,5 @@
|
||||
from concurrent.futures import Future
|
||||
from typing import List, Union
|
||||
from typing import List, Union, Optional
|
||||
|
||||
import gi
|
||||
|
||||
@@ -130,15 +130,22 @@ class ArtistsGrid(CoverArtGrid):
|
||||
def get_header_text(self, item) -> str:
|
||||
return item.name
|
||||
|
||||
def get_info_text(self, item) -> str:
|
||||
def get_info_text(self, item) -> Optional[str]:
|
||||
if item.album_count:
|
||||
return (str(item.album_count) + ' '
|
||||
+ util.pluralize('album', item.album_count))
|
||||
return None
|
||||
|
||||
def get_model_list_future(self, before_download) -> List[ArtistID3]:
|
||||
return CacheManager.get_artists(before_download=before_download)
|
||||
|
||||
def create_model_from_element(self, el):
|
||||
return ArtistModel(el.id, el.name, el.coverArt, el.albumCount)
|
||||
return ArtistModel(
|
||||
el.id,
|
||||
el.name,
|
||||
getattr(el, 'coverArt', None),
|
||||
getattr(el, 'albumCount', None),
|
||||
)
|
||||
|
||||
def get_cover_art_filename_future(self, item, before_download) -> Future:
|
||||
# TODO convert to get_artist_artwork
|
||||
@@ -190,10 +197,12 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
self.artist_artwork = Gtk.Image(name='artist-album-artwork')
|
||||
artwork_overlay.add(self.artist_artwork)
|
||||
|
||||
self.artwork_spinner = Gtk.Spinner(name='artist-artwork-spinner',
|
||||
self.artwork_spinner = Gtk.Spinner(
|
||||
name='artist-artwork-spinner',
|
||||
active=True,
|
||||
halign=Gtk.Align.CENTER,
|
||||
valign=Gtk.Align.CENTER)
|
||||
valign=Gtk.Align.CENTER,
|
||||
)
|
||||
artwork_overlay.add_overlay(self.artwork_spinner)
|
||||
self.big_info_panel.pack_start(artwork_overlay, False, False, 0)
|
||||
|
||||
@@ -210,10 +219,9 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
self.artist_action_buttons.pack_end(view_refresh_button, False, False,
|
||||
5)
|
||||
|
||||
download_all_button = util.button_with_icon('folder-download-symbolic')
|
||||
download_all_button.connect('clicked', self.on_download_all_click)
|
||||
self.artist_action_buttons.pack_end(download_all_button, False, False,
|
||||
5)
|
||||
download_all_btn = util.button_with_icon('folder-download-symbolic')
|
||||
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)
|
||||
@@ -278,7 +286,7 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
self.update_artist_info(artist.id)
|
||||
self.update_artist_artwork(artist)
|
||||
|
||||
self.albums = artist.album
|
||||
self.albums = artist.get('album', artist.get('child', []))
|
||||
self.albums_grid.update()
|
||||
|
||||
@util.async_callback(
|
||||
@@ -340,11 +348,22 @@ class ArtistDetailPanel(Gtk.Box):
|
||||
self.artwork_spinner.hide()
|
||||
|
||||
def format_stats(self, artist):
|
||||
album_count = artist.get('albumCount', len(artist.get('child', [])))
|
||||
components = [
|
||||
'{} {}'.format(album_count, util.pluralize('album', album_count)),
|
||||
]
|
||||
|
||||
if artist.get('album'):
|
||||
song_count = sum(a.songCount for a in artist.album)
|
||||
duration = sum(a.duration for a in artist.album)
|
||||
return util.dot_join(
|
||||
'{} {}'.format(artist.albumCount,
|
||||
util.pluralize('album', artist.albumCount)),
|
||||
components += [
|
||||
'{} {}'.format(song_count, util.pluralize('song', song_count)),
|
||||
util.format_sequence_duration(duration),
|
||||
)
|
||||
]
|
||||
elif artist.get('child'):
|
||||
plays = sum(c.playCount for c in artist.child)
|
||||
components += [
|
||||
'{} {}'.format(plays, util.pluralize('play', plays)),
|
||||
]
|
||||
|
||||
return util.dot_join(*components)
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from concurrent.futures import Future
|
||||
from typing import Optional
|
||||
|
||||
import gi
|
||||
gi.require_version('Gtk', '3.0')
|
||||
@@ -97,6 +98,7 @@ class CoverArtGrid(Gtk.ScrolledWindow):
|
||||
|
||||
# Extra info for the widget
|
||||
info_text = self.get_info_text(item)
|
||||
if info_text:
|
||||
info_label = make_label(info_text, 'grid-info-label')
|
||||
widget_box.pack_start(info_label, False, False, 0)
|
||||
|
||||
@@ -122,7 +124,7 @@ class CoverArtGrid(Gtk.ScrolledWindow):
|
||||
'get_header_text must be implemented by the inheritor of CoverArtGrid.'
|
||||
)
|
||||
|
||||
def get_info_text(self, item) -> str:
|
||||
def get_info_text(self, item) -> Optional[str]:
|
||||
raise NotImplementedError(
|
||||
'get_info_text must be implemented by the inheritor of CoverArtGrid.'
|
||||
)
|
||||
|
Reference in New Issue
Block a user