diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5cf75cf..9360259 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -19,6 +19,8 @@ v0.11.1 * Fixed issue where users couldn't log in to LMS due to Sublime Music always sending version number "1.15.0" instead of figuring out what version of the API the server actually reports. +* Fixed issue where edits to the music provider configurations were applied even + if ESC was pressed. (#247) v0.11.0 ======= diff --git a/sublime/adapters/adapter_base.py b/sublime/adapters/adapter_base.py index 56e538f..e97b10d 100644 --- a/sublime/adapters/adapter_base.py +++ b/sublime/adapters/adapter_base.py @@ -438,7 +438,6 @@ class Adapter(abc.ABC): Examples of values that could be provided include ``http``, ``https``, ``file``, or ``ftp``. """ - # TODO (#189) actually use this return () @property @@ -446,18 +445,19 @@ class Adapter(abc.ABC): """ Whether or not the adapter supports :class:`get_cover_art_uri`. """ + return False @property - def can_stream(self) -> bool: + def can_get_song_file_uri(self) -> bool: """ - Whether or not the adapter can provide a stream URI. + Whether or not the adapter supports :class:`get_song_file_uri`. """ return False @property - def can_get_song_uri(self) -> bool: + def can_get_song_stream_uri(self) -> bool: """ - Whether or not the adapter supports :class:`get_song_uri`. + Whether or not the adapter supports :class:`get_song_stream_uri`. """ return False @@ -645,20 +645,26 @@ class Adapter(abc.ABC): """ raise self._check_can_error("get_cover_art_uri") - def get_song_uri(self, song_id: str, scheme: str, stream: bool = False) -> str: + def get_song_file_uri(self, song_id: str, schemes: Iterable[str]) -> str: """ - Get a URI for a given song. + Get a URI for a given song. This URI must give the full file. :param song_id: The ID of the song to get a URI for. - :param scheme: The URI scheme that should be returned. It is guaranteed that - ``scheme`` will be one of the schemes returned by + :param schemes: A set of URI schemes that can be returned. It is guaranteed that + all of the items in ``schemes`` will be one of the schemes returned by :class:`supported_schemes`. - :param stream: Whether or not the URI returned should be a stream URI. This will - only be ``True`` if :class:`supports_streaming` returns ``True``. :returns: The URI for the given song. """ - # TODO (#189) - raise self._check_can_error("get_song_uri") + raise self._check_can_error("get_song_file_uri") + + def get_song_stream_uri(self, song_id: str) -> str: + """ + Get a URI for streaming the given song. + + :param song_id: The ID of the song to get the stream URI for. + :returns: the stream URI for the given song. + """ + raise self._check_can_error("get_song_stream_uri") def get_song_details(self, song_id: str) -> Song: """ diff --git a/sublime/adapters/filesystem/adapter.py b/sublime/adapters/filesystem/adapter.py index fc595e0..473f18c 100644 --- a/sublime/adapters/filesystem/adapter.py +++ b/sublime/adapters/filesystem/adapter.py @@ -101,7 +101,7 @@ class FilesystemAdapter(CachingAdapter): # TODO (#200) make these dependent on cache state. Need to do this kinda efficiently can_get_cover_art_uri = True - can_get_song_uri = True + can_get_song_file_uri = True can_get_song_details = True can_get_artist = True can_get_albums = True @@ -286,7 +286,7 @@ class FilesystemAdapter(CachingAdapter): raise CacheMissError() - def get_song_uri(self, song_id: str, scheme: str, stream: bool = False) -> str: + def get_song_file_uri(self, song_id: str, schemes: Iterable[str]) -> str: song = models.Song.get_or_none(models.Song.id == song_id) if not song: if self.is_cache: diff --git a/sublime/adapters/manager.py b/sublime/adapters/manager.py index fec7904..466ab5f 100644 --- a/sublime/adapters/manager.py +++ b/sublime/adapters/manager.py @@ -539,17 +539,6 @@ class AdapterManager: return future_finished - @staticmethod - def _get_scheme() -> str: - # TODO (#189): eventually this will come from the players - assert AdapterManager._instance - scheme_priority = ("https", "http") - schemes = sorted( - AdapterManager._instance.ground_truth_adapter.supported_schemes, - key=scheme_priority.index, - ) - return list(schemes)[0] - @staticmethod def get_supported_artist_query_types() -> Set[AlbumSearchQuery.Type]: assert AdapterManager._instance @@ -666,13 +655,17 @@ class AdapterManager: return AdapterManager._ground_truth_can_do("delete_playlist") @staticmethod - def can_get_song_filename_or_stream() -> bool: - return AdapterManager._ground_truth_can_do("get_song_uri") + def can_get_song_file_uri() -> bool: + return AdapterManager._ground_truth_can_do("get_song_file_uri") + + @staticmethod + def can_get_song_stream_uri() -> bool: + return AdapterManager._ground_truth_can_do("get_song_stream_uri") @staticmethod def can_batch_download_songs() -> bool: # We can only download from the ground truth adapter. - return AdapterManager._ground_truth_can_do("get_song_uri") + return AdapterManager._ground_truth_can_do("get_song_file_uri") @staticmethod def can_get_genres() -> bool: @@ -805,122 +798,133 @@ class AdapterManager: CachingAdapter.CachedDataKey.PLAYLIST_DETAILS, playlist_id ) - # TODO (#189): allow this to take a set of schemes and unify with - # get_cover_art_filename @staticmethod - def get_cover_art_uri(cover_art_id: str = None, size: int = 300) -> str: + def _get_networked_scheme() -> str: assert AdapterManager._instance + networked_scheme_priority = ("https", "http") + return sorted( + AdapterManager._instance.ground_truth_adapter.supported_schemes, + key=lambda s: networked_scheme_priority.index(s), + )[0] + + @staticmethod + def get_cover_art_uri( + cover_art_id: Optional[str], + scheme: str, + size: int = 300, + before_download: Callable[[], None] = None, + force: bool = False, + allow_download: bool = True, + ) -> Result[str]: + existing_filename = str( + Path(__file__).parent.joinpath("images/default-album-art.png") + ) if ( not AdapterManager._ground_truth_can_do("get_cover_art_uri") or not cover_art_id ): - return "" - - return AdapterManager._instance.ground_truth_adapter.get_cover_art_uri( - cover_art_id, AdapterManager._get_scheme(), size=size - ) - - @staticmethod - def get_cover_art_filename( - cover_art_id: str = None, - before_download: Callable[[], None] = None, - force: bool = False, # TODO (#202): rename to use_ground_truth_adapter? - allow_download: bool = True, - ) -> Result[str]: - existing_cover_art_filename = str( - Path(__file__).parent.joinpath("images/default-album-art.png") - ) - if cover_art_id is None: - return Result(existing_cover_art_filename) + return Result(existing_filename if scheme == "file" else "") assert AdapterManager._instance - - # If the ground truth adapter can't provide cover art, just give up immediately. - if not AdapterManager._ground_truth_can_do("get_cover_art_uri"): - return Result(existing_cover_art_filename) - - # There could be partial data if the cover art exists, but for some reason was - # marked out-of-date. - if AdapterManager._can_use_cache(force, "get_cover_art_uri"): - assert AdapterManager._instance.caching_adapter - try: - return Result( - AdapterManager._instance.caching_adapter.get_cover_art_uri( - cover_art_id, "file", size=300 - ) - ) - except CacheMissError as e: - if e.partial_data is not None: - existing_cover_art_filename = cast(str, e.partial_data) - logging.info(f'Cache Miss on {"get_cover_art_uri"}.') - except Exception: - logging.exception( - f'Error on {"get_cover_art_uri"} retrieving from cache.' - ) - - if AdapterManager._instance.caching_adapter and force: - AdapterManager._instance.caching_adapter.invalidate_data( - CachingAdapter.CachedDataKey.COVER_ART_FILE, cover_art_id - ) - - if not allow_download or ( - AdapterManager._offline_mode - and AdapterManager._instance.ground_truth_adapter.is_networked - ): - return Result(existing_cover_art_filename) - - future: Result[str] = AdapterManager._create_download_result( - AdapterManager._instance.ground_truth_adapter.get_cover_art_uri( - cover_art_id, AdapterManager._get_scheme(), size=300 - ), - cover_art_id, - before_download, - default_value=existing_cover_art_filename, + supported_schemes = ( + AdapterManager._instance.ground_truth_adapter.supported_schemes ) - if AdapterManager._instance.caching_adapter: - future.add_done_callback( - AdapterManager._create_caching_done_callback( + # If the scheme is supported natively, then return it. + if scheme in supported_schemes: + uri = AdapterManager._instance.ground_truth_adapter.get_cover_art_uri( + cover_art_id, scheme, size=size + ) + return Result(uri) + + # If the scheme is "file", then we may need to try to download. + if scheme == "file" and ( + "http" in supported_schemes or "https" in supported_schemes + ): + if AdapterManager._can_use_cache(force, "get_cover_art_uri"): + assert AdapterManager._instance.caching_adapter + try: + return Result( + AdapterManager._instance.caching_adapter.get_cover_art_uri( + cover_art_id, "file", size=size + ) + ) + except CacheMissError as e: + if e.partial_data is not None: + existing_filename = cast(str, e.partial_data) + logging.info("Cache Miss on get_cover_art_uri.") + except Exception: + logging.exception( + "Error on get_cover_art_uri retrieving from cache." + ) + + # If we are forcing, invalidate the existing cached data. + if AdapterManager._instance.caching_adapter and force: + AdapterManager._instance.caching_adapter.invalidate_data( CachingAdapter.CachedDataKey.COVER_ART_FILE, cover_art_id ) + + if not allow_download or ( + AdapterManager._offline_mode + and AdapterManager._instance.ground_truth_adapter.is_networked + ): + return Result(existing_filename) + + # Create a download result. + future = AdapterManager._create_download_result( + AdapterManager._instance.ground_truth_adapter.get_cover_art_uri( + cover_art_id, AdapterManager._get_networked_scheme(), size=size, + ), + cover_art_id, + before_download, + default_value=existing_filename, ) - return future + if AdapterManager._instance.caching_adapter: + future.add_done_callback( + AdapterManager._create_caching_done_callback( + CachingAdapter.CachedDataKey.COVER_ART_FILE, cover_art_id + ) + ) + + return future + + return Result("") - # TODO (#189): allow this to take a set of schemes @staticmethod - def get_song_filename_or_stream( - song: Song, format: str = None, force_stream: bool = False - ) -> str: + def get_song_file_uri(song: Song) -> str: assert AdapterManager._instance cached_song_filename = None - if AdapterManager._can_use_cache(force_stream, "get_song_uri"): - assert AdapterManager._instance.caching_adapter + if AdapterManager._can_use_cache(False, "get_song_file_uri"): + assert (caching_adapter := AdapterManager._instance.caching_adapter) try: - return AdapterManager._instance.caching_adapter.get_song_uri( - song.id, "file" - ) + if "file" not in caching_adapter.supported_schemes: + raise Exception("file not a supported scheme") + + return caching_adapter.get_song_file_uri(song.id, "file") except CacheMissError as e: if e.partial_data is not None: cached_song_filename = cast(str, e.partial_data) - logging.info(f'Cache Miss on {"get_song_filename_or_stream"}.') + logging.info("Cache Miss on get_song_file_uri.") except Exception: - logging.exception( - f'Error on {"get_song_filename_or_stream"} retrieving from cache.' - ) + logging.exception("Error on get_song_file_uri retrieving from cache.") + ground_truth_adapter = AdapterManager._instance.ground_truth_adapter if ( - not AdapterManager._ground_truth_can_do("stream") - or not AdapterManager._ground_truth_can_do("get_song_uri") - or ( - AdapterManager._instance.ground_truth_adapter.is_networked - and AdapterManager._offline_mode - ) + not AdapterManager._ground_truth_can_do("get_song_file_uri") + or (ground_truth_adapter.is_networked and AdapterManager._offline_mode) + or ("file" not in ground_truth_adapter.supported_schemes) ): raise CacheMissError(partial_data=cached_song_filename) - return AdapterManager._instance.ground_truth_adapter.get_song_uri( - song.id, AdapterManager._get_scheme(), stream=True, + return ground_truth_adapter.get_song_file_uri(song.id, "file") + + @staticmethod + def get_song_stream_uri(song: Song) -> str: + assert AdapterManager._instance + # TODO + return AdapterManager._instance.ground_truth_adapter.get_song_stream_uri( + song.id ) @staticmethod @@ -968,7 +972,9 @@ class AdapterManager: # Download the actual song file. try: # If the song file is already cached, just indicate done immediately. - AdapterManager._instance.caching_adapter.get_song_uri(song_id, "file") + AdapterManager._instance.caching_adapter.get_song_file_uri( + song_id, "file" + ) AdapterManager._instance.download_limiter_semaphore.release() AdapterManager._instance.song_download_progress( song_id, DownloadProgress(DownloadProgress.Type.DONE), @@ -984,8 +990,8 @@ class AdapterManager: song_tmp_filename_result: Result[ str ] = AdapterManager._create_download_result( - AdapterManager._instance.ground_truth_adapter.get_song_uri( - song_id, AdapterManager._get_scheme() + AdapterManager._instance.ground_truth_adapter.get_song_file_uri( + song_id, AdapterManager._get_networked_scheme() ), song_id, lambda: before_download(song_id), diff --git a/sublime/adapters/subsonic/adapter.py b/sublime/adapters/subsonic/adapter.py index c8b4158..de7133a 100644 --- a/sublime/adapters/subsonic/adapter.py +++ b/sublime/adapters/subsonic/adapter.py @@ -281,7 +281,8 @@ class SubsonicAdapter(Adapter): can_get_playlist_details = True can_get_playlists = True can_get_song_details = True - can_get_song_uri = True + can_get_song_file_uri = True + can_get_song_stream_uri = True can_scrobble_song = True can_search = True can_stream = True @@ -537,10 +538,14 @@ class SubsonicAdapter(Adapter): params = {"id": cover_art_id, "size": size, **self._get_params()} return self._make_url("getCoverArt") + "?" + urlencode(params) - def get_song_uri(self, song_id: str, scheme: str, stream: bool = False) -> str: + def get_song_file_uri(self, song_id: str, schemes: Iterable[str]) -> str: + assert any(s in schemes for s in self.supported_schemes) params = {"id": song_id, **self._get_params()} - endpoint = "stream" if stream else "download" - return self._make_url(endpoint) + "?" + urlencode(params) + return self._make_url("download") + "?" + urlencode(params) + + def get_song_stream_uri(self, song_id: str) -> str: + params = {"id": song_id, **self._get_params()} + return self._make_url("stream") + "?" + urlencode(params) def get_song_details(self, song_id: str) -> API.Song: song = self._get_json(self._make_url("getSong"), id=song_id).song diff --git a/sublime/app.py b/sublime/app.py index 9412e5d..cb906ca 100644 --- a/sublime/app.py +++ b/sublime/app.py @@ -7,6 +7,7 @@ from datetime import timedelta from functools import partial from pathlib import Path from typing import Any, Callable, Dict, Iterable, List, Optional, Set, Tuple +from urllib.parse import urlparse try: import osxmmkeys @@ -35,6 +36,7 @@ except Exception: from .adapters import ( AdapterManager, AlbumSearchQuery, + CacheMissError, DownloadProgress, Result, SongCacheStatus, @@ -469,8 +471,8 @@ class SublimeMusicApp(Gtk.Application): ) def make_playlist_tuple(p: Playlist) -> GLib.Variant: - cover_art_filename = AdapterManager.get_cover_art_filename( - p.cover_art, allow_download=False, + cover_art_filename = AdapterManager.get_cover_art_uri( + p.cover_art, "file", allow_download=False, ).result() return (f"/playlist/{p.id}", p.name, cover_art_filename or "") @@ -1112,9 +1114,27 @@ class SublimeMusicApp(Gtk.Application): if order_token != self.song_playing_order_token: return - # TODO (#189): make this actually use the player's allowed list of schemes - # to play. - uri = AdapterManager.get_song_filename_or_stream(song) + uri = None + try: + if "file" in self.player_manager.supported_schemes: + uri = AdapterManager.get_song_file_uri(song) + except CacheMissError: + logging.debug("Couldn't find the file, will attempt to stream.") + + if not uri: + try: + uri = AdapterManager.get_song_stream_uri(song) + except Exception: + pass + if ( + not uri + or urlparse(uri).scheme not in self.player_manager.supported_schemes + ): + self.app_config.state.current_notification = UIState.UINotification( + markup=f"Unable to play {song.title}.", + icon="dialog-error", + ) + return # Prevent it from doing the thing where it continually loads # songs when it has to download. @@ -1165,8 +1185,8 @@ class SublimeMusicApp(Gtk.Application): ) song_notification.show() - cover_art_result = AdapterManager.get_cover_art_filename( - song.cover_art + cover_art_result = AdapterManager.get_cover_art_uri( + song.cover_art, "file" ) cover_art_result.add_done_callback( lambda f: on_cover_art_download_complete(f.result()) @@ -1190,7 +1210,7 @@ class SublimeMusicApp(Gtk.Application): os.system(f"osascript -e '{' '.join(osascript_command)}'") except Exception: - logging.exception( + logging.warning( "Unable to display notification. Is a notification daemon running?" # noqa: E501 ) @@ -1215,7 +1235,7 @@ class SublimeMusicApp(Gtk.Application): assert self.player_manager if self.player_manager.can_start_playing_with_no_latency: self.player_manager.play_media( - AdapterManager.get_song_filename_or_stream(song), + AdapterManager.get_song_file_uri(song), self.app_config.state.song_progress, song, ) diff --git a/sublime/config.py b/sublime/config.py index b93442e..58e3489 100644 --- a/sublime/config.py +++ b/sublime/config.py @@ -111,7 +111,7 @@ def decode_providers( else None ), caching_adapter_config=( - ConfigurationStore(**config.get("caching_adapter_config", {})) + ConfigurationStore(**(config.get("caching_adapter_config") or {})) ), ) for id_, config in providers_dict.items() diff --git a/sublime/dbus/manager.py b/sublime/dbus/manager.py index 64b4873..faf211f 100644 --- a/sublime/dbus/manager.py +++ b/sublime/dbus/manager.py @@ -277,8 +277,8 @@ class DBusManager: ).result() try: - cover_art = AdapterManager.get_cover_art_filename( - playlist.cover_art, allow_download=False + cover_art = AdapterManager.get_cover_art_uri( + playlist.cover_art, "file", allow_download=False ).result() except CacheMissError: cover_art = "" @@ -319,8 +319,8 @@ class DBusManager: ) try: - cover_art = AdapterManager.get_cover_art_filename( - song.cover_art, allow_download=False + cover_art = AdapterManager.get_cover_art_uri( + song.cover_art, "file", allow_download=False ).result() except CacheMissError: cover_art = "" diff --git a/sublime/players/chromecast.py b/sublime/players/chromecast.py index 2719866..6fdfe02 100644 --- a/sublime/players/chromecast.py +++ b/sublime/players/chromecast.py @@ -237,8 +237,7 @@ class ChromecastPlayer(Player): song = AdapterManager.get_song_details( self._serving_song_id.value.decode() ).result() - filename = AdapterManager.get_song_filename_or_stream(song) - assert filename.startswith("file://") + filename = AdapterManager.get_song_file_uri(song) with open(filename[7:], "rb") as fin: song_buffer = io.BytesIO(fin.read()) @@ -300,7 +299,15 @@ class ChromecastPlayer(Player): uri = f"http://{host_ip}:{self.config.get(LAN_PORT_KEY)}/s/{token.decode()}" logging.info("Serving {song.name} at {uri}") - cover_art_url = AdapterManager.get_cover_art_uri(song.cover_art, size=1000) + assert AdapterManager._instance + networked_scheme_priority = ("https", "http") + scheme = sorted( + AdapterManager._instance.ground_truth_adapter.supported_schemes, + key=lambda s: networked_scheme_priority.index(s), + )[0] + cover_art_url = AdapterManager.get_cover_art_uri( + song.cover_art, scheme, size=1000 + ) self._current_chromecast.media_controller.play_media( uri, # Just pretend that whatever we send it is mp3, even if it isn't. diff --git a/sublime/players/manager.py b/sublime/players/manager.py index 86f21c7..6c840ec 100644 --- a/sublime/players/manager.py +++ b/sublime/players/manager.py @@ -1,6 +1,6 @@ import logging from datetime import timedelta -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, Union +from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type, Union from sublime.adapters.api_objects import Song @@ -89,6 +89,12 @@ class PlayerManager: if current_player_type := self._get_current_player_type(): return self.players.get(current_player_type) + @property + def supported_schemes(self) -> Set[str]: + if cp := self._get_current_player(): + return cp.supported_schemes + return set() + @property def can_start_playing_with_no_latency(self) -> bool: if self._current_device_id: diff --git a/sublime/ui/albums.py b/sublime/ui/albums.py index 368bfed..450130e 100644 --- a/sublime/ui/albums.py +++ b/sublime/ui/albums.py @@ -839,8 +839,8 @@ class AlbumsGrid(Gtk.Overlay): artwork.set_from_file(filename.result()) artwork.set_loading(False) - cover_art_filename_future = AdapterManager.get_cover_art_filename( - item.album.cover_art + cover_art_filename_future = AdapterManager.get_cover_art_uri( + item.album.cover_art, "file" ) if cover_art_filename_future.data_is_available: on_artwork_downloaded(cover_art_filename_future) diff --git a/sublime/ui/artists.py b/sublime/ui/artists.py index 97bdf85..37430c1 100644 --- a/sublime/ui/artists.py +++ b/sublime/ui/artists.py @@ -1,4 +1,5 @@ from datetime import timedelta +from functools import partial from random import randint from typing import cast, List, Sequence @@ -462,7 +463,7 @@ class ArtistDetailPanel(Gtk.Box): self.albums_list.update(artist, app_config, force=force) @util.async_callback( - AdapterManager.get_cover_art_filename, + partial(AdapterManager.get_cover_art_uri, scheme="file"), before_download=lambda self: self.artist_artwork.set_loading(True), on_failure=lambda self, e: self.artist_artwork.set_loading(False), ) diff --git a/sublime/ui/common/album_with_songs.py b/sublime/ui/common/album_with_songs.py index 727b37b..6c24cca 100644 --- a/sublime/ui/common/album_with_songs.py +++ b/sublime/ui/common/album_with_songs.py @@ -51,8 +51,10 @@ class AlbumWithSongs(Gtk.Box): artist_artwork.set_from_file(f.result()) artist_artwork.set_loading(False) - cover_art_filename_future = AdapterManager.get_cover_art_filename( - album.cover_art, before_download=lambda: artist_artwork.set_loading(True), + cover_art_filename_future = AdapterManager.get_cover_art_uri( + album.cover_art, + "file", + before_download=lambda: artist_artwork.set_loading(True), ) cover_art_filename_future.add_done_callback( lambda f: GLib.idle_add(cover_art_future_done, f) diff --git a/sublime/ui/main.py b/sublime/ui/main.py index 9d303c5..701024e 100644 --- a/sublime/ui/main.py +++ b/sublime/ui/main.py @@ -1065,7 +1065,7 @@ class MainWindow(Gtk.ApplicationWindow): image.set_loading(False) image.set_from_file(f.result()) - artwork_future = AdapterManager.get_cover_art_filename(cover_art_id) + artwork_future = AdapterManager.get_cover_art_uri(cover_art_id, "file") artwork_future.add_done_callback(lambda f: GLib.idle_add(image_callback, f)) return row @@ -1196,7 +1196,7 @@ class DownloadStatusBox(Gtk.Box): image.set_loading(False) image.set_from_file(f.result()) - artwork_future = AdapterManager.get_cover_art_filename(self.song.cover_art) + artwork_future = AdapterManager.get_cover_art_uri(self.song.cover_art, "file") artwork_future.add_done_callback(lambda f: GLib.idle_add(image_callback, f)) def update_progress(self, progress_fraction: float): diff --git a/sublime/ui/player_controls.py b/sublime/ui/player_controls.py index 08b7122..521516b 100644 --- a/sublime/ui/player_controls.py +++ b/sublime/ui/player_controls.py @@ -1,7 +1,7 @@ import copy import math - from datetime import timedelta +from functools import partial from pathlib import Path from typing import Any, Callable, Dict, Optional, Set, Tuple @@ -246,7 +246,7 @@ class PlayerControls(Gtk.ActionBar): def get_cover_art_filename_or_create_future( cover_art_id: Optional[str], idx: int, order_token: int ) -> Optional[str]: - cover_art_result = AdapterManager.get_cover_art_filename(cover_art_id) + cover_art_result = AdapterManager.get_cover_art_uri(cover_art_id, "file") if not cover_art_result.data_is_available: cover_art_result.add_done_callback( make_idle_index_capturing_function( @@ -331,7 +331,7 @@ class PlayerControls(Gtk.ActionBar): self.editing_play_queue_song_list = False @util.async_callback( - AdapterManager.get_cover_art_filename, + partial(AdapterManager.get_cover_art_uri, scheme="file"), before_download=lambda self: self.album_art.set_loading(True), on_failure=lambda self, e: self.album_art.set_loading(False), ) diff --git a/sublime/ui/playlists.py b/sublime/ui/playlists.py index 775a19f..d918b3a 100644 --- a/sublime/ui/playlists.py +++ b/sublime/ui/playlists.py @@ -1,4 +1,4 @@ -from functools import lru_cache +from functools import lru_cache, partial from random import randint from typing import Any, cast, Dict, Iterable, List, Tuple @@ -679,7 +679,7 @@ class PlaylistDetailPanel(Gtk.Overlay): self.playlist_action_buttons.show_all() @util.async_callback( - AdapterManager.get_cover_art_filename, + partial(AdapterManager.get_cover_art_uri, scheme="file"), before_download=lambda self: self.playlist_artwork.set_loading(True), on_failure=lambda self, e: self.playlist_artwork.set_loading(False), ) diff --git a/tests/adapter_tests/filesystem_adapter_tests.py b/tests/adapter_tests/filesystem_adapter_tests.py index 5afe4b8..8dd7007 100644 --- a/tests/adapter_tests/filesystem_adapter_tests.py +++ b/tests/adapter_tests/filesystem_adapter_tests.py @@ -371,13 +371,13 @@ def test_invalidate_song_file(cache_adapter: FilesystemAdapter): cache_adapter.invalidate_data(KEYS.COVER_ART_FILE, "s1") with pytest.raises(CacheMissError): - cache_adapter.get_song_uri("1", "file") + cache_adapter.get_song_file_uri("1", "file") with pytest.raises(CacheMissError): cache_adapter.get_cover_art_uri("s1", "file", size=300) # Make sure it didn't delete the other song. - assert cache_adapter.get_song_uri("2", "file").endswith("song2.mp3") + assert cache_adapter.get_song_file_uri("2", "file").endswith("song2.mp3") def test_malformed_song_path(cache_adapter: FilesystemAdapter): @@ -390,10 +390,10 @@ def test_malformed_song_path(cache_adapter: FilesystemAdapter): KEYS.SONG_FILE, "2", ("fine/path/song2.mp3", MOCK_SONG_FILE2, None) ) - song_uri = cache_adapter.get_song_uri("1", "file") + song_uri = cache_adapter.get_song_file_uri("1", "file") assert song_uri.endswith(f"/music/{MOCK_SONG_FILE_HASH}") - song_uri2 = cache_adapter.get_song_uri("2", "file") + song_uri2 = cache_adapter.get_song_file_uri("2", "file") assert song_uri2.endswith("fine/path/song2.mp3") @@ -467,7 +467,7 @@ def test_delete_song_data(cache_adapter: FilesystemAdapter): KEYS.COVER_ART_FILE, "s1", MOCK_ALBUM_ART, ) - music_file_path = cache_adapter.get_song_uri("1", "file") + music_file_path = cache_adapter.get_song_file_uri("1", "file") cover_art_path = cache_adapter.get_cover_art_uri("s1", "file", size=300) cache_adapter.delete_data(KEYS.SONG_FILE, "1") @@ -477,7 +477,7 @@ def test_delete_song_data(cache_adapter: FilesystemAdapter): assert not Path(cover_art_path).exists() try: - cache_adapter.get_song_uri("1", "file") + cache_adapter.get_song_file_uri("1", "file") assert 0, "DID NOT raise CacheMissError" except CacheMissError as e: assert e.partial_data is None