Simplify library sync/import settings (#2507)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Tue, 14 Oct 2025 06:49:34 +0000 (08:49 +0200)
committerGitHub <noreply@github.com>
Tue, 14 Oct 2025 06:49:34 +0000 (08:49 +0200)
* Simplify import options

* bump models to 1.1.62

* Also simplify the sync back option

* fix filesystem

* Update music_assistant/models/music_provider.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
15 files changed:
music_assistant/constants.py
music_assistant/controllers/config.py
music_assistant/controllers/media/albums.py
music_assistant/controllers/music.py
music_assistant/models/music_provider.py
music_assistant/providers/_demo_music_provider/__init__.py
music_assistant/providers/audiobookshelf/__init__.py
music_assistant/providers/builtin/__init__.py
music_assistant/providers/builtin/constants.py
music_assistant/providers/filesystem_local/__init__.py
music_assistant/providers/filesystem_local/constants.py
music_assistant/providers/filesystem_smb/__init__.py
music_assistant/providers/radiobrowser/__init__.py
pyproject.toml
requirements_all.txt

index c2a3ae9b649e4109f53f3499df287df730099451..bf7d815e257714d34e0521915a7aff07d98fcdcb 100644 (file)
@@ -656,157 +656,118 @@ CONF_ENTRY_MANUAL_DISCOVERY_IPS = ConfigEntry(
     multi_value=True,
 )
 
-CONF_LIBRARY_IMPORT_OPTIONS = [
-    ConfigValueOption("Import into the library only", "import_only"),
-    ConfigValueOption("Import into the library, and mark as favorite", "import_as_favorite"),
-    ConfigValueOption("Do not import into the library", "no_import"),
-]
-CONF_ENTRY_LIBRARY_IMPORT_ARTISTS = ConfigEntry(
-    key="library_import_artists",
-    type=ConfigEntryType.STRING,
-    label="Import Artists from this provider into Music Assistant",
-    description="Whether to import (favourite/library) artists from this "
-    "provider into the Music Assistant Library.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_as_favorite",
+CONF_ENTRY_LIBRARY_SYNC_ARTISTS = ConfigEntry(
+    key="library_sync_artists",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync Library Artists from this provider to Music Assistant",
+    description="Whether to synchronize (favourited/in-library) Artists from this "
+    "provider to the Music Assistant Library.",
+    default_value=True,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_ALBUMS = ConfigEntry(
-    key="library_import_albums",
-    type=ConfigEntryType.STRING,
-    label="Import Albums from this provider into Music Assistant",
-    description="Whether to import (favourite/library) albums from this "
-    "provider into the Music Assistant Library. \n\n"
+CONF_ENTRY_LIBRARY_SYNC_ALBUMS = ConfigEntry(
+    key="library_sync_albums",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync Library Albums from this provider to Music Assistant",
+    description="Whether to import (favourited/in-library) Albums from this "
+    "provider to the Music Assistant Library. \n\n"
     "Please note that by adding an Album into the Music Assistant library, "
-    "the album artists will always be imported as well (not as favorites though).",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_as_favorite",
+    "the Album Artists will always be imported as well.",
+    default_value=True,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_TRACKS = ConfigEntry(
-    key="library_import_tracks",
-    type=ConfigEntryType.STRING,
-    label="Import Tracks from this provider into Music Assistant",
-    description="Whether to import (favourite/library) tracks from this "
-    "provider into the Music Assistant Library. \n\n"
+CONF_ENTRY_LIBRARY_SYNC_TRACKS = ConfigEntry(
+    key="library_sync_tracks",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync Library Tracks from this provider to Music Assistant",
+    description="Whether to import (favourited/in-library) Tracks from this "
+    "provider to the Music Assistant Library. \n\n"
     "Please note that by adding a Track into the Music Assistant library, "
-    "the track artists and album will always be imported as well (not as favorites though).",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_as_favorite",
+    "the Track's Artists and Album will always be imported as well.",
+    default_value=True,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS = ConfigEntry(
-    key="library_import_playlists",
-    type=ConfigEntryType.STRING,
-    label="Import Playlists from this provider into Music Assistant",
-    description="Whether to import (favourite/library) playlists from this "
-    "provider into the Music Assistant Library.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_as_favorite",
+CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS = ConfigEntry(
+    key="library_sync_playlists",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync Library Playlists from this provider to Music Assistant",
+    description="Whether to import (favourited/in-library) Playlists from this "
+    "provider to the Music Assistant Library.",
+    default_value=True,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_PODCASTS = ConfigEntry(
-    key="library_import_podcasts",
-    type=ConfigEntryType.STRING,
-    label="Import Podcasts from this provider into Music Assistant",
-    description="Whether to import (favourite/library) podcasts from this "
-    "provider into the Music Assistant Library.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_as_favorite",
+CONF_ENTRY_LIBRARY_SYNC_PODCASTS = ConfigEntry(
+    key="library_sync_podcasts",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync Library Podcasts from this provider to Music Assistant",
+    description="Whether to import (favourited/in-library) Podcasts from this "
+    "provider to the Music Assistant Library.",
+    default_value=True,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS = ConfigEntry(
-    key="library_import_audiobooks",
-    type=ConfigEntryType.STRING,
-    label="Import Audiobooks from this provider into Music Assistant",
-    description="Whether to import (favourite/library) audiobooks from this "
-    "provider into the Music Assistant Library.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_as_favorite",
+CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS = ConfigEntry(
+    key="library_sync_audiobooks",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync Library Audiobooks from this provider to Music Assistant",
+    description="Whether to import (favourited/in-library) Audiobooks from this "
+    "provider to the Music Assistant Library.",
+    default_value=True,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_RADIOS = ConfigEntry(
-    key="library_import_radios",
-    type=ConfigEntryType.STRING,
-    label="Import Radios from this provider into Music Assistant",
-    description="Whether to import (favourite/library) radios from this "
-    "provider into the Music Assistant Library.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_as_favorite",
+CONF_ENTRY_LIBRARY_SYNC_RADIOS = ConfigEntry(
+    key="library_sync_radios",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync Library Radios from this provider to Music Assistant",
+    description="Whether to import (favourited/in-library) Radio stations from this "
+    "provider to the Music Assistant Library.",
+    default_value=True,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_ALBUM_TRACKS = ConfigEntry(
-    key="library_import_album_tracks",
+CONF_ENTRY_LIBRARY_SYNC_ALBUM_TRACKS = ConfigEntry(
+    key="library_sync_album_tracks",
     type=ConfigEntryType.BOOLEAN,
     label="Import album tracks",
-    description="By default, when importing albums into the library, "
+    description="By default, when importing Albums into the library, "
     "only the Album itself will be imported into the Music Assistant Library, "
     "allowing you to manually browse and select which tracks you want to import. \n\n"
     "If you want to override this default behavior, "
     "you can use this configuration option.\n\n"
-    "Please note that some streaming providers may already define this behavior unsolicited, "
+    "Please note that some (streaming) providers may already define this behavior unsolicited, "
     "by automatically adding all tracks from the album to their library/favorites.",
     default_value=False,
     category="sync_options",
 )
-CONF_ENTRY_LIBRARY_IMPORT_PLAYLIST_TRACKS = ConfigEntry(
-    key="library_import_playlist_tracks",
+CONF_ENTRY_LIBRARY_SYNC_PLAYLIST_TRACKS = ConfigEntry(
+    key="library_sync_playlist_tracks",
     type=ConfigEntryType.STRING,
     label="Import playlist tracks",
-    description="By default, when importing playlists into the library, "
+    description="By default, when importing Playlists into the library, "
     "only the Playlist itself will be imported into the Music Assistant Library, "
-    "allowing you to browse and play the playlist and optionally add any individual "
-    "tracks of the playlist to the Music Assistant Library manually. \n\n"
+    "allowing you to browse and play the Playlist and optionally add any individual "
+    "tracks of the Playlist to the Music Assistant Library manually. \n\n"
     "Use this configuration option to override this default behavior, "
-    "by specifying the playlists for which you'd like to import all tracks.\n"
+    "by specifying the Playlists for which you'd like to import all tracks.\n"
     "You can either enter the Playlist name (case sensitive) or the Playlist URI.",
     default_value=[],
     category="sync_options",
     multi_value=True,
 )
 
-CONF_ENTRY_LIBRARY_EXPORT_ADD = ConfigEntry(
-    key="library_export_add",
-    type=ConfigEntryType.STRING,
-    label="Sync back library additions",
-    description="Specify the behavior if an item is (manually) added to the "
-    "Music Assistant Library (or favorites). \n"
-    "Should we synchronise that action back to the provider?\n\n"
-    "You can choose to add items to the provider's library as soon as you "
-    "add it to the Music Assistant Library or only do that when you mark the item as "
-    "favorite. \nIf you do not want to sync back to the provider at all, you can choose "
-    "the 'Don't sync back to the provider' option.",
-    default_value="export_favorite",
-    category="sync_options",
-    options=[
-        ConfigValueOption("When an item is added to the library", "export_library"),
-        ConfigValueOption("When an item is marked as favorite", "export_favorite"),
-        ConfigValueOption("Don't sync back to the provider", "no_export"),
-    ],
-)
-CONF_ENTRY_LIBRARY_EXPORT_REMOVE = ConfigEntry(
-    key="library_export_remove",
-    type=ConfigEntryType.STRING,
-    label="Sync back library removals",
-    description="Specify the behavior if an item is (manually) removed from the "
-    "Music Assistant Library (or favorites). \n"
+CONF_ENTRY_LIBRARY_SYNC_BACK = ConfigEntry(
+    key="library_sync_back",
+    type=ConfigEntryType.BOOLEAN,
+    label="Sync back library additions/removals (2-way sync)",
+    description="Specify the behavior if an item is manually added to "
+    "(or removed from) the Music Assistant Library. \n"
     "Should we synchronise that action back to the provider?\n\n"
-    "You can choose to remove items from the provider's library as soon as you (manually) "
-    "remove it from the Music Assistant Library or only do that when you unmark the item as "
-    "favorite. \nIf you do not want to sync back to the provider at all, you can choose "
-    "the 'Don't sync back to the provider' option.\n\n"
-    "Please note that if you you don't sync removals back to the provider and you have enabled "
-    "automatic sync/import for this provider, the item may reappear in the library "
+    "Please note that if you you don't sync back to the provider and you have enabled "
+    "automatic sync/import for this provider, a removed item may reappear in the library "
     "the next time a sync is performed.",
-    default_value="export_favorite",
+    default_value=True,
     category="sync_options",
-    options=[
-        ConfigValueOption("When an item is removed from the library", "export_library"),
-        ConfigValueOption("When an item is unmarked as favorite", "export_favorite"),
-        ConfigValueOption("Don't sync back to the provider", "no_export"),
-    ],
 )
 
+
 CONF_PROVIDER_SYNC_INTERVAL_OPTIONS = [
     ConfigValueOption("Disable automatic sync for this mediatype", 0),
     ConfigValueOption("Every 30 minutes", 30),
@@ -827,7 +788,7 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_ARTISTS = ConfigEntry(
     options=CONF_PROVIDER_SYNC_INTERVAL_OPTIONS,
     default_value=720,
     category="sync_options",
-    depends_on=CONF_ENTRY_LIBRARY_IMPORT_ARTISTS.key,
+    depends_on=CONF_ENTRY_LIBRARY_SYNC_ARTISTS.key,
     depends_on_value_not="no_import",
     required=True,
 )
@@ -839,7 +800,7 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_ALBUMS = ConfigEntry(
     options=CONF_PROVIDER_SYNC_INTERVAL_OPTIONS,
     default_value=720,
     category="sync_options",
-    depends_on=CONF_ENTRY_LIBRARY_IMPORT_ALBUMS.key,
+    depends_on=CONF_ENTRY_LIBRARY_SYNC_ALBUMS.key,
     depends_on_value_not="no_import",
     required=True,
 )
@@ -851,7 +812,7 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_TRACKS = ConfigEntry(
     options=CONF_PROVIDER_SYNC_INTERVAL_OPTIONS,
     default_value=720,
     category="sync_options",
-    depends_on=CONF_ENTRY_LIBRARY_IMPORT_TRACKS.key,
+    depends_on=CONF_ENTRY_LIBRARY_SYNC_TRACKS.key,
     depends_on_value_not="no_import",
     required=True,
 )
@@ -863,7 +824,7 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_PLAYLISTS = ConfigEntry(
     options=CONF_PROVIDER_SYNC_INTERVAL_OPTIONS,
     default_value=720,
     category="sync_options",
-    depends_on=CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS.key,
+    depends_on=CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS.key,
     depends_on_value_not="no_import",
     required=True,
 )
@@ -875,7 +836,7 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_PODCASTS = ConfigEntry(
     options=CONF_PROVIDER_SYNC_INTERVAL_OPTIONS,
     default_value=720,
     category="sync_options",
-    depends_on=CONF_ENTRY_LIBRARY_IMPORT_PODCASTS.key,
+    depends_on=CONF_ENTRY_LIBRARY_SYNC_PODCASTS.key,
     depends_on_value_not="no_import",
     required=True,
 )
@@ -887,7 +848,7 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_AUDIOBOOKS = ConfigEntry(
     options=CONF_PROVIDER_SYNC_INTERVAL_OPTIONS,
     default_value=720,
     category="sync_options",
-    depends_on=CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS.key,
+    depends_on=CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS.key,
     depends_on_value_not="no_import",
     required=True,
 )
@@ -899,7 +860,7 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS = ConfigEntry(
     options=CONF_PROVIDER_SYNC_INTERVAL_OPTIONS,
     default_value=720,
     category="sync_options",
-    depends_on=CONF_ENTRY_LIBRARY_IMPORT_RADIOS.key,
+    depends_on=CONF_ENTRY_LIBRARY_SYNC_RADIOS.key,
     depends_on_value_not="no_import",
     required=True,
 )
index 09ba41ebf9d5f47662434fb22f663f7de4658c43..58ee0db4920efcf9f30c92159a0c0ddbdcdd57dc 100644 (file)
@@ -36,17 +36,16 @@ from music_assistant.constants import (
     CONF_DEPRECATED_EQ_BASS,
     CONF_DEPRECATED_EQ_MID,
     CONF_DEPRECATED_EQ_TREBLE,
-    CONF_ENTRY_LIBRARY_EXPORT_ADD,
-    CONF_ENTRY_LIBRARY_EXPORT_REMOVE,
-    CONF_ENTRY_LIBRARY_IMPORT_ALBUM_TRACKS,
-    CONF_ENTRY_LIBRARY_IMPORT_ALBUMS,
-    CONF_ENTRY_LIBRARY_IMPORT_ARTISTS,
-    CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS,
-    CONF_ENTRY_LIBRARY_IMPORT_PLAYLIST_TRACKS,
-    CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS,
-    CONF_ENTRY_LIBRARY_IMPORT_PODCASTS,
-    CONF_ENTRY_LIBRARY_IMPORT_RADIOS,
-    CONF_ENTRY_LIBRARY_IMPORT_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_ALBUM_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_ALBUMS,
+    CONF_ENTRY_LIBRARY_SYNC_ARTISTS,
+    CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS,
+    CONF_ENTRY_LIBRARY_SYNC_BACK,
+    CONF_ENTRY_LIBRARY_SYNC_PLAYLIST_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS,
+    CONF_ENTRY_LIBRARY_SYNC_PODCASTS,
+    CONF_ENTRY_LIBRARY_SYNC_RADIOS,
+    CONF_ENTRY_LIBRARY_SYNC_TRACKS,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_ALBUMS,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_ARTISTS,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_AUDIOBOOKS,
@@ -283,23 +282,23 @@ class ConfigController:
         if manifest.type == ProviderType.MUSIC:
             # library sync settings
             if ProviderFeature.LIBRARY_ARTISTS in supported_features:
-                extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_ARTISTS)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_ARTISTS)
             if ProviderFeature.LIBRARY_ALBUMS in supported_features:
-                extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_ALBUMS)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_ALBUMS)
                 if provider and provider.is_streaming_provider:
-                    extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_ALBUM_TRACKS)
+                    extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_ALBUM_TRACKS)
             if ProviderFeature.LIBRARY_TRACKS in supported_features:
-                extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_TRACKS)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_TRACKS)
             if ProviderFeature.LIBRARY_PLAYLISTS in supported_features:
-                extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS)
                 if provider and provider.is_streaming_provider:
-                    extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_PLAYLIST_TRACKS)
+                    extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_PLAYLIST_TRACKS)
             if ProviderFeature.LIBRARY_AUDIOBOOKS in supported_features:
-                extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS)
             if ProviderFeature.LIBRARY_PODCASTS in supported_features:
-                extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_PODCASTS)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_PODCASTS)
             if ProviderFeature.LIBRARY_RADIOS in supported_features:
-                extra_entries.append(CONF_ENTRY_LIBRARY_IMPORT_RADIOS)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_RADIOS)
             # sync interval settings
             if ProviderFeature.LIBRARY_ARTISTS in supported_features:
                 extra_entries.append(CONF_ENTRY_PROVIDER_SYNC_INTERVAL_ARTISTS)
@@ -327,8 +326,7 @@ class ConfigController:
                     ProviderFeature.LIBRARY_RADIOS_EDIT,
                 }
             ):
-                extra_entries.append(CONF_ENTRY_LIBRARY_EXPORT_ADD)
-                extra_entries.append(CONF_ENTRY_LIBRARY_EXPORT_REMOVE)
+                extra_entries.append(CONF_ENTRY_LIBRARY_SYNC_BACK)
 
         return [
             *DEFAULT_PROVIDER_CONFIG_ENTRIES,
index f71e42e9eaca4d9f8a01e97e44682d44cce23eb5..0bb634dacb443a7d59575cda8807e840ccc89aff 100644 (file)
@@ -302,9 +302,7 @@ class AlbumsController(MediaControllerBase[Album]):
             extra_query_parts=[f"WHERE album_tracks.album_id = {item_id}"],
         )
 
-    async def add_item_mapping_as_album_to_library(
-        self, item: ItemMapping, import_as_favorite: bool = False
-    ) -> Album:
+    async def add_item_mapping_as_album_to_library(self, item: ItemMapping) -> Album:
         """
         Add an ItemMapping as an Album to the library.
 
index a5e79c0a139ce124512c4ecd01f046fd28fb47bc..a235409d01428562f71f0e474eb0404f89726e74 100644 (file)
@@ -43,8 +43,6 @@ from music_assistant_models.provider import SyncTask
 from music_assistant_models.unique_list import UniqueList
 
 from music_assistant.constants import (
-    CONF_ENTRY_LIBRARY_EXPORT_ADD,
-    CONF_ENTRY_LIBRARY_EXPORT_REMOVE,
     DB_TABLE_ALBUM_ARTISTS,
     DB_TABLE_ALBUM_TRACKS,
     DB_TABLE_ALBUMS,
@@ -210,14 +208,13 @@ class MusicController(CoreController):
                 if not provider.library_supported(media_type):
                     continue
                 # handle mediatype specific sync config
-                conf_key = f"library_import_{media_type}s"
+                conf_key = f"library_sync_{media_type}s"
                 sync_conf = await self.mass.config.get_provider_config_value(
                     provider.instance_id, conf_key
                 )
-                if sync_conf == "no_import":
+                if not sync_conf:
                     continue
-                import_as_favorite = sync_conf == "import_as_favorite"
-                self._start_provider_sync(provider, media_type, import_as_favorite)
+                self._start_provider_sync(provider, media_type)
 
     @api_command("music/synctasks")
     def get_running_sync_tasks(self) -> list[SyncTask]:
@@ -620,18 +617,21 @@ class MusicController(CoreController):
             provider = self.mass.get_provider(prov_mapping.provider_instance)
             if not provider.library_edit_supported(item.media_type):
                 continue
-            if prov_mapping.in_library:
+            if not provider.library_sync_back_enabled(full_item.media_type):
                 continue
-            conf_export_library = provider.config.get_value(
-                CONF_ENTRY_LIBRARY_EXPORT_ADD.key, CONF_ENTRY_LIBRARY_EXPORT_ADD.default_value
-            )
-            if conf_export_library != "export_favorite":
+            if not prov_mapping.in_library:
+                # add to provider library first
+                prov_item = deepcopy(full_item)
+                prov_item.provider = prov_mapping.provider_instance
+                prov_item.item_id = prov_mapping.item_id
+                await provider.library_add(prov_item)
+                provider_mappings_updated = True
+                prov_mapping.in_library = True
+            # set favorite at provider
+            if not provider.library_favorites_edit_supported(full_item.media_type):
                 continue
-            prov_item = deepcopy(full_item)
-            prov_item.provider = prov_mapping.provider_instance
-            prov_item.item_id = prov_mapping.item_id
-            self.mass.create_task(provider.library_add(prov_item))
-            provider_mappings_updated = True
+            await provider.set_favorite(prov_mapping.item_id, full_item.media_type, True)
+
         if provider_mappings_updated:
             await ctrl.set_provider_mappings(full_item.item_id, full_item.provider_mappings)
 
@@ -654,14 +654,11 @@ class MusicController(CoreController):
             if not prov_mapping.in_library:
                 continue
             provider = self.mass.get_provider(prov_mapping.provider_instance)
-            if not provider.library_edit_supported(full_item.media_type):
+            if not provider.library_favorites_edit_supported(full_item.media_type):
                 continue
-            conf_export_library = provider.config.get_value(
-                CONF_ENTRY_LIBRARY_EXPORT_REMOVE.key, CONF_ENTRY_LIBRARY_EXPORT_REMOVE.default_value
-            )
-            if conf_export_library != "export_favorite":
+            if not provider.library_sync_back_enabled(full_item.media_type):
                 continue
-            self.mass.create_task(provider.library_remove(prov_mapping.item_id, media_type))
+            self.mass.create_task(provider.set_favorite(prov_mapping.item_id, media_type, False))
             prov_mapping.in_library = False
             provider_mappings_updated = True
         if provider_mappings_updated:
@@ -685,11 +682,9 @@ class MusicController(CoreController):
             provider = self.mass.get_provider(prov_mapping.provider_instance)
             if not provider.library_edit_supported(full_item.media_type):
                 continue
-            conf_export_library = provider.config.get_value(
-                CONF_ENTRY_LIBRARY_EXPORT_REMOVE.key, CONF_ENTRY_LIBRARY_EXPORT_REMOVE.default_value
-            )
-            if conf_export_library != "export_library":
+            if not provider.library_sync_back_enabled(full_item.media_type):
                 continue
+            prov_mapping.in_library = False
             self.mass.create_task(provider.library_remove(prov_mapping.item_id, media_type))
         # remove from library
         await ctrl.remove_item_from_library(library_item_id, recursive)
@@ -713,10 +708,7 @@ class MusicController(CoreController):
             provider = self.mass.get_provider(prov_mapping.provider_instance)
             if not provider.library_edit_supported(full_item.media_type):
                 continue
-            conf_export_library = provider.config.get_value(
-                CONF_ENTRY_LIBRARY_EXPORT_ADD.key, CONF_ENTRY_LIBRARY_EXPORT_ADD.default_value
-            )
-            if conf_export_library != "export_library":
+            if not provider.library_sync_back_enabled(full_item.media_type):
                 continue
             prov_item = deepcopy(full_item) if full_item.provider == "library" else full_item
             prov_item.provider = prov_mapping.provider_instance
@@ -1408,9 +1400,7 @@ class MusicController(CoreController):
             )
             return []
 
-    def _start_provider_sync(
-        self, provider: MusicProvider, media_type: MediaType, import_as_favorite: bool
-    ) -> None:
+    def _start_provider_sync(self, provider: MusicProvider, media_type: MediaType) -> None:
         """Start sync task on provider and track progress."""
         # check if we're not already running a sync task for this provider/mediatype
         for sync_task in self.in_progress_syncs:
@@ -1430,7 +1420,7 @@ class MusicController(CoreController):
             # Wrap the provider sync into a lock to prevent
             # race conditions when multiple providers are syncing at the same time.
             async with self._sync_lock:
-                await provider.sync_library(media_type, import_as_favorite)
+                await provider.sync_library(media_type)
 
         # we keep track of running sync tasks
         task = self.mass.create_task(run_sync())
@@ -1523,9 +1513,9 @@ class MusicController(CoreController):
         # cancel any existing timers
         self.mass.cancel_timer(job_key)
         # handle mediatype specific sync config
-        conf_key = f"library_import_{media_type}s"
+        conf_key = f"library_sync_{media_type}s"
         sync_conf = await self.mass.config.get_provider_config_value(provider.instance_id, conf_key)
-        if sync_conf == "no_import":
+        if not sync_conf:
             return
         conf_key = f"provider_sync_interval_{media_type.value}s"
         sync_interval = cast(
@@ -1536,7 +1526,6 @@ class MusicController(CoreController):
             # sync disabled for this media type
             return
         sync_interval = sync_interval * 60  # config interval is in minutes - convert to seconds
-        import_as_favorite = sync_conf == "import_as_favorite"
 
         if is_initial:
             # schedule the first sync run
@@ -1554,7 +1543,6 @@ class MusicController(CoreController):
             self._start_provider_sync,
             provider,
             media_type,
-            import_as_favorite,
             task_id=job_key,
         )
 
index d170b7b3b83784a8ef080525a373ab37d8a35f26..adf5d4df51810d61f4c2e3f0523128a77137c241 100644 (file)
@@ -29,8 +29,9 @@ from music_assistant_models.media_items import (
 )
 
 from music_assistant.constants import (
-    CONF_ENTRY_LIBRARY_IMPORT_ALBUM_TRACKS,
-    CONF_ENTRY_LIBRARY_IMPORT_PLAYLIST_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_ALBUM_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_BACK,
+    CONF_ENTRY_LIBRARY_SYNC_PLAYLIST_TRACKS,
 )
 
 from .provider import Provider
@@ -312,6 +313,51 @@ class MusicProvider(Provider):
         )
         return True
 
+    async def set_favorite(self, prov_item_id: str, media_type: MediaType, favorite: bool) -> None:
+        """
+        Set favorite status for item in provider's library.
+
+        Only called if provider supports ProviderFeature.FAVORITE_*_EDIT.
+
+        Note that this should only be implemented by a provider implementation if
+        the provider differentiates between 'in library' and 'favorited' items.
+        """
+        if (
+            media_type == MediaType.ARTIST
+            and ProviderFeature.FAVORITE_ARTISTS_EDIT in self.supported_features
+        ):
+            raise NotImplementedError
+        if (
+            media_type == MediaType.ALBUM
+            and ProviderFeature.FAVORITE_ALBUMS_EDIT in self.supported_features
+        ):
+            raise NotImplementedError
+        if (
+            media_type == MediaType.TRACK
+            and ProviderFeature.FAVORITE_TRACKS_EDIT in self.supported_features
+        ):
+            raise NotImplementedError
+        if (
+            media_type == MediaType.PLAYLIST
+            and ProviderFeature.FAVORITE_PLAYLISTS_EDIT in self.supported_features
+        ):
+            raise NotImplementedError
+        if (
+            media_type == MediaType.RADIO
+            and ProviderFeature.FAVORITE_RADIOS_EDIT in self.supported_features
+        ):
+            raise NotImplementedError
+        if (
+            media_type == MediaType.AUDIOBOOK
+            and ProviderFeature.FAVORITE_AUDIOBOOKS_EDIT in self.supported_features
+        ):
+            raise NotImplementedError
+        if (
+            media_type == MediaType.PODCAST
+            and ProviderFeature.FAVORITE_PODCASTS_EDIT in self.supported_features
+        ):
+            raise NotImplementedError
+
     async def add_playlist_tracks(self, prov_playlist_id: str, prov_track_ids: list[str]) -> None:
         """Add track(s) to playlist.
 
@@ -578,7 +624,7 @@ class MusicProvider(Provider):
             raise NotImplementedError
         return []
 
-    async def sync_library(self, media_type: MediaType, import_as_favorite: bool) -> None:
+    async def sync_library(self, media_type: MediaType) -> None:
         """Run library sync for this provider."""
         # this reference implementation may be overridden
         # with a provider specific approach if needed
@@ -587,19 +633,19 @@ class MusicProvider(Provider):
             raise UnsupportedFeaturedException("Library sync not supported for this media type")
 
         if media_type == MediaType.ARTIST:
-            cur_db_ids = await self._sync_library_artists(import_as_favorite)
+            cur_db_ids = await self._sync_library_artists()
         elif media_type == MediaType.ALBUM:
-            cur_db_ids = await self._sync_library_albums(import_as_favorite)
+            cur_db_ids = await self._sync_library_albums()
         elif media_type == MediaType.TRACK:
-            cur_db_ids = await self._sync_library_tracks(import_as_favorite)
+            cur_db_ids = await self._sync_library_tracks()
         elif media_type == MediaType.PLAYLIST:
-            cur_db_ids = await self._sync_library_playlists(import_as_favorite)
+            cur_db_ids = await self._sync_library_playlists()
         elif media_type == MediaType.PODCAST:
-            cur_db_ids = await self._sync_library_podcasts(import_as_favorite)
+            cur_db_ids = await self._sync_library_podcasts()
         elif media_type == MediaType.RADIO:
-            cur_db_ids = await self._sync_library_radios(import_as_favorite)
+            cur_db_ids = await self._sync_library_radios()
         elif media_type == MediaType.AUDIOBOOK:
-            cur_db_ids = await self._sync_library_audiobooks(import_as_favorite)
+            cur_db_ids = await self._sync_library_audiobooks()
         else:
             # this should not happen but catch it anyways
             raise UnsupportedFeaturedException(f"Unexpected media type to sync: {media_type}")
@@ -664,7 +710,7 @@ class MusicProvider(Provider):
             category=CACHE_CATEGORY_PREV_LIBRARY_IDS,
         )
 
-    async def _sync_library_artists(self, import_as_favorite: bool) -> set[int]:
+    async def _sync_library_artists(self) -> set[int]:
         """Sync Library Artists to Music Assistant library."""
         self.logger.debug("Start sync of Artists to Music Assistant library.")
         cur_db_ids: set[int] = set()
@@ -675,10 +721,8 @@ class MusicProvider(Provider):
             try:
                 if not library_item:
                     # add item to the library
-                    if import_as_favorite:
-                        prov_item.favorite = True
                     library_item = await self.mass.music.artists.add_item_to_library(prov_item)
-                elif not library_item.favorite and import_as_favorite:
+                elif not library_item.favorite and prov_item.favorite:
                     # existing library item not favorite but should be
                     await self.mass.music.artists.set_favorite(library_item.item_id, True)
                 elif not self._check_provider_mappings(library_item, prov_item, True):
@@ -696,13 +740,13 @@ class MusicProvider(Provider):
                 )
         return cur_db_ids
 
-    async def _sync_library_albums(self, import_as_favorite: bool) -> set[int]:
+    async def _sync_library_albums(self) -> set[int]:
         """Sync Library Albums to Music Assistant library."""
         self.logger.debug("Start sync of Albums to Music Assistant library.")
         cur_db_ids: set[int] = set()
         conf_sync_album_tracks = self.config.get_value(
-            CONF_ENTRY_LIBRARY_IMPORT_ALBUM_TRACKS.key,
-            CONF_ENTRY_LIBRARY_IMPORT_ALBUM_TRACKS.default_value,
+            CONF_ENTRY_LIBRARY_SYNC_ALBUM_TRACKS.key,
+            CONF_ENTRY_LIBRARY_SYNC_ALBUM_TRACKS.default_value,
         )
         sync_album_tracks = bool(conf_sync_album_tracks)
         async for prov_item in self.get_library_albums():
@@ -712,10 +756,8 @@ class MusicProvider(Provider):
             try:
                 if not library_item:
                     # add item to the library
-                    if import_as_favorite:
-                        prov_item.favorite = True
                     library_item = await self.mass.music.albums.add_item_to_library(prov_item)
-                elif not library_item.favorite and import_as_favorite:
+                elif not library_item.favorite and prov_item.favorite:
                     # existing library item not favorite but should be
                     await self.mass.music.albums.set_favorite(library_item.item_id, True)
                 elif not self._check_provider_mappings(library_item, prov_item, True):
@@ -763,7 +805,7 @@ class MusicProvider(Provider):
                     str(err),
                 )
 
-    async def _sync_library_audiobooks(self, import_as_favorite: bool) -> set[int]:
+    async def _sync_library_audiobooks(self) -> set[int]:
         """Sync Library Audiobooks to Music Assistant library."""
         self.logger.debug("Start sync of Audiobooks to Music Assistant library.")
         cur_db_ids: set[int] = set()
@@ -774,10 +816,8 @@ class MusicProvider(Provider):
             try:
                 if not library_item:
                     # add item to the library
-                    if import_as_favorite:
-                        prov_item.favorite = True
                     library_item = await self.mass.music.audiobooks.add_item_to_library(prov_item)
-                elif not library_item.favorite and import_as_favorite:
+                elif not library_item.favorite and prov_item.favorite:
                     # existing library item not favorite but should be
                     await self.mass.music.audiobooks.set_favorite(library_item.item_id, True)
                 elif not self._check_provider_mappings(library_item, prov_item, True):
@@ -809,12 +849,12 @@ class MusicProvider(Provider):
                 )
         return cur_db_ids
 
-    async def _sync_library_playlists(self, import_as_favorite: bool) -> set[int]:
+    async def _sync_library_playlists(self) -> set[int]:
         """Sync Library Playlists to Music Assistant library."""
         self.logger.debug("Start sync of Playlists to Music Assistant library.")
         conf_sync_playlist_tracks = self.config.get_value(
-            CONF_ENTRY_LIBRARY_IMPORT_PLAYLIST_TRACKS.key,
-            CONF_ENTRY_LIBRARY_IMPORT_PLAYLIST_TRACKS.default_value,
+            CONF_ENTRY_LIBRARY_SYNC_PLAYLIST_TRACKS.key,
+            CONF_ENTRY_LIBRARY_SYNC_PLAYLIST_TRACKS.default_value,
         )
         conf_sync_playlist_tracks = cast("list[str]", conf_sync_playlist_tracks)
         cur_db_ids: set[int] = set()
@@ -825,10 +865,8 @@ class MusicProvider(Provider):
             try:
                 if not library_item:
                     # add item to the library
-                    if import_as_favorite:
-                        prov_item.favorite = True
                     library_item = await self.mass.music.playlists.add_item_to_library(prov_item)
-                elif not library_item.favorite and import_as_favorite:
+                elif not library_item.favorite and prov_item.favorite:
                     # existing library item not favorite but should be
                     await self.mass.music.playlists.set_favorite(library_item.item_id, True)
                 elif not self._check_provider_mappings(library_item, prov_item, True):
@@ -879,7 +917,7 @@ class MusicProvider(Provider):
                     str(err),
                 )
 
-    async def _sync_library_tracks(self, import_as_favorite: bool) -> set[int]:
+    async def _sync_library_tracks(self) -> set[int]:
         """Sync Library Tracks to Music Assistant library."""
         self.logger.debug("Start sync of Tracks to Music Assistant library.")
         cur_db_ids: set[int] = set()
@@ -898,15 +936,13 @@ class MusicProvider(Provider):
                     continue
                 if not library_item:
                     # add item to the library
-                    if import_as_favorite:
-                        prov_item.favorite = True
                     library_item = await self.mass.music.tracks.add_item_to_library(prov_item)
                 elif library_item.available != prov_item.available:
                     # existing library item but availability changed
                     library_item = await self.mass.music.tracks.update_item_in_library(
                         library_item.item_id, prov_item
                     )
-                elif not library_item.favorite and import_as_favorite:
+                elif not library_item.favorite and prov_item.favorite:
                     # existing library item not favorite but should be
                     await self.mass.music.tracks.set_favorite(library_item.item_id, True)
                 elif not self._check_provider_mappings(library_item, prov_item, True):
@@ -924,7 +960,7 @@ class MusicProvider(Provider):
                 )
         return cur_db_ids
 
-    async def _sync_library_podcasts(self, import_as_favorite: bool) -> set[int]:
+    async def _sync_library_podcasts(self) -> set[int]:
         """Sync Library Podcasts to Music Assistant library."""
         self.logger.debug("Start sync of Podcasts to Music Assistant library.")
         cur_db_ids: set[int] = set()
@@ -935,15 +971,13 @@ class MusicProvider(Provider):
             try:
                 if not library_item:
                     # add item to the library
-                    if import_as_favorite:
-                        prov_item.favorite = True
                     library_item = await self.mass.music.podcasts.add_item_to_library(prov_item)
                 elif library_item.available != prov_item.available:
                     # existing library item but availability changed
                     library_item = await self.mass.music.podcasts.update_item_in_library(
                         library_item.item_id, prov_item
                     )
-                elif not library_item.favorite and import_as_favorite:
+                elif not library_item.favorite and prov_item.favorite:
                     # existing library item not favorite but should be
                     await self.mass.music.podcasts.set_favorite(library_item.item_id, True)
                 elif not self._check_provider_mappings(library_item, prov_item, True):
@@ -968,7 +1002,7 @@ class MusicProvider(Provider):
                 )
         return cur_db_ids
 
-    async def _sync_library_radios(self, import_as_favorite: bool) -> set[int]:
+    async def _sync_library_radios(self) -> set[int]:
         """Sync Library Radios to Music Assistant library."""
         self.logger.debug("Start sync of Radios to Music Assistant library.")
         cur_db_ids: set[int] = set()
@@ -979,10 +1013,8 @@ class MusicProvider(Provider):
             try:
                 if not library_item:
                     # add item to the library
-                    if import_as_favorite:
-                        prov_item.favorite = True
                     library_item = await self.mass.music.radio.add_item_to_library(prov_item)
-                elif not library_item.favorite and import_as_favorite:
+                elif not library_item.favorite and prov_item.favorite:
                     # existing library item not favorite but should be
                     await self.mass.music.radio.set_favorite(library_item.item_id, True)
                 elif not self._check_provider_mappings(library_item, prov_item, True):
@@ -1040,6 +1072,31 @@ class MusicProvider(Provider):
             return ProviderFeature.LIBRARY_PODCASTS_EDIT in self.supported_features
         return False
 
+    def library_sync_back_enabled(self, media_type: MediaType) -> bool:
+        """Return if Library sync back is enabled for given MediaType on this provider."""
+        conf_value = self.config.get_value(
+            CONF_ENTRY_LIBRARY_SYNC_BACK.key, CONF_ENTRY_LIBRARY_SYNC_BACK.default_value
+        )
+        return bool(conf_value)
+
+    def library_favorites_edit_supported(self, media_type: MediaType) -> bool:
+        """Return if favorites add/remove is supported for given MediaType on this provider."""
+        if media_type == MediaType.ARTIST:
+            return ProviderFeature.FAVORITE_ARTISTS_EDIT in self.supported_features
+        if media_type == MediaType.ALBUM:
+            return ProviderFeature.FAVORITE_ALBUMS_EDIT in self.supported_features
+        if media_type == MediaType.TRACK:
+            return ProviderFeature.FAVORITE_TRACKS_EDIT in self.supported_features
+        if media_type == MediaType.PLAYLIST:
+            return ProviderFeature.FAVORITE_PLAYLISTS_EDIT in self.supported_features
+        if media_type == MediaType.RADIO:
+            return ProviderFeature.FAVORITE_RADIOS_EDIT in self.supported_features
+        if media_type == MediaType.AUDIOBOOK:
+            return ProviderFeature.FAVORITE_AUDIOBOOKS_EDIT in self.supported_features
+        if media_type == MediaType.PODCAST:
+            return ProviderFeature.FAVORITE_PODCASTS_EDIT in self.supported_features
+        return False
+
     async def iter_playlist_tracks(
         self,
         prov_playlist_id: str,
index 3bb4788fc2e593fe0379fbf0176e659ed653ba37..67fbe7f8eda8a670358262f4444a6c267f890c56 100644 (file)
@@ -584,7 +584,7 @@ class MyDemoMusicprovider(MusicProvider):
         # This is only called if you reported the RECOMMENDATIONS feature in the supported_features.
         return []
 
-    async def sync_library(self, media_type: MediaType, import_as_favorite: bool) -> None:
+    async def sync_library(self, media_type: MediaType) -> None:
         """Run library sync for this provider."""
         # Run a full sync of the library for the given media type.
         # This is called by the music controller to sync items from your provider to the library.
index 4bcd82267b9d4b9435123852a4380d3581bb1233..c67e326a5fdc7f6c9f3866a9c4ae3f58f0b433d5 100644 (file)
@@ -343,7 +343,7 @@ for more details.
         return False
 
     @handle_refresh_token
-    async def sync_library(self, media_type: MediaType, import_as_favorite: bool) -> None:
+    async def sync_library(self, media_type: MediaType) -> None:
         """Obtain audiobook library ids and podcast library ids."""
         libraries = await self._client.get_all_libraries()
         if len(libraries) == 0:
@@ -356,7 +356,7 @@ for more details.
                 and media_type == MediaType.PODCAST
             ):
                 self.libraries.podcasts[library.id_] = LibraryHelper(name=library.name)
-        await super().sync_library(media_type, import_as_favorite)
+        await super().sync_library(media_type)
         await self._cache_set_helper_libraries()
 
         # update playlog
index 1fdb9d524b918bf6c8e2574a06d2c15f8e9c7619..98a6390c3ea185bd567c84a98215d696d7b9ff11 100644 (file)
@@ -46,11 +46,10 @@ from .constants import (
     BUILTIN_PLAYLISTS,
     BUILTIN_PLAYLISTS_ENTRIES,
     COLLAGE_IMAGE_PLAYLISTS,
-    CONF_ENTRY_LIBRARY_EXPORT_ADD_HIDDEN,
-    CONF_ENTRY_LIBRARY_EXPORT_REMOVE_HIDDEN,
-    CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS_HIDDEN,
-    CONF_ENTRY_LIBRARY_IMPORT_RADIOS_HIDDEN,
-    CONF_ENTRY_LIBRARY_IMPORT_TRACKS_HIDDEN,
+    CONF_ENTRY_LIBRARY_SYNC_BACK_HIDDEN,
+    CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS_HIDDEN,
+    CONF_ENTRY_LIBRARY_SYNC_RADIOS_HIDDEN,
+    CONF_ENTRY_LIBRARY_SYNC_TRACKS_HIDDEN,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_PLAYLISTS_MOD,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS_HIDDEN,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_TRACKS_HIDDEN,
@@ -110,14 +109,13 @@ async def get_config_entries(
     return (
         *BUILTIN_PLAYLISTS_ENTRIES,
         # hide some of the default (dynamic) entries for library management
-        CONF_ENTRY_LIBRARY_IMPORT_TRACKS_HIDDEN,
-        CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS_HIDDEN,
-        CONF_ENTRY_LIBRARY_IMPORT_RADIOS_HIDDEN,
+        CONF_ENTRY_LIBRARY_SYNC_TRACKS_HIDDEN,
+        CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS_HIDDEN,
+        CONF_ENTRY_LIBRARY_SYNC_RADIOS_HIDDEN,
         CONF_ENTRY_PROVIDER_SYNC_INTERVAL_TRACKS_HIDDEN,
         CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS_HIDDEN,
         CONF_ENTRY_PROVIDER_SYNC_INTERVAL_PLAYLISTS_MOD,
-        CONF_ENTRY_LIBRARY_EXPORT_ADD_HIDDEN,
-        CONF_ENTRY_LIBRARY_EXPORT_REMOVE_HIDDEN,
+        CONF_ENTRY_LIBRARY_SYNC_BACK_HIDDEN,
     )
 
 
index 95e10b301098f62e21a7184645740cc89b6c76b1..49210a90c75f604b0b642f35da6c64cd23e2cadb 100644 (file)
@@ -9,11 +9,10 @@ from music_assistant_models.enums import ConfigEntryType, ImageType
 from music_assistant_models.media_items import MediaItemImage
 
 from music_assistant.constants import (
-    CONF_ENTRY_LIBRARY_EXPORT_ADD,
-    CONF_ENTRY_LIBRARY_EXPORT_REMOVE,
-    CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS,
-    CONF_ENTRY_LIBRARY_IMPORT_RADIOS,
-    CONF_ENTRY_LIBRARY_IMPORT_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_BACK,
+    CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS,
+    CONF_ENTRY_LIBRARY_SYNC_RADIOS,
+    CONF_ENTRY_LIBRARY_SYNC_TRACKS,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_PLAYLISTS,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_TRACKS,
@@ -74,32 +73,32 @@ DEFAULT_FANART = MediaItemImage(
     remotely_accessible=False,
 )
 
-CONF_ENTRY_LIBRARY_IMPORT_TRACKS_HIDDEN = ConfigEntry.from_dict(
+CONF_ENTRY_LIBRARY_SYNC_TRACKS_HIDDEN = ConfigEntry.from_dict(
     {
-        **CONF_ENTRY_LIBRARY_IMPORT_TRACKS.to_dict(),
+        **CONF_ENTRY_LIBRARY_SYNC_TRACKS.to_dict(),
         "hidden": True,
-        "default_value": "import_only",
+        "default_value": True,
     }
 )
-CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS_HIDDEN = ConfigEntry.from_dict(
+CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS_HIDDEN = ConfigEntry.from_dict(
     {
-        **CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS.to_dict(),
+        **CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS.to_dict(),
         "hidden": True,
-        "default_value": "import_only",
+        "default_value": True,
     }
 )
-CONF_ENTRY_LIBRARY_IMPORT_TRACKS_HIDDEN = ConfigEntry.from_dict(
+CONF_ENTRY_LIBRARY_SYNC_TRACKS_HIDDEN = ConfigEntry.from_dict(
     {
-        **CONF_ENTRY_LIBRARY_IMPORT_TRACKS.to_dict(),
+        **CONF_ENTRY_LIBRARY_SYNC_TRACKS.to_dict(),
         "hidden": True,
-        "default_value": "import_only",
+        "default_value": True,
     }
 )
-CONF_ENTRY_LIBRARY_IMPORT_RADIOS_HIDDEN = ConfigEntry.from_dict(
+CONF_ENTRY_LIBRARY_SYNC_RADIOS_HIDDEN = ConfigEntry.from_dict(
     {
-        **CONF_ENTRY_LIBRARY_IMPORT_RADIOS.to_dict(),
+        **CONF_ENTRY_LIBRARY_SYNC_RADIOS.to_dict(),
         "hidden": True,
-        "default_value": "import_only",
+        "default_value": True,
     }
 )
 CONF_ENTRY_PROVIDER_SYNC_INTERVAL_PLAYLISTS_MOD = ConfigEntry.from_dict(
@@ -126,17 +125,10 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS_HIDDEN = ConfigEntry.from_dict(
         "default_value": 180,
     }
 )
-CONF_ENTRY_LIBRARY_EXPORT_ADD_HIDDEN = ConfigEntry.from_dict(
+CONF_ENTRY_LIBRARY_SYNC_BACK_HIDDEN = ConfigEntry.from_dict(
     {
-        **CONF_ENTRY_LIBRARY_EXPORT_ADD.to_dict(),
+        **CONF_ENTRY_LIBRARY_SYNC_BACK.to_dict(),
         "hidden": True,
-        "default_value": "export_library",
-    }
-)
-CONF_ENTRY_LIBRARY_EXPORT_REMOVE_HIDDEN = ConfigEntry.from_dict(
-    {
-        **CONF_ENTRY_LIBRARY_EXPORT_REMOVE.to_dict(),
-        "hidden": True,
-        "default_value": "export_library",
+        "default_value": True,
     }
 )
index 557ce34835a03a143c218998308712ace7fa3757..500cf57924d9fd29eef40196ba5168fd296310d1 100644 (file)
@@ -82,10 +82,10 @@ from .constants import (
     CONF_ENTRY_CONTENT_TYPE,
     CONF_ENTRY_CONTENT_TYPE_READ_ONLY,
     CONF_ENTRY_IGNORE_ALBUM_PLAYLISTS,
-    CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS,
-    CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS,
-    CONF_ENTRY_LIBRARY_IMPORT_PODCASTS,
-    CONF_ENTRY_LIBRARY_IMPORT_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS,
+    CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS,
+    CONF_ENTRY_LIBRARY_SYNC_PODCASTS,
+    CONF_ENTRY_LIBRARY_SYNC_TRACKS,
     CONF_ENTRY_MISSING_ALBUM_ARTIST,
     CONF_ENTRY_PATH,
     IMAGE_EXTENSIONS,
@@ -151,10 +151,10 @@ async def get_config_entries(
         CONF_ENTRY_PATH,
         CONF_ENTRY_MISSING_ALBUM_ARTIST,
         CONF_ENTRY_IGNORE_ALBUM_PLAYLISTS,
-        CONF_ENTRY_LIBRARY_IMPORT_TRACKS,
-        CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS,
-        CONF_ENTRY_LIBRARY_IMPORT_PODCASTS,
-        CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS,
+        CONF_ENTRY_LIBRARY_SYNC_TRACKS,
+        CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS,
+        CONF_ENTRY_LIBRARY_SYNC_PODCASTS,
+        CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS,
     ]
     if instance_id is None or values is None:
         return (CONF_ENTRY_CONTENT_TYPE, *base_entries)
@@ -318,7 +318,7 @@ class LocalFileSystemProvider(MusicProvider):
                 )
         return items
 
-    async def sync_library(self, media_type: MediaType, import_as_favorite: bool) -> None:
+    async def sync_library(self, media_type: MediaType) -> None:
         """Run library sync for this provider."""
         assert self.mass.music.database
         start_time = time.time()
@@ -370,7 +370,7 @@ class LocalFileSystemProvider(MusicProvider):
             try:
                 for item in listdir(self.base_path):
                     prev_checksum = file_checksums.get(item.relative_path)
-                    if self._process_item(item, prev_checksum, import_as_favorite):
+                    if self._process_item(item, prev_checksum):
                         cur_filenames.add(item.relative_path)
             finally:
                 self.sync_running = False
@@ -390,9 +390,7 @@ class LocalFileSystemProvider(MusicProvider):
         # process orphaned albums and artists
         await self._process_orphaned_albums_and_artists()
 
-    def _process_item(
-        self, item: FileSystemItem, prev_checksum: str | None, import_as_favorite: bool
-    ) -> bool:
+    def _process_item(self, item: FileSystemItem, prev_checksum: str | None) -> bool:
         """Process a single item. NOT async friendly."""
         try:
             self.logger.log(VERBOSE_LOG_LEVEL, "Processing: %s", item.relative_path)
@@ -422,7 +420,7 @@ class LocalFileSystemProvider(MusicProvider):
                     # add/update track to db
                     # note that filesystem items are always overwriting existing info
                     # when they are detected as changed
-                    track.favorite = import_as_favorite
+                    track.favorite = False  # TODO: implement favorite status based on rating ?
                     await self.mass.music.tracks.add_item_to_library(
                         track, overwrite_existing=prev_checksum is not None
                     )
@@ -442,7 +440,6 @@ class LocalFileSystemProvider(MusicProvider):
                     # add/update audiobook to db
                     # note that filesystem items are always overwriting existing info
                     # when they are detected as changed
-                    audiobook.favorite = import_as_favorite
                     await self.mass.music.audiobooks.add_item_to_library(
                         audiobook, overwrite_existing=prev_checksum is not None
                     )
@@ -460,7 +457,6 @@ class LocalFileSystemProvider(MusicProvider):
                     # add/update episode to db
                     # note that filesystem items are always overwriting existing info
                     # when they are detected as changed
-                    episode.favorite = import_as_favorite
                     await self.mass.music.podcasts.add_item_to_library(
                         episode.podcast, overwrite_existing=prev_checksum is not None
                     )
@@ -473,8 +469,7 @@ class LocalFileSystemProvider(MusicProvider):
 
                 async def process_playlist() -> None:
                     playlist = await self.get_playlist(item.relative_path)
-                    # add/update] playlist to db
-                    playlist.favorite = import_as_favorite
+                    # add/update playlist to db
                     await self.mass.music.playlists.add_item_to_library(
                         playlist,
                         overwrite_existing=prev_checksum is not None,
index 3c54c5e6a4badacea8366a9e501d289d582a1315..181cf461e011ca4cda4515cd40b20d74c934eacd 100644 (file)
@@ -7,8 +7,6 @@ from typing import Final
 from music_assistant_models.config_entries import ConfigEntry, ConfigValueOption
 from music_assistant_models.enums import ConfigEntryType, ProviderFeature
 
-from music_assistant.constants import CONF_LIBRARY_IMPORT_OPTIONS
-
 CONF_MISSING_ALBUM_ARTIST_ACTION = "missing_album_artist_action"
 CONF_CONTENT_TYPE = "content_type"
 
@@ -53,56 +51,52 @@ CONF_ENTRY_CONTENT_TYPE_READ_ONLY = ConfigEntry.from_dict(
     {**CONF_ENTRY_CONTENT_TYPE.to_dict(), "read_only": True}
 )
 
-CONF_ENTRY_LIBRARY_IMPORT_TRACKS = ConfigEntry(
-    key="library_import_tracks",
-    type=ConfigEntryType.STRING,
+CONF_ENTRY_LIBRARY_SYNC_TRACKS = ConfigEntry(
+    key="library_sync_tracks",
+    type=ConfigEntryType.BOOLEAN,
     label="Import tracks/files into the Music Assistant library",
     description="Define how/if you want to import tracks/files from the filesystem "
     "into the Music Assistant Library. \nWhen not importing into the library, "
     "they can still be manually browsed using the Browse feature. \n\n"
     "Please note that by adding a Track into the Music Assistant library, "
-    "the track artists and album will always be imported as well (not as favorites though).",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_only",
+    "the track artists and album will always be imported as well.",
+    default_value=True,
     category="sync_options",
     depends_on=CONF_CONTENT_TYPE,
     depends_on_value="music",
 )
-CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS = ConfigEntry(
-    key="library_import_playlists",
-    type=ConfigEntryType.STRING,
+CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS = ConfigEntry(
+    key="library_sync_playlists",
+    type=ConfigEntryType.BOOLEAN,
     label="Import playlists (m3u files) into the Music Assistant library",
     description="Define how/if you want to import playlists (m3u files) from the filesystem "
     "into the Music Assistant Library. \nWhen not importing into the library, "
     "they can still be manually browsed using the Browse feature.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_only",
+    default_value=True,
     category="sync_options",
     depends_on=CONF_CONTENT_TYPE,
     depends_on_value="music",
 )
-CONF_ENTRY_LIBRARY_IMPORT_PODCASTS = ConfigEntry(
-    key="library_import_podcasts",
-    type=ConfigEntryType.STRING,
+CONF_ENTRY_LIBRARY_SYNC_PODCASTS = ConfigEntry(
+    key="library_sync_podcasts",
+    type=ConfigEntryType.BOOLEAN,
     label="Import Podcasts(files) into the Music Assistant library",
     description="Define how/if you want to import Podcasts(files) from the filesystem "
     "into the Music Assistant Library. \nWhen not importing into the library, "
     "they can still be manually browsed using the Browse feature.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_only",
+    default_value=True,
     category="sync_options",
     depends_on=CONF_CONTENT_TYPE,
     depends_on_value="podcasts",
 )
-CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS = ConfigEntry(
-    key="library_import_audiobooks",
-    type=ConfigEntryType.STRING,
+CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS = ConfigEntry(
+    key="library_sync_audiobooks",
+    type=ConfigEntryType.BOOLEAN,
     label="Import Audiobooks(files) into the Music Assistant library",
     description="Define how/if you want to import Audiobooks(files) from the filesystem "
     "into the Music Assistant Library. \nWhen not importing into the library, "
     "they can still be manually browsed using the Browse feature.",
-    options=CONF_LIBRARY_IMPORT_OPTIONS,
-    default_value="import_only",
+    default_value=True,
     category="sync_options",
     depends_on=CONF_CONTENT_TYPE,
     depends_on_value="audiobooks",
index 62c95bdbae7dd5ff1c57b8c392b9f54d4ffcd68d..240a9a85d908bc5a1d85019f5b0ae92df3e1d545 100644 (file)
@@ -18,10 +18,10 @@ from music_assistant.providers.filesystem_local.constants import (
     CONF_ENTRY_CONTENT_TYPE,
     CONF_ENTRY_CONTENT_TYPE_READ_ONLY,
     CONF_ENTRY_IGNORE_ALBUM_PLAYLISTS,
-    CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS,
-    CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS,
-    CONF_ENTRY_LIBRARY_IMPORT_PODCASTS,
-    CONF_ENTRY_LIBRARY_IMPORT_TRACKS,
+    CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS,
+    CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS,
+    CONF_ENTRY_LIBRARY_SYNC_PODCASTS,
+    CONF_ENTRY_LIBRARY_SYNC_TRACKS,
     CONF_ENTRY_MISSING_ALBUM_ARTIST,
 )
 
@@ -127,10 +127,10 @@ async def get_config_entries(
         ),
         CONF_ENTRY_MISSING_ALBUM_ARTIST,
         CONF_ENTRY_IGNORE_ALBUM_PLAYLISTS,
-        CONF_ENTRY_LIBRARY_IMPORT_TRACKS,
-        CONF_ENTRY_LIBRARY_IMPORT_PLAYLISTS,
-        CONF_ENTRY_LIBRARY_IMPORT_PODCASTS,
-        CONF_ENTRY_LIBRARY_IMPORT_AUDIOBOOKS,
+        CONF_ENTRY_LIBRARY_SYNC_TRACKS,
+        CONF_ENTRY_LIBRARY_SYNC_PLAYLISTS,
+        CONF_ENTRY_LIBRARY_SYNC_PODCASTS,
+        CONF_ENTRY_LIBRARY_SYNC_AUDIOBOOKS,
     )
 
     if instance_id is None or values is None:
index 4d2cca683c36967bed42a55a485be55aa8522cbb..44d9bd3034a36b9ef433eb46977e0cd26c279f3e 100644 (file)
@@ -31,9 +31,8 @@ from music_assistant_models.streamdetails import StreamDetails
 from radios import FilterBy, Order, RadioBrowser, RadioBrowserError, Station
 
 from music_assistant.constants import (
-    CONF_ENTRY_LIBRARY_EXPORT_ADD,
-    CONF_ENTRY_LIBRARY_EXPORT_REMOVE,
-    CONF_ENTRY_LIBRARY_IMPORT_RADIOS,
+    CONF_ENTRY_LIBRARY_SYNC_BACK,
+    CONF_ENTRY_LIBRARY_SYNC_RADIOS,
     CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS,
 )
 from music_assistant.controllers.cache import use_cache
@@ -55,9 +54,9 @@ if TYPE_CHECKING:
 
 CONF_STORED_RADIOS = "stored_radios"
 
-CONF_ENTRY_LIBRARY_IMPORT_RADIOS_HIDDEN = ConfigEntry.from_dict(
+CONF_ENTRY_LIBRARY_SYNC_RADIOS_HIDDEN = ConfigEntry.from_dict(
     {
-        **CONF_ENTRY_LIBRARY_IMPORT_RADIOS.to_dict(),
+        **CONF_ENTRY_LIBRARY_SYNC_RADIOS.to_dict(),
         "hidden": True,
         "default_value": "import_only",
     }
@@ -69,18 +68,11 @@ CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS_HIDDEN = ConfigEntry.from_dict(
         "default_value": 180,
     }
 )
-CONF_ENTRY_LIBRARY_EXPORT_ADD_HIDDEN = ConfigEntry.from_dict(
+CONF_ENTRY_LIBRARY_SYNC_BACK_HIDDEN = ConfigEntry.from_dict(
     {
-        **CONF_ENTRY_LIBRARY_EXPORT_ADD.to_dict(),
+        **CONF_ENTRY_LIBRARY_SYNC_BACK.to_dict(),
         "hidden": True,
-        "default_value": "export_library",
-    }
-)
-CONF_ENTRY_LIBRARY_EXPORT_REMOVE_HIDDEN = ConfigEntry.from_dict(
-    {
-        **CONF_ENTRY_LIBRARY_EXPORT_REMOVE.to_dict(),
-        "hidden": True,
-        "default_value": "export_library",
+        "default_value": True,
     }
 )
 
@@ -119,10 +111,9 @@ async def get_config_entries(
             hidden=True,
         ),
         # hide some of the default (dynamic) entries for library management
-        CONF_ENTRY_LIBRARY_IMPORT_RADIOS_HIDDEN,
+        CONF_ENTRY_LIBRARY_SYNC_RADIOS_HIDDEN,
         CONF_ENTRY_PROVIDER_SYNC_INTERVAL_RADIOS_HIDDEN,
-        CONF_ENTRY_LIBRARY_EXPORT_ADD_HIDDEN,
-        CONF_ENTRY_LIBRARY_EXPORT_REMOVE_HIDDEN,
+        CONF_ENTRY_LIBRARY_SYNC_BACK_HIDDEN,
     )
 
 
index cae0f78eeb0da4fc4da951814afeea95c356ac8c..7f40c5a42ed225d27f5e28607c40a5d7c3938db6 100644 (file)
@@ -25,7 +25,7 @@ dependencies = [
   "ifaddr==0.2.0",
   "mashumaro==3.16",
   "music-assistant-frontend==2.16.5",
-  "music-assistant-models==1.1.61",
+  "music-assistant-models==1.1.62",
   "mutagen==1.47.0",
   "orjson==3.11.3",
   "pillow==11.3.0",
index c0426365972d9eb771be0ad0a27a90cfa517f057..03d420b4db7a8b3a0bd7a603a31757dd726ca61a 100644 (file)
@@ -35,7 +35,7 @@ llvmlite==0.44.0
 lyricsgenius==3.7.2
 mashumaro==3.16
 music-assistant-frontend==2.16.5
-music-assistant-models==1.1.61
+music-assistant-models==1.1.62
 mutagen==1.47.0
 numpy==2.2.6
 orjson==3.11.3