Search radio stations on tunein provider (#2204)
authorcociweb <cociweb@users.noreply.github.com>
Sat, 31 May 2025 12:33:48 +0000 (14:33 +0200)
committerGitHub <noreply@github.com>
Sat, 31 May 2025 12:33:48 +0000 (14:33 +0200)
music_assistant/providers/tunein/__init__.py

index c03a2ddfc33e4e6e5baa623cdb1f2744a7ad1adf..1e42229c4a048117a3ba245220dca47df307051a 100644 (file)
@@ -1,10 +1,14 @@
-"""Tune-In musicprovider support for MusicAssistant."""
+"""Tune-In music provider support for MusicAssistant."""
 
 from __future__ import annotations
 
 from typing import TYPE_CHECKING
+from urllib.parse import quote
 
-from music_assistant_models.config_entries import ConfigEntry, ConfigValueType
+from music_assistant_models.config_entries import (
+    ConfigEntry,
+    ConfigValueType,
+)
 from music_assistant_models.enums import (
     ConfigEntryType,
     ContentType,
@@ -12,13 +16,18 @@ from music_assistant_models.enums import (
     ProviderFeature,
     StreamType,
 )
-from music_assistant_models.errors import InvalidDataError, LoginFailed, MediaNotFoundError
+from music_assistant_models.errors import (
+    InvalidDataError,
+    LoginFailed,
+    MediaNotFoundError,
+)
 from music_assistant_models.media_items import (
     AudioFormat,
     MediaItemImage,
     MediaType,
     ProviderMapping,
     Radio,
+    SearchResults,
 )
 from music_assistant_models.streamdetails import StreamDetails
 
@@ -26,11 +35,6 @@ from music_assistant.constants import CONF_USERNAME
 from music_assistant.helpers.throttle_retry import Throttler
 from music_assistant.models.music_provider import MusicProvider
 
-SUPPORTED_FEATURES = {
-    ProviderFeature.LIBRARY_RADIOS,
-    ProviderFeature.BROWSE,
-}
-
 if TYPE_CHECKING:
     from collections.abc import AsyncGenerator
 
@@ -40,6 +44,12 @@ if TYPE_CHECKING:
     from music_assistant import MusicAssistant
     from music_assistant.models import ProviderInstanceType
 
+SUPPORTED_FEATURES = {
+    ProviderFeature.LIBRARY_RADIOS,
+    ProviderFeature.BROWSE,
+    ProviderFeature.SEARCH,
+}
+
 
 async def setup(
     mass: MusicAssistant, manifest: ProviderManifest, config: ProviderConfig
@@ -239,7 +249,7 @@ class TuneInProvider(MusicProvider):
         return result
 
     async def get_stream_details(self, item_id: str, media_type: MediaType) -> StreamDetails:
-        """Get streamdetails for a radio station."""
+        """Get stream details for a radio station."""
         if item_id.startswith("http"):
             # custom url
             return StreamDetails(
@@ -276,6 +286,37 @@ class TuneInProvider(MusicProvider):
         msg = f"Unable to retrieve stream details for {item_id}"
         raise MediaNotFoundError(msg)
 
+    async def search(
+        self, search_query: str, media_types: list[MediaType], limit: int = 10
+    ) -> SearchResults:
+        """Perform search on Tune-in music provider."""
+        result = SearchResults()
+        if MediaType.RADIO not in media_types:
+            return result
+        params = {
+            "query": quote(search_query),
+            "formats": "ogg,aac,wma,mp3,hls",
+            "username": self.config.get_value(CONF_USERNAME),
+            "partnerId": "1",
+            "render": "json",
+        }
+        data = await self.__get_data("search.ashx", **params)
+        radios = []
+        if data and "body" in data:
+            count = 0
+            for item in data["body"]:
+                if item.get("type") == "audio" and "preset_id" in item:
+                    try:
+                        stream_info = await self._get_stream_info(item["preset_id"])
+                        radios.append(self._parse_radio(item, stream_info))
+                        count += 1
+                        if count >= limit:
+                            break
+                    except Exception as err:
+                        self.logger.debug("Failed to parse radio: %s", err)
+        result.radio = radios
+        return result
+
     async def __get_data(self, endpoint: str, **kwargs):
         """Get data from api."""
         if endpoint.startswith("http"):