From 288c25f216d3189fdcfa1c2dac5cb66dc507b974 Mon Sep 17 00:00:00 2001 From: Giel Janssens Date: Wed, 19 Apr 2023 18:34:04 +0200 Subject: [PATCH] Add Radio Browser music provider (#634) --- .../server/providers/radiobrowser/__init__.py | 151 ++++++++++++++++++ .../server/providers/radiobrowser/icon.png | Bin 0 -> 36674 bytes .../providers/radiobrowser/manifest.json | 10 ++ requirements_all.txt | 1 + 4 files changed, 162 insertions(+) create mode 100644 music_assistant/server/providers/radiobrowser/__init__.py create mode 100644 music_assistant/server/providers/radiobrowser/icon.png create mode 100644 music_assistant/server/providers/radiobrowser/manifest.json diff --git a/music_assistant/server/providers/radiobrowser/__init__.py b/music_assistant/server/providers/radiobrowser/__init__.py new file mode 100644 index 00000000..8c33fe0c --- /dev/null +++ b/music_assistant/server/providers/radiobrowser/__init__.py @@ -0,0 +1,151 @@ +"""RadioBrowser musicprovider support for MusicAssistant.""" +from __future__ import annotations + +from collections.abc import AsyncGenerator +from time import time +from typing import TYPE_CHECKING + +from radios import RadioBrowser, RadioBrowserError + +from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType +from music_assistant.common.models.enums import LinkType, ProviderFeature +from music_assistant.common.models.media_items import ( + ContentType, + ImageType, + MediaItemImage, + MediaItemLink, + MediaType, + ProviderMapping, + Radio, + SearchResults, + StreamDetails, +) +from music_assistant.constants import __version__ as MASS_VERSION # noqa: N812 +from music_assistant.server.helpers.audio import get_radio_stream +from music_assistant.server.models.music_provider import MusicProvider + +SUPPORTED_FEATURES = (ProviderFeature.SEARCH,) + +if TYPE_CHECKING: + from music_assistant.common.models.config_entries import ProviderConfig + from music_assistant.common.models.provider import ProviderManifest + from music_assistant.server import MusicAssistant + from music_assistant.server.models import ProviderInstanceType + + +async def setup( + mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig +) -> ProviderInstanceType: + """Initialize provider(instance) with given configuration.""" + prov = RadioBrowserProvider(mass, manifest, config) + + await prov.handle_setup() + return prov + + +async def get_config_entries( + mass: MusicAssistant, + instance_id: str | None = None, + action: str | None = None, + values: dict[str, ConfigValueType] | None = None, +) -> tuple[ConfigEntry, ...]: + """ + Return Config entries to setup this provider. + instance_id: id of an existing provider instance (None if new instance setup). + action: [optional] action key called from config entries UI. + values: the (intermediate) raw values for config entries sent with the action. + """ + # ruff: noqa: ARG001 D205 + return tuple() # we do not have any config entries (yet) + + +class RadioBrowserProvider(MusicProvider): + """Provider implementation for RadioBrowser.""" + + @property + def supported_features(self) -> tuple[ProviderFeature, ...]: + """Return the features supported by this Provider.""" + return SUPPORTED_FEATURES + + async def handle_setup(self) -> None: + """Handle async initialization of the provider.""" + self.radios = RadioBrowser( + session=self.mass.http_session, user_agent=f"MusicAssistant/{MASS_VERSION}" + ) + try: + # Try to get some stats to check connection to RadioBrowser API + await self.radios.stats() + except RadioBrowserError as err: + self.logger.error("%s", err) + + async def search( + self, search_query: str, media_types=list[MediaType] | None, limit: int = 10 + ) -> SearchResults: + """Perform search on musicprovider. + + :param search_query: Search query. + :param media_types: A list of media_types to include. All types if None. + :param limit: Number of items to return in the search (per type). + """ + result = SearchResults() + searchtypes = [] + if MediaType.RADIO in media_types: + searchtypes.append("radio") + + time_start = time() + + searchresult = await self.radios.search(name=search_query, limit=limit) + + self.logger.debug( + "Processing RadioBrowser search took %s seconds", + round(time() - time_start, 2), + ) + for item in searchresult: + result.radio.append(await self._parse_radio(item)) + + return result + + async def get_radio(self, prov_radio_id: str) -> Radio: + """Get radio station details.""" + radio = await self.radios.station(uuid=prov_radio_id) + return await self._parse_radio(radio) + + async def _parse_radio(self, radio_obj: dict) -> Radio: + """Parse Radio object from json obj returned from api.""" + radio = Radio(item_id=radio_obj.uuid, provider=self.domain, name=radio_obj.name) + radio.add_provider_mapping( + ProviderMapping( + item_id=radio_obj.uuid, + provider_domain=self.domain, + provider_instance=self.instance_id, + ) + ) + radio.metadata.label = radio_obj.tags + radio.metadata.popularity = radio_obj.votes + radio.metadata.links = [MediaItemLink(LinkType.WEBSITE, radio_obj.homepage)] + radio.metadata.images = [MediaItemImage(ImageType.THUMB, radio_obj.favicon)] + + return radio + + async def get_stream_details(self, item_id: str) -> StreamDetails: + """Get streamdetails for a radio station.""" + stream = await self.radios.station(uuid=item_id) + url = stream.url + url_resolved = stream.url_resolved + await self.radios.station_click(uuid=item_id) + return StreamDetails( + provider=self.domain, + item_id=item_id, + content_type=ContentType.try_parse(stream.codec), + media_type=MediaType.RADIO, + data=url, + expires=time() + 24 * 3600, + direct=url_resolved, + ) + + async def get_audio_stream( + self, streamdetails: StreamDetails, seek_position: int = 0 # noqa: ARG002 + ) -> AsyncGenerator[bytes, None]: + """Return the audio stream for the provider item.""" + async for chunk in get_radio_stream(self.mass, streamdetails.data, streamdetails): + yield chunk diff --git a/music_assistant/server/providers/radiobrowser/icon.png b/music_assistant/server/providers/radiobrowser/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..20672a23557e27a4b8ecd283cfec7539d13a7399 GIT binary patch literal 36674 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.1.18 +git+https://github.com/gieljnssns/python-radios.git@main git+https://github.com/jozefKruszynski/python-tidal.git@v0.7.1 git+https://github.com/pytube/pytube.git@refs/pull/1501/head mashumaro==3.7 -- 2.34.1