non-ID3 browsing of Artists

This commit is contained in:
Sumner Evans
2019-07-28 21:14:10 -06:00
parent 1d0ac1a9b8
commit 8f27a7c830
4 changed files with 61 additions and 33 deletions

View File

@@ -25,6 +25,7 @@ from libremsonic.server.api_objects import (
# Non-ID3 versions # Non-ID3 versions
Artist, Artist,
ArtistInfo, ArtistInfo,
Directory,
# ID3 versions # ID3 versions
ArtistID3, ArtistID3,
@@ -127,8 +128,8 @@ class CacheManager(metaclass=Singleton):
# Non-ID3 caches # Non-ID3 caches
('albums', Child, list), ('albums', Child, list),
('artists', Child, list), ('artists', Artist, list),
# ('artist_details', None, dict), TODO ('artist_details', Directory, dict),
('artist_infos', ArtistInfo, dict), ('artist_infos', ArtistInfo, dict),
# ID3 caches # ID3 caches
@@ -302,7 +303,7 @@ class CacheManager(metaclass=Singleton):
# TODO: implement the non-ID3 version # TODO: implement the non-ID3 version
cache_name = f"artist_details{'_id3' if self.browse_by_tags else ''}" cache_name = f"artist_details{'_id3' if self.browse_by_tags else ''}"
server_fn = (self.server.get_artist 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: if artist_id not in self.cache.get(cache_name, {}) or force:
before_download() before_download()
@@ -345,7 +346,7 @@ class CacheManager(metaclass=Singleton):
def get_artist_artwork( def get_artist_artwork(
self, self,
artist: ArtistID3, artist: Union[Artist, ArtistID3],
before_download: Callable[[], None] = lambda: None, before_download: Callable[[], None] = lambda: None,
force: bool = False, force: bool = False,
) -> Future: ) -> Future:
@@ -356,8 +357,14 @@ class CacheManager(metaclass=Singleton):
# If it is the placeholder LastFM URL, then just use the cover # If it is the placeholder LastFM URL, then just use the cover
# art filename given by the server. # art filename given by the server.
if lastfm_url == 'https://lastfm-img2.akamaized.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png': if lastfm_url == 'https://lastfm-img2.akamaized.net/i/u/300x300/2a96cbd8b46e442fc41c2b86b821562f.png':
return CacheManager.get_cover_art_filename( if isinstance(artist, ArtistWithAlbumsID3):
artist.coverArt, size=300).result() 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() url_hash = hashlib.md5(lastfm_url.encode('utf-8')).hexdigest()
return self.return_cache_or_download( return self.return_cache_or_download(

View File

@@ -1,5 +1,5 @@
import gi import gi
from typing import List from typing import Optional
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gio, Gtk, GObject, Pango from gi.repository import Gio, Gtk, GObject, Pango
@@ -47,7 +47,7 @@ class AlbumsGrid(CoverArtGrid):
def get_header_text(self, item: AlbumModel) -> str: def get_header_text(self, item: AlbumModel) -> str:
return item.title 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) return util.dot_join(item.artist, item.year)
def get_model_list_future(self, before_download): def get_model_list_future(self, before_download):

View File

@@ -1,5 +1,5 @@
from concurrent.futures import Future from concurrent.futures import Future
from typing import List, Union from typing import List, Union, Optional
import gi import gi
@@ -130,15 +130,22 @@ class ArtistsGrid(CoverArtGrid):
def get_header_text(self, item) -> str: def get_header_text(self, item) -> str:
return item.name return item.name
def get_info_text(self, item) -> str: def get_info_text(self, item) -> Optional[str]:
return (str(item.album_count) + ' ' if item.album_count:
+ util.pluralize('album', 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]: def get_model_list_future(self, before_download) -> List[ArtistID3]:
return CacheManager.get_artists(before_download=before_download) return CacheManager.get_artists(before_download=before_download)
def create_model_from_element(self, el): 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: def get_cover_art_filename_future(self, item, before_download) -> Future:
# TODO convert to get_artist_artwork # TODO convert to get_artist_artwork
@@ -190,10 +197,12 @@ class ArtistDetailPanel(Gtk.Box):
self.artist_artwork = Gtk.Image(name='artist-album-artwork') self.artist_artwork = Gtk.Image(name='artist-album-artwork')
artwork_overlay.add(self.artist_artwork) artwork_overlay.add(self.artist_artwork)
self.artwork_spinner = Gtk.Spinner(name='artist-artwork-spinner', self.artwork_spinner = Gtk.Spinner(
active=True, name='artist-artwork-spinner',
halign=Gtk.Align.CENTER, active=True,
valign=Gtk.Align.CENTER) halign=Gtk.Align.CENTER,
valign=Gtk.Align.CENTER,
)
artwork_overlay.add_overlay(self.artwork_spinner) artwork_overlay.add_overlay(self.artwork_spinner)
self.big_info_panel.pack_start(artwork_overlay, False, False, 0) 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, self.artist_action_buttons.pack_end(view_refresh_button, False, False,
5) 5)
download_all_button = util.button_with_icon('folder-download-symbolic') download_all_btn = util.button_with_icon('folder-download-symbolic')
download_all_button.connect('clicked', self.on_download_all_click) download_all_btn.connect('clicked', self.on_download_all_click)
self.artist_action_buttons.pack_end(download_all_button, False, False, self.artist_action_buttons.pack_end(download_all_btn, False, False, 5)
5)
artist_details_box.pack_start(self.artist_action_buttons, False, False, artist_details_box.pack_start(self.artist_action_buttons, False, False,
5) 5)
@@ -278,7 +286,7 @@ class ArtistDetailPanel(Gtk.Box):
self.update_artist_info(artist.id) self.update_artist_info(artist.id)
self.update_artist_artwork(artist) self.update_artist_artwork(artist)
self.albums = artist.album self.albums = artist.get('album', artist.get('child', []))
self.albums_grid.update() self.albums_grid.update()
@util.async_callback( @util.async_callback(
@@ -340,11 +348,22 @@ class ArtistDetailPanel(Gtk.Box):
self.artwork_spinner.hide() self.artwork_spinner.hide()
def format_stats(self, artist): def format_stats(self, artist):
song_count = sum(a.songCount for a in artist.album) album_count = artist.get('albumCount', len(artist.get('child', [])))
duration = sum(a.duration for a in artist.album) components = [
return util.dot_join( '{} {}'.format(album_count, util.pluralize('album', album_count)),
'{} {}'.format(artist.albumCount, ]
util.pluralize('album', artist.albumCount)),
'{} {}'.format(song_count, util.pluralize('song', song_count)), if artist.get('album'):
util.format_sequence_duration(duration), song_count = sum(a.songCount for a in artist.album)
) duration = sum(a.duration for a in artist.album)
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)

View File

@@ -1,4 +1,5 @@
from concurrent.futures import Future from concurrent.futures import Future
from typing import Optional
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
@@ -97,8 +98,9 @@ class CoverArtGrid(Gtk.ScrolledWindow):
# Extra info for the widget # Extra info for the widget
info_text = self.get_info_text(item) info_text = self.get_info_text(item)
info_label = make_label(info_text, 'grid-info-label') if info_text:
widget_box.pack_start(info_label, False, False, 0) info_label = make_label(info_text, 'grid-info-label')
widget_box.pack_start(info_label, False, False, 0)
# Download the cover art. # Download the cover art.
def on_artwork_downloaded(f): def on_artwork_downloaded(f):
@@ -122,7 +124,7 @@ class CoverArtGrid(Gtk.ScrolledWindow):
'get_header_text must be implemented by the inheritor of CoverArtGrid.' '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( raise NotImplementedError(
'get_info_text must be implemented by the inheritor of CoverArtGrid.' 'get_info_text must be implemented by the inheritor of CoverArtGrid.'
) )