Fix radio streams (#575)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 26 Mar 2023 01:22:27 +0000 (03:22 +0200)
committerGitHub <noreply@github.com>
Sun, 26 Mar 2023 01:22:27 +0000 (03:22 +0200)
* fix typo in didl

* fix for mpeg dash radio streams

music_assistant/server/helpers/didl_lite.py
music_assistant/server/providers/tunein/__init__.py
music_assistant/server/providers/url/__init__.py

index d5be2943f3f88e4f8d6d6a285e408da7228b4739..833a2e14bd0007ddc0adf12a7fb0295e1c425811 100644 (file)
@@ -34,7 +34,7 @@ def create_didl_metadata(url: str, queue_item: QueueItem, flow_mode: bool = Fals
 
     if is_radio:
         # radio or other non-track item
-        image = queue_item.image_url if queue_item.image else ""
+        image = queue_item.image_url if queue_item.image_url else ""
         return (
             '<DIDL-Lite xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/" xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dlna="urn:schemas-dlna-org:metadata-1-0/">'
             f'<item id="{queue_item.queue_item_id}" parentID="0" restricted="1">'
index 89c6a163cc53226309937226f78a596b7433e8b0..7f9864b86207c81ae3407ce656e79b16eff2eb38 100644 (file)
@@ -201,9 +201,13 @@ class TuneInProvider(MusicProvider):
                 continue
             # check if the radio stream is not a playlist
             url = stream["url"]
-            if url.endswith("m3u8") or url.endswith("m3u") or url.endswith("pls"):
-                playlist = await fetch_playlist(self.mass, url)
-                url = playlist[0]
+            direct = None
+            if url.endswith("m3u8") or url.endswith("m3u") or url.endswith("pls"):  # noqa: SIM102
+                if playlist := await fetch_playlist(self.mass, url):
+                    if len(playlist) > 1 or ".m3u" in playlist[0] or ".pls" in playlist[0]:
+                        # this is most likely an mpeg-dash stream, let ffmpeg handle that
+                        direct = playlist[0]
+                    url = playlist[0]
             return StreamDetails(
                 provider=self.domain,
                 item_id=item_id,
@@ -211,6 +215,7 @@ class TuneInProvider(MusicProvider):
                 media_type=MediaType.RADIO,
                 data=url,
                 expires=time() + 24 * 3600,
+                url=direct,
             )
         raise MediaNotFoundError(f"Unable to retrieve stream details for {item_id}")
 
index c3f2a36c533e2238970a86b24477943a5b8ffec0..b319269c1fef2e889a03e0d37e4324a670721ea0 100644 (file)
@@ -53,6 +53,7 @@ class URLProvider(MusicProvider):
         Called when provider is registered.
         """
         self._full_url = {}
+        # self.mass.register_api_command("music/tracks", self.db_items)
 
     async def get_track(self, prov_track_id: str) -> Track:
         """Get full track details by id."""
@@ -60,7 +61,7 @@ class URLProvider(MusicProvider):
 
     async def get_radio(self, prov_radio_id: str) -> Radio:
         """Get full radio details by id."""
-        return await self.parse_item(prov_radio_id)
+        return await self.parse_item(prov_radio_id, force_radio=True)
 
     async def get_artist(self, prov_artist_id: str) -> Track:
         """Get full artist details by id."""
@@ -87,11 +88,13 @@ class URLProvider(MusicProvider):
             return await self.parse_item(prov_item_id)
         raise NotImplementedError
 
-    async def parse_item(self, item_id_or_url: str, force_refresh: bool = False) -> Track | Radio:
+    async def parse_item(
+        self, item_id_or_url: str, force_refresh: bool = False, force_radio: bool = False
+    ) -> Track | Radio:
         """Parse plain URL to MediaItem of type Radio or Track."""
         item_id, url, media_info = await self._get_media_info(item_id_or_url, force_refresh)
         is_radio = media_info.get("icy-name") or not media_info.duration
-        if is_radio:
+        if is_radio or force_radio:
             # treat as radio
             media_item = Radio(
                 item_id=item_id,
@@ -163,6 +166,8 @@ class URLProvider(MusicProvider):
         """Get streamdetails for a track/radio."""
         item_id, url, media_info = await self._get_media_info(item_id)
         is_radio = media_info.get("icy-name") or not media_info.duration
+        # we let ffmpeg handle with mpeg dash streams
+        mpeg_dash_stream = ".m3u" in url or ".pls" in url
         return StreamDetails(
             provider=self.instance_id,
             item_id=item_id,
@@ -170,7 +175,7 @@ class URLProvider(MusicProvider):
             media_type=MediaType.RADIO if is_radio else MediaType.TRACK,
             sample_rate=media_info.sample_rate,
             bit_depth=media_info.bits_per_sample,
-            direct=None if is_radio else url,
+            direct=None if is_radio and not mpeg_dash_stream else url,
             data=url,
         )