"""Return the features supported by this Provider."""
return ()
- async def handle_setup(self) -> None:
- """Handle async initialization of the provider."""
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
async def unload(self) -> None:
"""
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = AirplayProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
"""Return the features supported by this Provider."""
return (ProviderFeature.SYNC_PLAYERS,)
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self._atv_players = {}
self._stream_tasks = {}
self._cliraop_bin = await self.get_cliraop_binary()
- self.mass.create_task(self._run_discovery())
dacp_port = await select_free_port(39831, 49831)
# the pyatv logger is way to noisy, silence it a bit
logging.getLogger("pyatv").setLevel(self.logger.level + 10)
)
await self.mass.zeroconf.async_register_service(self._dacp_info)
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
+ await self._run_discovery()
+
async def unload(self) -> None:
"""Handle close/cleanup of the provider."""
# power off all players (will disconnct and close cliraop)
mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
- prov = ChromecastProvider(mass, manifest, config)
- await prov.handle_setup()
- return prov
+ return ChromecastProvider(mass, manifest, config)
async def get_config_entries(
castplayers: dict[str, CastPlayer]
_discover_lock: threading.Lock
- async def handle_setup(self) -> None:
+ def __init__(
+ self, mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
+ ) -> None:
"""Handle async initialization of the provider."""
+ super.__init__(mass, manifest, config)
self._discover_lock = threading.Lock()
self.castplayers = {}
self.mz_mgr = MultizoneManager()
)
# silence pychromecast logging
logging.getLogger("pychromecast").setLevel(self.logger.level + 10)
+
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
# start discovery in executor
await self.mass.loop.run_in_executor(None, self.browser.start_discovery)
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = DeezerProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
credentials: DeezerCredentials
user: deezer.User
- async def handle_setup(self) -> None:
- """Set up the Deezer provider."""
+ async def handle_async_init(self) -> None:
+ """Handle async init of the Deezer provider."""
self.credentials = DeezerCredentials(
app_id=DEEZER_APP_ID,
app_secret=DEEZER_APP_SECRET,
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = DLNAPlayerProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
upnp_factory: UpnpFactory
notify_server: DLNANotifyServer
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self.dlnaplayers = {}
self.lock = asyncio.Lock()
self.requester = AiohttpSessionRequester(self.mass.http_session, with_sleep=True)
self.upnp_factory = UpnpFactory(self.requester, non_strict=True)
self.notify_server = DLNANotifyServer(self.requester, self.mass)
- self.mass.create_task(self._run_discovery())
+
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
+ await self._run_discovery()
async def unload(self) -> None:
"""
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = FanartTvMetadataProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
throttler: Throttler
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self.cache = self.mass.cache
self.throttler = Throttler(rate_limit=2, period=1)
msg = f"Music Directory {conf_path} does not exist"
raise SetupFailedError(msg)
prov = LocalFileSystemProvider(mass, manifest, config)
- await prov.handle_setup()
+ prov.base_path = config.get_value(CONF_PATH)
return prov
base_path: str
- async def handle_setup(self) -> None:
- """Handle async initialization of the provider."""
- self.base_path = self.config.get_value(CONF_PATH)
-
async def listdir(
self, path: str, recursive: bool = False
) -> AsyncGenerator[FileSystemItem, None]:
import cchardet
import xmltodict
-from music_assistant.common.helpers.util import (
- create_sort_name,
- parse_title_and_version,
-)
+from music_assistant.common.helpers.util import create_sort_name, parse_title_and_version
from music_assistant.common.models.config_entries import (
ConfigEntry,
ConfigEntryType,
return SUPPORTED_FEATURES
@abstractmethod
- async def handle_setup(self) -> None:
+ async def async_setup(self) -> None:
"""Handle async initialization of the provider."""
@abstractmethod
msg = "Invalid share name"
raise LoginFailed(msg)
prov = SMBFileSystemProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
smb library for Python (and we tried both pysmb and smbprotocol).
"""
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
# base_path will be the path where we're going to mount the remote share
self.base_path = f"/tmp/{self.instance_id}" # noqa: S108
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = FullyKioskProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_fully: FullyKiosk
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self._fully = FullyKiosk(
self.mass.http_session,
try:
async with asyncio.timeout(15):
await self._fully.getDeviceInfo()
- self._handle_player_init()
- self._handle_player_update()
except Exception as err:
msg = f"Unable to start the FullyKiosk connection ({err!s}"
raise SetupFailedError(msg) from err
- def _handle_player_init(self) -> None:
- """Process FullyKiosk add to Player controller."""
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
+ # Add FullyKiosk device to Player controller.
player_id = self._fully.deviceInfo["deviceID"]
player = self.mass.players.get(player_id, raise_unavailable=False)
address = (
supported_features=(PlayerFeature.VOLUME_SET,),
)
self.mass.players.register_or_update(player)
+ self._handle_player_update()
def _handle_player_update(self) -> None:
"""Update FullyKiosk player attributes."""
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = HomeAssistant(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
hass: HomeAssistantClient
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the plugin."""
url = get_websocket_url(self.config.get_value(CONF_URL))
token = self.config.get_value(CONF_AUTH_TOKEN)
- logging.getLogger("hass_client").setLevel(self.logger.level)
+ logging.getLogger("hass_client").setLevel(self.logger.level + 10)
self.hass = HomeAssistantClient(url, token, self.mass.http_session)
await self.hass.connect()
mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
+ hass_prov: HomeAssistantProvider = mass.get_provider(HASS_DOMAIN)
+ if not hass_prov:
+ msg = "The Home Assistant Plugin needs to be set-up first"
+ raise SetupFailedError(msg)
prov = HomeAssistantPlayers(mass, manifest, config)
- await prov.handle_setup()
+ prov.hass_prov = hass_prov
return prov
hass_prov: HomeAssistantProvider
- async def handle_setup(self) -> None:
- """Handle async initialization of the plugin."""
- hass_prov: HomeAssistantProvider = self.mass.get_provider(HASS_DOMAIN)
- if not hass_prov:
- msg = "The Home Assistant Plugin needs to be set-up first"
- raise SetupFailedError(msg)
- self.hass_prov = hass_prov
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
player_ids: list[str] = self.config.get_value(CONF_PLAYERS)
# prefetch the device- and entity registry
- device_registry = {x["id"]: x for x in await hass_prov.hass.get_device_registry()}
- entity_registry = {x["entity_id"]: x for x in await hass_prov.hass.get_entity_registry()}
+ device_registry = {x["id"]: x for x in await self.hass_prov.hass.get_device_registry()}
+ entity_registry = {
+ x["entity_id"]: x for x in await self.hass_prov.hass.get_entity_registry()
+ }
# setup players from hass entities
- async for state in _get_hass_media_players(hass_prov):
+ async for state in _get_hass_media_players(self.hass_prov):
if state["entity_id"] not in player_ids:
continue
await self._setup_player(state, entity_registry, device_registry)
# register for entity state updates
- await hass_prov.hass.subscribe_entities(self._on_entity_state_update, player_ids)
+ await self.hass_prov.hass.subscribe_entities(self._on_entity_state_update, player_ids)
# remove any leftover players (after reconfigure of players)
for player in self.players:
if player.player_id not in player_ids:
) -> ProviderInstanceType:\r
"""Initialize provider(instance) with given configuration."""\r
prov = JellyfinProvider(mass, manifest, config)\r
- await prov.handle_setup()\r
+ await prov.handle_async_init()\r
return prov\r
\r
\r
\r
# _jellyfin_server : JellyfinClient = None\r
\r
- async def handle_setup(self) -> None:\r
+ async def handle_async_init(self) -> None:\r
"""Initialize provider(instance) with given configuration."""\r
logging.getLogger("pytube").setLevel(self.logger.level + 10)\r
logging.getLogger("ytmusicapi").setLevel(self.logger.level + 10)\r
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = MusicbrainzProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
throttler: Throttler
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self.cache = self.mass.cache
self.throttler = Throttler(rate_limit=1, period=1)
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = OpenSonicProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_conn: SonicConnection = None
_enable_podcasts: bool = True
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Set up the music provider and test the connection."""
logging.getLogger("libopensonic").setLevel(self.logger.level)
port = self.config.get_value(CONF_PORT)
raise LoginFailed(msg)
prov = PlexProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_plex_library: PlexMusicSection = None
_myplex_account: MyPlexAccount = None
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Set up the music provider by connecting to the server."""
# silence loggers
logging.getLogger("plexapi").setLevel(self.logger.level + 10)
from music_assistant.common.helpers.util import parse_title_and_version, try_parse_int
from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType
-from music_assistant.common.models.enums import (
- ConfigEntryType,
- ExternalID,
- ProviderFeature,
-)
+from music_assistant.common.models.enums import ConfigEntryType, ExternalID, ProviderFeature
from music_assistant.common.models.errors import LoginFailed, MediaNotFoundError
from music_assistant.common.models.media_items import (
Album,
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = QobuzProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_user_auth_info: str | None = None
_throttler: Throttler
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self._throttler = Throttler(rate_limit=4, period=1)
"""Initialize provider(instance) with given configuration."""
prov = RadioBrowserProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
"""Return the features supported by this Provider."""
return SUPPORTED_FEATURES
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self.radios = RadioBrowser(
session=self.mass.http_session, user_agent=f"MusicAssistant/{self.mass.version}"
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = SlimprotoProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
"""Return the features supported by this Provider."""
return (ProviderFeature.SYNC_PLAYERS,)
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self._socket_clients = {}
self._sync_playpoints = {}
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = SnapCastProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
"""Return the features supported by this Provider."""
return (ProviderFeature.SYNC_PLAYERS,)
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self.snapcast_server_host = self.config.get_value(CONF_SNAPCAST_SERVER_HOST)
self.snapcast_server_control_port = self.config.get_value(CONF_SNAPCAST_SERVER_CONTROL_PORT)
reconnect=True,
)
self._snapserver.set_on_update_callback(self._handle_update)
- self._handle_update()
self.logger.info(
f"Started Snapserver connection on:"
f"{self.snapcast_server_host}:{self.snapcast_server_control_port}"
msg = "Unable to start the Snapserver connection ?"
raise SetupFailedError(msg) from err
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
+ # initial load of players
+ self._handle_update()
+
+ async def unload(self) -> None:
+ """Handle close/cleanup of the provider."""
+ for client in self._snapserver.clients:
+ await self.cmd_stop(client.identifier)
+ await self._snapserver.stop()
+
def _handle_update(self) -> None:
"""Process Snapcast init Player/Group and set callback ."""
for snap_client in self._snapserver.clients:
player.active_source = stream.name
self.mass.players.register_or_update(player)
- async def unload(self) -> None:
- """Handle close/cleanup of the provider."""
- for client in self._snapserver.clients:
- await self.cmd_stop(client.identifier)
- await self._snapserver.stop()
-
async def get_player_config_entries(self, player_id: str) -> tuple[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
base_entries = await super().get_player_config_entries(player_id)
logging.getLogger("soco").setLevel(logging.INFO)
logging.getLogger("urllib3.connectionpool").setLevel(logging.INFO)
prov = SonosPlayerProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
"""Return the features supported by this Provider."""
return (ProviderFeature.SYNC_PLAYERS,)
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self.sonosplayers: OrderedDict[str, SonosPlayer] = OrderedDict()
self.topology_condition = asyncio.Condition()
self.creation_lock = asyncio.Lock()
self._known_invisible: set[SoCo] = set()
- self.mass.create_task(self._run_discovery())
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
+ await self._run_discovery()
async def unload(self) -> None:
"""Handle close/cleanup of the provider."""
msg = "Invalid login credentials"
raise LoginFailed(msg)
prov = SoundcloudMusicProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_soundcloud = None
_me = None
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Set up the Soundcloud provider."""
client_id = self.config.get_value(CONF_CLIENT_ID)
auth_token = self.config.get_value(CONF_AUTHORIZATION)
from music_assistant.common.helpers.util import parse_title_and_version
from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType
-from music_assistant.common.models.enums import (
- ConfigEntryType,
- ExternalID,
- ProviderFeature,
-)
+from music_assistant.common.models.enums import ConfigEntryType, ExternalID, ProviderFeature
from music_assistant.common.models.errors import LoginFailed, MediaNotFoundError
from music_assistant.common.models.media_items import (
Album,
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = SpotifyProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_sp_user: str | None = None
_librespot_bin: str | None = None
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self._throttler = Throttler(rate_limit=1, period=0.1)
self._cache_dir = CACHE_DIR
self._ap_workaround = False
-
# 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
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = AudioDbMetadataProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
throttler: Throttler
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self.cache = self.mass.cache
self.throttler = Throttler(rate_limit=2, period=1)
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = TidalProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_tidal_session: TidalSession | None = None
_tidal_user_id: str | None = None
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self._tidal_user_id: str = self.config.get_value(CONF_USER_ID)
self._tidal_session = await self._get_tidal_session()
from music_assistant.common.helpers.util import create_sort_name
from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType
from music_assistant.common.models.enums import ConfigEntryType, ProviderFeature
-from music_assistant.common.models.errors import (
- InvalidDataError,
- LoginFailed,
- MediaNotFoundError,
-)
+from music_assistant.common.models.errors import InvalidDataError, LoginFailed, MediaNotFoundError
from music_assistant.common.models.media_items import (
AudioFormat,
ContentType,
"Email address detected instead of username, "
"it is advised to use the tunein username instead of email."
)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
"""Return the features supported by this Provider."""
return SUPPORTED_FEATURES
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Handle async initialization of the provider."""
self._throttler = Throttler(rate_limit=1, period=1)
ProviderFeature,
)
from music_assistant.common.models.player import DeviceInfo, Player
-from music_assistant.constants import (
- CONF_CROSSFADE,
- CONF_GROUP_MEMBERS,
- SYNCGROUP_PREFIX,
-)
+from music_assistant.constants import CONF_CROSSFADE, CONF_GROUP_MEMBERS, SYNCGROUP_PREFIX
from music_assistant.server.models.player_provider import PlayerProvider
if TYPE_CHECKING:
mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
- prov = UniversalGroupProvider(mass, manifest, config)
- await prov.handle_setup()
- return prov
+ return UniversalGroupProvider(mass, manifest, config)
async def get_config_entries(
"""Return the features supported by this Provider."""
return (ProviderFeature.PLAYER_GROUP_CREATE,)
- async def handle_setup(self) -> None:
- """Handle async initialization of the provider."""
+ def __init__(
+ self, mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
+ ) -> None:
+ """Initialize MusicProvider."""
+ super().__init__(mass, manifest, config)
self.prev_sync_leaders = {}
- self.mass.loop.create_task(self._register_all_players())
+
+ async def loaded_in_mass(self) -> None:
+ """Call after the provider has been loaded."""
+ await self._register_all_players()
async def get_player_config_entries(self, player_id: str) -> tuple[ConfigEntry]:
"""Return all (provider/player specific) Config Entries for the given player (if any)."""
mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
- prov = URLProvider(mass, manifest, config)
- await prov.handle_setup()
- return prov
+ return URLProvider(mass, manifest, config)
async def get_config_entries(
class URLProvider(MusicProvider):
"""Music Provider for manual URL's/files added to the queue."""
- async def handle_setup(self) -> None:
- """Handle async initialization of the provider.
-
- Called when provider is registered.
- """
+ def __init__(
+ self, mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
+ ) -> None:
+ """Initialize MusicProvider."""
+ super().__init__(mass, manifest, config)
self._full_url = {}
async def get_track(self, prov_track_id: str) -> Track:
) -> ProviderInstanceType:
"""Initialize provider(instance) with given configuration."""
prov = YoutubeMusicProvider(mass, manifest, config)
- await prov.handle_setup()
+ await prov.handle_async_init()
return prov
_signature_timestamp = 0
_cipher = None
- async def handle_setup(self) -> None:
+ async def handle_async_init(self) -> None:
"""Set up the YTMusic provider."""
logging.getLogger("pytube").setLevel(self.logger.level + 10)
if not self.config.get_value(CONF_AUTH_TOKEN):
)
raise SetupFailedError(msg)
- # try to load the module
+ # try to setup the module
prov_mod = await get_provider_module(domain)
try:
async with asyncio.timeout(30):
)
provider.available = True
self._providers[provider.instance_id] = provider
+ self.create_task(provider.loaded_in_mass())
self.config.set(f"{CONF_PROVIDERS}/{conf.instance_id}/last_error", None)
self.signal_event(EventType.PROVIDERS_UPDATED, data=self.get_providers())
# if this is a music provider, start sync