Fix some bugs caused by regression (#1446)
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 5 Jul 2024 07:54:49 +0000 (09:54 +0200)
committerGitHub <noreply@github.com>
Fri, 5 Jul 2024 07:54:49 +0000 (09:54 +0200)
music_assistant/server/controllers/player_queues.py
music_assistant/server/helpers/audio.py
music_assistant/server/helpers/process.py
music_assistant/server/helpers/util.py
music_assistant/server/providers/airplay/__init__.py
music_assistant/server/providers/filesystem_smb/__init__.py
music_assistant/server/providers/radiobrowser/__init__.py
music_assistant/server/providers/snapcast/__init__.py
music_assistant/server/providers/spotify/__init__.py

index 384a51ba943f9387730725aac9cc3312dc4e6d82..c29eef642af183dd99b216aa58ece3f783289b1f 100644 (file)
@@ -660,18 +660,9 @@ class PlayerQueuesController(CoreController):
         queue = self._queues[queue_id]
         queue_items = self._queue_items[queue_id]
         resume_item = queue.current_item
-        next_item = queue.next_item
         resume_pos = queue.elapsed_time
-        if (
-            resume_item
-            and next_item
-            and resume_item.duration
-            and resume_pos > (resume_item.duration * 0.9)
-        ):
-            # track is already played for > 90% - skip to next
-            resume_item = next_item
-            resume_pos = 0
-        elif not resume_item and queue.current_index is not None and len(queue_items) > 0:
+
+        if not resume_item and queue.current_index is not None and len(queue_items) > 0:
             resume_item = self.get_item(queue_id, queue.current_index)
             resume_pos = 0
         elif not resume_item and queue.current_index is None and len(queue_items) > 0:
index 721c544702ef0013b701ac9ac9eca7f8393d80e2..61821d99257b9e5e74d0431b0d549a6e8669403b 100644 (file)
@@ -819,7 +819,7 @@ async def get_ffmpeg_stream(
 async def check_audio_support() -> tuple[bool, bool, str]:
     """Check if ffmpeg is present (with/without libsoxr support)."""
     # check for FFmpeg presence
-    returncode, output = await check_output(["ffmpeg", "-version"])
+    returncode, output = await check_output("ffmpeg", "-version")
     ffmpeg_present = returncode == 0 and "FFmpeg" in output.decode()
 
     # use globals as in-memory cache
index 73a010400ace412dc35b420f008d58dd5b6ddb3c..492bca619ef11f6a5f3b176afb692dd017a9bdac 100644 (file)
@@ -257,42 +257,27 @@ class AsyncProcess:
         return self._returncode
 
 
-async def check_output(args: str | list[str]) -> tuple[int, bytes]:
+async def check_output(*args: str) -> tuple[int, bytes]:
     """Run subprocess and return returncode and output."""
-    if isinstance(args, str):
-        proc = await asyncio.create_subprocess_shell(
-            args,
-            stderr=asyncio.subprocess.STDOUT,
-            stdout=asyncio.subprocess.PIPE,
-        )
-    else:
-        proc = await asyncio.create_subprocess_exec(
-            *args,
-            stderr=asyncio.subprocess.STDOUT,
-            stdout=asyncio.subprocess.PIPE,
-        )
+    proc = await asyncio.create_subprocess_exec(
+        *args,
+        stderr=asyncio.subprocess.STDOUT,
+        stdout=asyncio.subprocess.PIPE,
+    )
     stdout, _ = await proc.communicate()
     return (proc.returncode, stdout)
 
 
 async def communicate(
-    args: str | list[str],
+    args: list[str],
     input: bytes | None = None,  # noqa: A002
 ) -> tuple[int, bytes, bytes]:
     """Communicate with subprocess and return returncode, stdout and stderr output."""
-    if isinstance(args, str):
-        proc = await asyncio.create_subprocess_shell(
-            args,
-            stderr=asyncio.subprocess.PIPE,
-            stdout=asyncio.subprocess.PIPE,
-            stdin=asyncio.subprocess.PIPE if input is not None else None,
-        )
-    else:
-        proc = await asyncio.create_subprocess_exec(
-            *args,
-            stderr=asyncio.subprocess.PIPE,
-            stdout=asyncio.subprocess.PIPE,
-            stdin=asyncio.subprocess.PIPE if input is not None else None,
-        )
+    proc = await asyncio.create_subprocess_exec(
+        *args,
+        stderr=asyncio.subprocess.PIPE,
+        stdout=asyncio.subprocess.PIPE,
+        stdin=asyncio.subprocess.PIPE if input is not None else None,
+    )
     stdout, stderr = await proc.communicate(input)
     return (proc.returncode, stdout, stderr)
index fa1c0163a7fd85ceb841b3f4390f2d78bf62bd8b..352af3783da079eda2c82c6334bd9b83a862d90e 100644 (file)
@@ -34,7 +34,7 @@ async def install_package(package: str) -> None:
     """Install package with pip, raise when install failed."""
     LOGGER.debug("Installing python package %s", package)
     args = ["pip", "install", "--find-links", HA_WHEELS, package]
-    return_code, output = await check_output(args)
+    return_code, output = await check_output(*args)
 
     if return_code != 0:
         msg = f"Failed to install package {package}\n{output.decode()}"
index aadab9aa11f6e4ab24341cf1df0feea345783dc7..6899e015f237f4a1d41bff8285cb65c68b509344 100644 (file)
@@ -818,7 +818,8 @@ class AirplayProvider(PlayerProvider):
         async def check_binary(cliraop_path: str) -> str | None:
             try:
                 returncode, output = await check_output(
-                    [cliraop_path, "-check"],
+                    cliraop_path,
+                    "-check",
                 )
                 if returncode == 0 and output.strip().decode() == "cliraop check":
                     self.cliraop_bin = cliraop_path
index 806898bc7c36b46eb9dc58c8649eff2983854cec..6a0c4759ce83e10cf6a079e04a65a8f6f88b46e6 100644 (file)
@@ -225,13 +225,13 @@ class SMBFileSystemProvider(LocalFileSystemProvider):
             [m.replace(password, "########") if password else m for m in mount_cmd],
         )
 
-        returncode, output = await check_output(mount_cmd)
+        returncode, output = await check_output(*mount_cmd)
         if returncode != 0:
             msg = f"SMB mount failed with error: {output.decode()}"
             raise LoginFailed(msg)
 
     async def unmount(self, ignore_error: bool = False) -> None:
         """Unmount the remote share."""
-        returncode, output = await check_output(["umount", self.base_path])
+        returncode, output = await check_output("umount", self.base_path)
         if returncode != 0 and not ignore_error:
             self.logger.warning("SMB unmount failed with error: %s", output.decode())
index 8b349296a01df1b1ac58284fb28f9471d829fe31..23c550affe92bb1c2cfadd3b44813c26f4329cdc 100644 (file)
@@ -102,12 +102,14 @@ class RadioBrowserProvider(MusicProvider):
 
         return result
 
-    @use_cache(86400 * 7)
     async def browse(self, path: str, offset: int, limit: int) -> list[MediaItemType]:
         """Browse this provider's items.
 
         :param path: The path to browse, (e.g. provid://artists).
         """
+        if offset != 0:
+            # paging is broken on RadioBrowser, we just return some big lists
+            return []
         subpath = path.split("://", 1)[1]
         subsubpath = "" if "/" not in subpath else subpath.split("/")[-1]
 
@@ -138,13 +140,11 @@ class RadioBrowserProvider(MusicProvider):
             ]
 
         if subpath == "popular":
-            return await self.get_by_popularity(limit=limit, offset=offset)
+            return await self.get_by_popularity()
 
         if subpath == "tag":
             tags = await self.radios.tags(
                 hide_broken=True,
-                limit=limit,
-                offset=offset,
                 order=Order.STATION_COUNT,
                 reverse=True,
             )
@@ -161,9 +161,7 @@ class RadioBrowserProvider(MusicProvider):
 
         if subpath == "country":
             items: list[BrowseFolder | Radio] = []
-            for country in await self.radios.countries(
-                order=Order.NAME, hide_broken=True, limit=limit, offset=offset
-            ):
+            for country in await self.radios.countries(order=Order.NAME, hide_broken=True):
                 folder = BrowseFolder(
                     item_id=country.code.lower(),
                     provider=self.domain,
@@ -181,19 +179,19 @@ class RadioBrowserProvider(MusicProvider):
                 items.append(folder)
             return items
 
-        if subsubpath in await self.get_tag_names(limit=limit, offset=offset):
+        if subsubpath in await self.get_tag_names():
             return await self.get_by_tag(subsubpath)
 
-        if subsubpath in await self.get_country_codes(limit=limit, offset=offset):
+        if subsubpath in await self.get_country_codes():
             return await self.get_by_country(subsubpath)
         return []
 
-    async def get_tag_names(self, limit: int, offset: int):
+    @use_cache(3600 * 24)
+    async def get_tag_names(self):
         """Get a list of tag names."""
         tags = await self.radios.tags(
             hide_broken=True,
-            limit=limit,
-            offset=offset,
+            limit=10000,
             order=Order.STATION_COUNT,
             reverse=True,
         )
@@ -203,22 +201,21 @@ class RadioBrowserProvider(MusicProvider):
             tag_names.append(tag.name.lower())
         return tag_names
 
-    async def get_country_codes(self, limit: int, offset: int):
+    @use_cache(3600 * 24)
+    async def get_country_codes(self):
         """Get a list of country names."""
-        countries = await self.radios.countries(
-            order=Order.NAME, hide_broken=True, limit=limit, offset=offset
-        )
+        countries = await self.radios.countries(order=Order.NAME, hide_broken=True)
         country_codes = []
         for country in countries:
             country_codes.append(country.code.lower())
         return country_codes
 
-    async def get_by_popularity(self, limit: int, offset: int):
+    @use_cache(3600)
+    async def get_by_popularity(self):
         """Get radio stations by popularity."""
         stations = await self.radios.stations(
             hide_broken=True,
-            limit=limit,
-            offset=offset,
+            limit=5000,
             order=Order.CLICK_COUNT,
             reverse=True,
         )
@@ -227,6 +224,7 @@ class RadioBrowserProvider(MusicProvider):
             items.append(await self._parse_radio(station))
         return items
 
+    @use_cache(3600)
     async def get_by_tag(self, tag: str):
         """Get radio stations by tag."""
         items = []
@@ -241,6 +239,7 @@ class RadioBrowserProvider(MusicProvider):
             items.append(await self._parse_radio(station))
         return items
 
+    @use_cache(3600)
     async def get_by_country(self, country_code: str):
         """Get radio stations by country."""
         items = []
index 73d829e91509a45f538d1dec63ddaafa834a564e..73ec3d3e567cb7a11531725265f6054ec7ab0b6d 100644 (file)
@@ -104,7 +104,7 @@ async def get_config_entries(
     action: [optional] action key called from config entries UI.
     values: the (intermediate) raw values for config entries sent with the action.
     """
-    returncode, output = await check_output(["snapserver", "-v"])
+    returncode, output = await check_output("snapserver", "-v")
     snapserver_version = int(output.decode().split(".")[1]) if returncode == 0 else -1
     local_snapserver_present = snapserver_version >= 27
     if returncode == 0 and not local_snapserver_present:
index 87035247b9ca58a51261e520d401f7040ca83499..15b57a01d4004a59dfee8378b56a37b0e3d155cd 100644 (file)
@@ -696,7 +696,7 @@ class SpotifyProvider(MusicProvider):
         ]
         if self._ap_workaround:
             args += ["--ap-port", "12345"]
-        _returncode, output = await check_output(args)
+        _returncode, output = await check_output(*args)
         if _returncode == 0 and output.decode().strip() != "authorized":
             raise LoginFailed(f"Login failed for username {self.config.get_value(CONF_USERNAME)}")
         # get token with (authorized) librespot
@@ -731,7 +731,7 @@ class SpotifyProvider(MusicProvider):
         ]
         if self._ap_workaround:
             args += ["--ap-port", "12345"]
-        _returncode, output = await check_output(args)
+        _returncode, output = await check_output(*args)
         duration = round(time.time() - time_start, 2)
         try:
             result = json.loads(output)
@@ -873,7 +873,7 @@ class SpotifyProvider(MusicProvider):
 
         async def check_librespot(librespot_path: str) -> str | None:
             try:
-                returncode, output = await check_output([librespot_path, "--check"])
+                returncode, output = await check_output(librespot_path, "--check")
                 if returncode == 0 and b"ok spotty" in output and b"using librespot" in output:
                     self._librespot_bin = librespot_path
                     return librespot_path