Two updates to the Subsonic provider (#1335)
authorEric Munson <eric@munsonfam.org>
Sun, 9 Jun 2024 23:09:06 +0000 (19:09 -0400)
committerGitHub <noreply@github.com>
Sun, 9 Jun 2024 23:09:06 +0000 (01:09 +0200)
music_assistant/common/models/errors.py
music_assistant/server/providers/opensubsonic/sonic_provider.py

index 93cf584b01ebda39e89e5fd1c0e44cc5966bda97..fb0fc9ae24dec50698264d15c96bebbe1f8efc21 100644 (file)
@@ -121,3 +121,9 @@ class ResourceTemporarilyUnavailable(MusicAssistantError):
         self.backoff_time = backoff_time
 
     error_code = 17
+
+
+class ProviderPermissionDenied(MusicAssistantError):
+    """Error thrown when a provider action is denied because of permissions."""
+
+    error_code = 18
index a03a785de6632a2e4109c9b9e9099de88d802855..d77394e0648b64f4efb3dd37ba3dc255ee77fed0 100644 (file)
@@ -22,7 +22,11 @@ from music_assistant.common.models.enums import (
     ProviderFeature,
     StreamType,
 )
-from music_assistant.common.models.errors import LoginFailed, MediaNotFoundError
+from music_assistant.common.models.errors import (
+    LoginFailed,
+    MediaNotFoundError,
+    ProviderPermissionDenied,
+)
 from music_assistant.common.models.media_items import (
     Album,
     AlbumType,
@@ -123,11 +127,14 @@ class OpenSonicProvider(MusicProvider):
             ProviderFeature.LIBRARY_ALBUMS,
             ProviderFeature.LIBRARY_TRACKS,
             ProviderFeature.LIBRARY_PLAYLISTS,
+            ProviderFeature.LIBRARY_PLAYLISTS_EDIT,
             ProviderFeature.BROWSE,
             ProviderFeature.SEARCH,
             ProviderFeature.ARTIST_ALBUMS,
             ProviderFeature.ARTIST_TOPTRACKS,
             ProviderFeature.SIMILAR_TRACKS,
+            ProviderFeature.PLAYLIST_TRACKS_EDIT,
+            ProviderFeature.PLAYLIST_CREATE,
         )
 
     @property
@@ -237,6 +244,7 @@ class OpenSonicProvider(MusicProvider):
             item_id=sonic_artist.id,
             name=sonic_artist.name,
             provider=self.domain,
+            favorite=bool(sonic_artist.starred),
             provider_mappings={
                 ProviderMapping(
                     item_id=sonic_artist.id,
@@ -278,6 +286,7 @@ class OpenSonicProvider(MusicProvider):
             item_id=album_id,
             provider=self.domain,
             name=sonic_album.name,
+            favorite=bool(sonic_album.starred),
             provider_mappings={
                 ProviderMapping(
                     item_id=album_id,
@@ -675,6 +684,38 @@ class OpenSonicProvider(MusicProvider):
         )
         return [self._parse_track(entry) for entry in songs]
 
+    async def create_playlist(self, name: str) -> Playlist:
+        """Create a new empty playlist on the server."""
+        playlist: SonicPlaylist = await self._run_async(self._conn.createPlaylist, name=name)
+        return self._parse_playlist(playlist)
+
+    async def add_playlist_tracks(self, prov_playlist_id: str, prov_track_ids: list[str]) -> None:
+        """Append the listed tracks to the selected playlist.
+
+        Note that the configured user must own the playlist to edit this way.
+        """
+        try:
+            await self._run_async(
+                self._conn.updatePlaylist, lid=prov_playlist_id, songIdsToAdd=prov_track_ids
+            )
+        except SonicError:
+            msg = f"Failed to add songs to {prov_playlist_id}, check your permissions."
+            raise ProviderPermissionDenied(msg)
+
+    async def remove_playlist_tracks(
+        self, prov_playlist_id: str, positions_to_remove: tuple[int, ...]
+    ) -> None:
+        """Remove selected positions from the playlist."""
+        try:
+            await self._run_async(
+                self._conn.updatePlaylist,
+                lid=prov_playlist_id,
+                songIndexesToRemove=list(positions_to_remove),
+            )
+        except SonicError:
+            msg = f"Failed to remove songs from {prov_playlist_id}, check your permissions."
+            raise ProviderPermissionDenied(msg)
+
     async def get_stream_details(self, item_id: str) -> StreamDetails:
         """Get the details needed to process a specified track."""
         try: