return [
Track.from_dict(item)
for item in await self.client.send_command(
- "music/album/tracks",
+ "music/album/album_tracks",
item_id=item_id,
provider_instance_id_or_domain=provider_instance_id_or_domain,
)
changed_keys.add(key)
# config entry values
- for key, new_val in update.items():
+ values = update.get("values", update)
+ for key, new_val in values.items():
if key in root_values:
continue
cur_val = self.values[key].value if key in self.values else None
builtin: bool = False
# hidden: hide entry in the UI
hidden: bool = False
- # load_by_default: load this provider by default (mostly used together with `builtin`)
+ # load_by_default: load this provider by default (may be used together with `builtin`)
load_by_default: bool = False
# depends_on: depends on another provider to function
depends_on: str | None = None
existing = self.get(conf_key)
if not existing:
raise KeyError(f"Provider {instance_id} does not exist")
+ prov_manifest = self.mass.get_provider_manifest(existing["domain"])
+ if prov_manifest.load_by_default and instance_id == prov_manifest.domain:
+ # Guard for a provider that is loaded by default
+ LOGGER.warning(
+ "Provider %s can not be removed, disabling instead...", prov_manifest.name
+ )
+ existing["enabled"] = False
+ await self._update_provider_config(instance_id, existing)
+ return
+ if prov_manifest.builtin:
+ raise RuntimeError(f"Builtin provider {prov_manifest.name} can not be removed.")
self.remove(conf_key)
await self.mass.unload_provider(instance_id)
if existing["type"] == "music":
# cleanup entries in library
await self.mass.music.cleanup_provider(instance_id)
+ if existing["type"] == "player":
+ # cleanup entries in player manager
+ for player in self.mass.players:
+ if player.provider != instance_id:
+ continue
+ self.mass.players.remove(player.player_id, cleanup_config=True)
async def remove_provider_config_value(self, instance_id: str, key: str) -> None:
"""Remove/reset single Provider config value."""
await self._load_provider_config(config)
else:
# disable provider
+ prov_manifest = self.mass.get_provider_manifest(config.domain)
+ if prov_manifest.builtin:
+ raise RuntimeError("Builtin provider can not be disabled.")
# also unload any other providers dependent of this provider
for dep_prov in self.mass.providers:
if dep_prov.manifest.depends_on == config.domain:
await self.mass.unload_provider(dep_prov.instance_id)
await self.mass.unload_provider(config.instance_id)
+ if config.type == ProviderType.PLAYER:
+ # cleanup entries in player manager
+ for player in self.mass.players.all(
+ return_unavailable=True, return_hidden=True, return_disabled=True
+ ):
+ if player.provider != instance_id:
+ continue
+ self.mass.players.remove(player.player_id, cleanup_config=False)
# load succeeded, save new config
config.last_error = None
conf_key = f"{CONF_PROVIDERS}/{instance_id}"
domain: str = "music"
database: DatabaseConnection | None = None
+ config: CoreConfig
def __init__(self, *args, **kwargs) -> None:
"""Initialize class."""
async def setup(self, config: CoreConfig) -> None:
"""Async initialize of module."""
+ self.config = config
# setup library database
await self._setup_database()
sync_interval = config.get_value(CONF_SYNC_INTERVAL)
- self.logger.info("Setting up the sync interval to %s minutes.", sync_interval)
- self._sync_task = self.mass.create_task(self.start_sync(reschedule=sync_interval))
+ self.logger.info("Using a sync interval of %s minutes.", sync_interval)
+ self._schedule_sync()
async def close(self) -> None:
"""Cleanup on exit."""
return self.mass.get_providers(ProviderType.MUSIC)
@api_command("music/sync")
- async def start_sync(
+ def start_sync(
self,
media_types: list[MediaType] | None = None,
providers: list[str] | None = None,
- reschedule: int | None = None,
) -> None:
"""Start running the sync of (all or selected) musicproviders.
continue
self._start_provider_sync(provider.instance_id, media_types)
- # reschedule task if needed
- def create_sync_task():
- self.mass.create_task(self.start_sync, media_types, providers, reschedule)
-
- if reschedule is not None:
- self.mass.loop.call_later(reschedule, create_sync_task)
-
@api_command("music/synctasks")
def get_running_sync_tasks(self) -> list[SyncTask]:
"""Return list with providers that are currently (scheduled for) syncing."""
for item in prov_items:
await ctrl.remove_provider_mappings(item.item_id, provider_instance)
+ def _schedule_sync(self) -> None:
+ """Schedule the periodic sync."""
+ self.start_sync()
+ sync_interval = self.config.get_value(CONF_SYNC_INTERVAL)
+ # we reschedule ourselves right after execution
+ # NOTE: sync_interval is stored in minutes, we need seconds
+ self.mass.loop.call_later(sync_interval * 60, self._schedule_sync)
+
async def _setup_database(self):
"""Initialize database."""
db_path = os.path.join(self.mass.storage_path, "library.db")
self.register(player)
@api_command("players/remove")
- def remove(self, player_id: str) -> None:
+ def remove(self, player_id: str, cleanup_config: bool = True) -> None:
"""Remove a player from the registry."""
player = self._players.pop(player_id, None)
if player is None:
return
LOGGER.info("Player removed: %s", player.name)
self.mass.player_queues.on_player_remove(player_id)
- self.mass.config.remove(f"players/{player_id}")
+ if cleanup_config:
+ self.mass.config.remove(f"players/{player_id}")
self._prev_states.pop(player_id, None)
self.mass.signal_event(EventType.PLAYER_REMOVED, player_id)
"""Return all Provider manifests."""
return list(self._provider_manifests.values())
+ @api_command("providers/manifests/get")
+ def get_provider_manifest(self, domain: str) -> ProviderManifest:
+ """Return Provider manifests of single provider(domain)."""
+ return self._provider_manifests[domain]
+
@api_command("providers")
def get_providers(
self, provider_type: ProviderType | None = None
self.signal_event(EventType.PROVIDERS_UPDATED, data=self.get_providers())
# if this is a music provider, start sync
if provider.type == ProviderType.MUSIC:
- await self.music.start_sync(providers=[provider.instance_id])
+ self.music.start_sync(providers=[provider.instance_id])
async def unload_provider(self, instance_id: str) -> None:
"""Unload a provider."""