diff --git a/setup.py b/setup.py index b733b4e..79af221 100644 --- a/setup.py +++ b/setup.py @@ -63,6 +63,7 @@ setup( "python-Levenshtein", "python-mpv", "requests", + "semver", ], extras_require={ "keyring": ["keyring"], diff --git a/sublime/adapters/filesystem/adapter.py b/sublime/adapters/filesystem/adapter.py index 6a0ff4c..7d2fb3f 100644 --- a/sublime/adapters/filesystem/adapter.py +++ b/sublime/adapters/filesystem/adapter.py @@ -572,7 +572,10 @@ class FilesystemAdapter(CachingAdapter): return_val = db_album elif data_key == KEYS.ALBUMS: - albums = [self._do_ingest_new_data(KEYS.ALBUM, a.id, a) for a in data] + albums = [ + self._do_ingest_new_data(KEYS.ALBUM, a.id, a, partial=True) + for a in data + ] album_query_result, created = models.AlbumQueryResult.get_or_create( query_hash=param, defaults={"query_hash": param, "albums": albums} ) @@ -617,7 +620,7 @@ class FilesystemAdapter(CachingAdapter): ], ), "albums": [ - self._do_ingest_new_data(KEYS.ALBUM, a.id, a) + self._do_ingest_new_data(KEYS.ALBUM, a.id, a, partial=True) for a in artist.albums or [] ], "_artist_image_url": ( diff --git a/sublime/adapters/subsonic/adapter.py b/sublime/adapters/subsonic/adapter.py index b80215f..cee0f15 100644 --- a/sublime/adapters/subsonic/adapter.py +++ b/sublime/adapters/subsonic/adapter.py @@ -24,6 +24,7 @@ from typing import ( from urllib.parse import urlencode, urlparse import requests +import semver from gi.repository import Gtk from .api_objects import Directory, Response @@ -209,6 +210,7 @@ class SubsonicAdapter(Adapter): # TODO (#112): support XML? _ping_process: Optional[multiprocessing.Process] = None + _version = multiprocessing.Array("c", 20) _offline_mode = False def initial_sync(self): @@ -265,24 +267,66 @@ class SubsonicAdapter(Adapter): # TODO (#199) make these way smarter can_get_playlists = True can_get_playlist_details = True - can_create_playlist = True - can_update_playlist = True - can_delete_playlist = True can_get_cover_art_uri = True can_get_song_uri = True can_stream = True - can_get_song_details = True - can_scrobble_song = True - can_get_artists = True - can_get_artist = True can_get_ignored_articles = True - can_get_albums = True can_get_album = True can_get_directory = True - can_get_genres = True - can_get_play_queue = True - can_save_play_queue = True - can_search = True + + # TODO Consider just requiring the server to implement at least 1.8.0 + def version_at_least(self, version: str) -> bool: + if not self._version.value: + return False + return semver.VersionInfo.parse(self._version.value.decode()) >= version + + @property + def can_create_playlist(self) -> bool: + return self.version_at_least("1.2.0") + + @property + def can_delete_playlist(self) -> bool: + return self.version_at_least("1.2.0") + + @property + def can_get_albums(self) -> bool: + return self.version_at_least("1.8.0") + + @property + def can_get_artists(self) -> bool: + return self.version_at_least("1.8.0") + + @property + def can_get_artist(self) -> bool: + return self.version_at_least("1.8.0") + + @property + def can_get_genres(self) -> bool: + return self.version_at_least("1.9.0") + + @property + def can_get_play_queue(self) -> bool: + return self.version_at_least("1.12.0") + + @property + def can_save_play_queue(self) -> bool: + return self.version_at_least("1.12.0") + + @property + def can_get_song_details(self) -> bool: + return self.version_at_least("1.8.0") + + @property + def can_scrobble_song(self) -> bool: + return self.version_at_least("1.5.0") + + @property + def can_search(self) -> bool: + return self.version_at_least("1.8.0") + + @property + def can_update_playlist(self) -> bool: + return self.version_at_least("1.8.0") _schemes = None @@ -412,12 +456,14 @@ class SubsonicAdapter(Adapter): if not subsonic_response: raise ServerError(500, f"{url} returned invalid JSON.") - if subsonic_response["status"] == "failed": + if subsonic_response["status"] != "ok": raise ServerError( subsonic_response["error"].get("code"), subsonic_response["error"].get("message"), ) + self._version.value = subsonic_response["version"].encode() + logging.debug(f"Response from {url}: {subsonic_response}") return Response.from_dict(subsonic_response) @@ -538,11 +584,14 @@ class SubsonicAdapter(Adapter): def get_artist(self, artist_id: str) -> API.Artist: artist = self._get_json(self._make_url("getArtist"), id=artist_id).artist assert artist, f"Error getting artist {artist_id}" - try: - artist_info = self._get_json(self._make_url("getArtistInfo2"), id=artist_id) - artist.augment_with_artist_info(artist_info.artist_info) - except Exception: - pass + if self.version_at_least("1.11.0"): + try: + artist_info = self._get_json( + self._make_url("getArtistInfo2"), id=artist_id + ) + artist.augment_with_artist_info(artist_info.artist_info) + except Exception: + pass return artist def get_ignored_articles(self) -> Set[str]: