fix supported features get mangled
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 13 Mar 2023 21:28:25 +0000 (22:28 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Mon, 13 Mar 2023 21:28:25 +0000 (22:28 +0100)
music_assistant/server/models/metadata_provider.py
music_assistant/server/models/provider.py
music_assistant/server/providers/fanarttv/__init__.py
music_assistant/server/providers/filesystem_local/base.py
music_assistant/server/providers/musicbrainz/__init__.py
music_assistant/server/providers/qobuz/__init__.py
music_assistant/server/providers/spotify/__init__.py
music_assistant/server/providers/theaudiodb/__init__.py
music_assistant/server/providers/tunein/__init__.py
music_assistant/server/providers/url/__init__.py
music_assistant/server/providers/ytmusic/__init__.py

index 9dfeb28124b6d1d9afc637174a734748e39f4e02..afa8b2c15d93dbe42c2c7041765a7a651413abaa 100644 (file)
@@ -13,6 +13,13 @@ if TYPE_CHECKING:
 
 # ruff: noqa: ARG001, ARG002
 
+DEFAULT_SUPPORTED_FEATURES = (
+    ProviderFeature.ARTIST_METADATA,
+    ProviderFeature.ALBUM_METADATA,
+    ProviderFeature.TRACK_METADATA,
+    ProviderFeature.GET_ARTIST_MBID,
+)
+
 
 class MetadataProvider(Provider):
     """Base representation of a Metadata Provider (controller).
@@ -20,12 +27,10 @@ class MetadataProvider(Provider):
     Metadata Provider implementations should inherit from this base model.
     """
 
-    _attr_supported_features: tuple[ProviderFeature, ...] = (
-        ProviderFeature.ARTIST_METADATA,
-        ProviderFeature.ALBUM_METADATA,
-        ProviderFeature.TRACK_METADATA,
-        ProviderFeature.GET_ARTIST_MBID,
-    )
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return DEFAULT_SUPPORTED_FEATURES
 
     async def get_artist_metadata(self, artist: Artist) -> MediaItemMetadata | None:
         """Retrieve metadata for an artist on this Metadata provider."""
index 5fe35b9de32571e35eeb9db48271f3ddfb26069c..c37ac91de7114d6e5d30429e5cf49b2f07cba23d 100644 (file)
@@ -18,8 +18,6 @@ if TYPE_CHECKING:
 class Provider:
     """Base representation of a Provider implementation within Music Assistant."""
 
-    _attr_supported_features: tuple[ProviderFeature, ...] = tuple()
-
     def __init__(
         self, mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
     ) -> None:
@@ -34,8 +32,8 @@ class Provider:
 
     @property
     def supported_features(self) -> tuple[ProviderFeature, ...]:
-        """Return the features supported by this MusicProvider."""
-        return self._attr_supported_features
+        """Return the features supported by this Provider."""
+        return tuple()
 
     async def setup(self) -> None:
         """Handle async initialization of the provider.
index cb1fb04335a6e24785d73db8e2e2e759d45804d9..cf499a2244e2baae0cd04726dd748e99d9ce4ee3 100644 (file)
@@ -16,6 +16,11 @@ from music_assistant.server.models.metadata_provider import MetadataProvider
 if TYPE_CHECKING:
     from music_assistant.common.models.media_items import Album, Artist
 
+SUPPORTED_FEATURES = (
+    ProviderFeature.ARTIST_METADATA,
+    ProviderFeature.ALBUM_METADATA,
+)
+
 # TODO: add support for personal api keys ?
 
 
@@ -36,10 +41,11 @@ class FanartTvMetadataProvider(MetadataProvider):
         """Handle async initialization of the provider."""
         self.cache = self.mass.cache
         self.throttler = Throttler(rate_limit=2, period=1)
-        self._attr_supported_features = (
-            ProviderFeature.ARTIST_METADATA,
-            ProviderFeature.ALBUM_METADATA,
-        )
+
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
 
     async def get_artist_metadata(self, artist: Artist) -> MediaItemMetadata | None:
         """Retrieve metadata for artist on fanart.tv."""
index 43b4456b8185a3a46762cc84f795f996fd9998fe..6c8f85e6f538b4535fffde4bc38777fb6a888e42 100644 (file)
@@ -43,6 +43,17 @@ PLAYLIST_EXTENSIONS = ("m3u", "pls")
 SUPPORTED_EXTENSIONS = TRACK_EXTENSIONS + PLAYLIST_EXTENSIONS
 IMAGE_EXTENSIONS = ("jpg", "jpeg", "JPG", "JPEG", "png", "PNG", "gif", "GIF")
 
+SUPPORTED_FEATURES = (
+    ProviderFeature.LIBRARY_ARTISTS,
+    ProviderFeature.LIBRARY_ALBUMS,
+    ProviderFeature.LIBRARY_TRACKS,
+    ProviderFeature.LIBRARY_PLAYLISTS,
+    ProviderFeature.PLAYLIST_TRACKS_EDIT,
+    ProviderFeature.PLAYLIST_CREATE,
+    ProviderFeature.BROWSE,
+    ProviderFeature.SEARCH,
+)
+
 
 @dataclass
 class FileSystemItem:
@@ -85,16 +96,10 @@ class FileSystemProviderBase(MusicProvider):
     Supports having URI's from streaming providers within m3u playlist.
     """
 
-    _attr_supported_features = (
-        ProviderFeature.LIBRARY_ARTISTS,
-        ProviderFeature.LIBRARY_ALBUMS,
-        ProviderFeature.LIBRARY_TRACKS,
-        ProviderFeature.LIBRARY_PLAYLISTS,
-        ProviderFeature.PLAYLIST_TRACKS_EDIT,
-        ProviderFeature.PLAYLIST_CREATE,
-        ProviderFeature.BROWSE,
-        ProviderFeature.SEARCH,
-    )
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
 
     @abstractmethod
     async def setup(self) -> None:
index dced64cb34f7aaf85070856fc23167f71ea693ab..59231976636f3deca5e8baf52cfb8a4df42bc1d1 100644 (file)
@@ -24,6 +24,8 @@ if TYPE_CHECKING:
 
 LUCENE_SPECIAL = r'([+\-&|!(){}\[\]\^"~*?:\\\/])'
 
+SUPPORTED_FEATURES = (ProviderFeature.GET_ARTIST_MBID,)
+
 
 class MusicbrainzProvider(MetadataProvider):
     """The Musicbrainz Metadata provider."""
@@ -34,7 +36,11 @@ class MusicbrainzProvider(MetadataProvider):
         """Handle async initialization of the provider."""
         self.cache = self.mass.cache
         self.throttler = Throttler(rate_limit=1, period=1)
-        self._attr_supported_features = (ProviderFeature.GET_ARTIST_MBID,)
+
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
 
     async def get_musicbrainz_artist_id(
         self, artist: Artist, ref_albums: Iterable[Album], ref_tracks: Iterable[Track]
index 17fbb0f56dff71ce057e1979edbc3ce7d347d808..aae4987a5729d76f733b86f08b9cccca77035d04 100644 (file)
@@ -31,6 +31,22 @@ from music_assistant.constants import CONF_PASSWORD, CONF_USERNAME
 from music_assistant.server.helpers.app_vars import app_var  # pylint: disable=no-name-in-module
 from music_assistant.server.models.music_provider import MusicProvider
 
+SUPPORTED_FEATURES = (
+    ProviderFeature.LIBRARY_ARTISTS,
+    ProviderFeature.LIBRARY_ALBUMS,
+    ProviderFeature.LIBRARY_TRACKS,
+    ProviderFeature.LIBRARY_PLAYLISTS,
+    ProviderFeature.LIBRARY_ARTISTS_EDIT,
+    ProviderFeature.LIBRARY_ALBUMS_EDIT,
+    ProviderFeature.LIBRARY_PLAYLISTS_EDIT,
+    ProviderFeature.LIBRARY_TRACKS_EDIT,
+    ProviderFeature.PLAYLIST_TRACKS_EDIT,
+    ProviderFeature.BROWSE,
+    ProviderFeature.SEARCH,
+    ProviderFeature.ARTIST_ALBUMS,
+    ProviderFeature.ARTIST_TOPTRACKS,
+)
+
 
 class QobuzProvider(MusicProvider):
     """Provider for the Qobux music service."""
@@ -41,21 +57,7 @@ class QobuzProvider(MusicProvider):
     async def setup(self) -> None:
         """Handle async initialization of the provider."""
         self._throttler = Throttler(rate_limit=4, period=1)
-        self._attr_supported_features = (
-            ProviderFeature.LIBRARY_ARTISTS,
-            ProviderFeature.LIBRARY_ALBUMS,
-            ProviderFeature.LIBRARY_TRACKS,
-            ProviderFeature.LIBRARY_PLAYLISTS,
-            ProviderFeature.LIBRARY_ARTISTS_EDIT,
-            ProviderFeature.LIBRARY_ALBUMS_EDIT,
-            ProviderFeature.LIBRARY_PLAYLISTS_EDIT,
-            ProviderFeature.LIBRARY_TRACKS_EDIT,
-            ProviderFeature.PLAYLIST_TRACKS_EDIT,
-            ProviderFeature.BROWSE,
-            ProviderFeature.SEARCH,
-            ProviderFeature.ARTIST_ALBUMS,
-            ProviderFeature.ARTIST_TOPTRACKS,
-        )
+
         if not self.config.get_value(CONF_USERNAME) or not self.config.get_value(CONF_PASSWORD):
             raise LoginFailed("Invalid login credentials")
         # try to get a token, raise if that fails
@@ -63,6 +65,11 @@ class QobuzProvider(MusicProvider):
         if not token:
             raise LoginFailed(f"Login failed for user {self.config.get_value(CONF_USERNAME)}")
 
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
+
     async def search(
         self, search_query: str, media_types=list[MediaType] | None, limit: int = 5
     ) -> list[MediaItemType]:
index c4752be34851647d37b082cdab0ef1c326e30e07..9a918682c92d057ab0148eb8a1d0fd58b9e8a1f8 100644 (file)
@@ -36,6 +36,22 @@ from music_assistant.server.helpers.process import AsyncProcess
 from music_assistant.server.models.music_provider import MusicProvider
 
 CACHE_DIR = gettempdir()
+SUPPORTED_FEATURES = (
+    ProviderFeature.LIBRARY_ARTISTS,
+    ProviderFeature.LIBRARY_ALBUMS,
+    ProviderFeature.LIBRARY_TRACKS,
+    ProviderFeature.LIBRARY_PLAYLISTS,
+    ProviderFeature.LIBRARY_ARTISTS_EDIT,
+    ProviderFeature.LIBRARY_ALBUMS_EDIT,
+    ProviderFeature.LIBRARY_PLAYLISTS_EDIT,
+    ProviderFeature.LIBRARY_TRACKS_EDIT,
+    ProviderFeature.PLAYLIST_TRACKS_EDIT,
+    ProviderFeature.BROWSE,
+    ProviderFeature.SEARCH,
+    ProviderFeature.ARTIST_ALBUMS,
+    ProviderFeature.ARTIST_TOPTRACKS,
+    ProviderFeature.SIMILAR_TRACKS,
+)
 
 
 class SpotifyProvider(MusicProvider):
@@ -50,27 +66,17 @@ class SpotifyProvider(MusicProvider):
         self._throttler = Throttler(rate_limit=1, period=0.1)
         self._cache_dir = CACHE_DIR
         self._ap_workaround = False
-        self._attr_supported_features = (
-            ProviderFeature.LIBRARY_ARTISTS,
-            ProviderFeature.LIBRARY_ALBUMS,
-            ProviderFeature.LIBRARY_TRACKS,
-            ProviderFeature.LIBRARY_PLAYLISTS,
-            ProviderFeature.LIBRARY_ARTISTS_EDIT,
-            ProviderFeature.LIBRARY_ALBUMS_EDIT,
-            ProviderFeature.LIBRARY_PLAYLISTS_EDIT,
-            ProviderFeature.LIBRARY_TRACKS_EDIT,
-            ProviderFeature.PLAYLIST_TRACKS_EDIT,
-            ProviderFeature.BROWSE,
-            ProviderFeature.SEARCH,
-            ProviderFeature.ARTIST_ALBUMS,
-            ProviderFeature.ARTIST_TOPTRACKS,
-            ProviderFeature.SIMILAR_TRACKS,
-        )
+
         # try to get a token, raise if that fails
         self._cache_dir = os.path.join(CACHE_DIR, self.instance_id)
         # try login which will raise if it fails
         await self.login()
 
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
+
     async def search(
         self, search_query: str, media_types=list[MediaType] | None, limit: int = 5
     ) -> list[MediaItemType]:
index 00a5470c8d220b0e108fd396fbc1c9df762424cd..c0d9acf83ad42f8e5723e3dcc6799722395be959 100644 (file)
@@ -27,6 +27,13 @@ from music_assistant.server.models.metadata_provider import MetadataProvider
 if TYPE_CHECKING:
     from collections.abc import Iterable
 
+SUPPORTED_FEATURES = (
+    ProviderFeature.ARTIST_METADATA,
+    ProviderFeature.ALBUM_METADATA,
+    ProviderFeature.TRACK_METADATA,
+    ProviderFeature.GET_ARTIST_MBID,
+)
+
 IMG_MAPPING = {
     "strArtistThumb": ImageType.THUMB,
     "strArtistLogo": ImageType.LOGO,
@@ -68,14 +75,13 @@ class AudioDbMetadataProvider(MetadataProvider):
     async def setup(self) -> None:
         """Handle async initialization of the provider."""
         self.cache = self.mass.cache
-        self._attr_supported_features = (
-            ProviderFeature.ARTIST_METADATA,
-            ProviderFeature.ALBUM_METADATA,
-            ProviderFeature.TRACK_METADATA,
-            ProviderFeature.GET_ARTIST_MBID,
-        )
         self.throttler = Throttler(rate_limit=2, period=1)
 
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
+
     async def get_artist_metadata(self, artist: Artist) -> MediaItemMetadata | None:
         """Retrieve metadata for artist on theaudiodb."""
         if data := await self._get_data("artist-mb.php", i=artist.musicbrainz_id):  # noqa: SIM102
index e6dc561a8407fb42008e5800940e3e280e586ff8..1a380c73d0543b82efb5033329f9ff478b781990 100644 (file)
@@ -24,19 +24,26 @@ from music_assistant.server.helpers.playlists import fetch_playlist
 from music_assistant.server.helpers.tags import parse_tags
 from music_assistant.server.models.music_provider import MusicProvider
 
+SUPPORTED_FEATURES = (
+    ProviderFeature.LIBRARY_RADIOS,
+    ProviderFeature.BROWSE,
+)
+
 
 class TuneInProvider(MusicProvider):
     """Provider implementation for Tune In."""
 
     _throttler: Throttler
 
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
+
     async def setup(self) -> None:
         """Handle async initialization of the provider."""
         self._throttler = Throttler(rate_limit=1, period=1)
-        self._attr_supported_features = (
-            ProviderFeature.LIBRARY_RADIOS,
-            ProviderFeature.BROWSE,
-        )
+
         if not self.config.get_value(CONF_USERNAME):
             raise LoginFailed("Username is invalid")
         if "@" in self.config.get_value(CONF_USERNAME):
index 7c9f18cc17c0334a58c56dc9b3a5bfe5ca636eb9..aeb248affa76aeb30f6506c33ac6b8f57f38b9a9 100644 (file)
@@ -28,7 +28,6 @@ class URLProvider(MusicProvider):
 
         Called when provider is registered.
         """
-        self._attr_available = True
         self._full_url = {}
 
     async def get_track(self, prov_track_id: str) -> Track:
index f80b36fa33472aadb15e733e6961cc3f2f82001e..4765cde58ad7ef9f258a981476a8650cb4ba97da 100644 (file)
@@ -45,15 +45,24 @@ from .helpers import (
     search,
 )
 
-# if TYPE_CHECKING:
-#     from collections.abc import AsyncGenerator
-
 CONF_COOKIE = "cookie"
 
 YT_DOMAIN = "https://www.youtube.com"
 YTM_DOMAIN = "https://music.youtube.com"
 YTM_BASE_URL = f"{YTM_DOMAIN}/youtubei/v1/"
 
+SUPPORTED_FEATURES = (
+    ProviderFeature.LIBRARY_ARTISTS,
+    ProviderFeature.LIBRARY_ALBUMS,
+    ProviderFeature.LIBRARY_TRACKS,
+    ProviderFeature.LIBRARY_PLAYLISTS,
+    ProviderFeature.BROWSE,
+    ProviderFeature.SEARCH,
+    ProviderFeature.ARTIST_ALBUMS,
+    ProviderFeature.ARTIST_TOPTRACKS,
+    ProviderFeature.SIMILAR_TRACKS,
+)
+
 # TODO: fix disabled tests
 # ruff: noqa: PLW2901, RET504
 
@@ -69,17 +78,6 @@ class YoutubeMusicProvider(MusicProvider):
 
     async def setup(self) -> None:
         """Set up the YTMusic provider."""
-        self._attr_supported_features = (
-            ProviderFeature.LIBRARY_ARTISTS,
-            ProviderFeature.LIBRARY_ALBUMS,
-            ProviderFeature.LIBRARY_TRACKS,
-            ProviderFeature.LIBRARY_PLAYLISTS,
-            ProviderFeature.BROWSE,
-            ProviderFeature.SEARCH,
-            ProviderFeature.ARTIST_ALBUMS,
-            ProviderFeature.ARTIST_TOPTRACKS,
-            ProviderFeature.SIMILAR_TRACKS,
-        )
         if not self.config.get_value(CONF_USERNAME) or not self.config.get_value(CONF_COOKIE):
             raise LoginFailed("Invalid login credentials")
         await self._initialize_headers(cookie=self.config.get_value(CONF_COOKIE))
@@ -87,6 +85,11 @@ class YoutubeMusicProvider(MusicProvider):
         self._cookies = {"CONSENT": "YES+1"}
         self._signature_timestamp = await self._get_signature_timestamp()
 
+    @property
+    def supported_features(self) -> tuple[ProviderFeature, ...]:
+        """Return the features supported by this Provider."""
+        return SUPPORTED_FEATURES
+
     async def search(
         self, search_query: str, media_types=list[MediaType] | None, limit: int = 5
     ) -> list[MediaItemType]: