Feat: Use dedicated directory for cache files
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 3 Nov 2024 13:15:42 +0000 (14:15 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Sun, 3 Nov 2024 13:15:42 +0000 (14:15 +0100)
this makes it easier to exclude it from backups

music_assistant/controllers/cache.py
music_assistant/controllers/metadata.py
music_assistant/mass.py
music_assistant/providers/spotify/__init__.py

index 687b3374778de54dd886b359d729501acf966445..9df242f575eb3e9a263ff736753e4fc31de81cc7 100644 (file)
@@ -210,7 +210,7 @@ class CacheController(CoreController):
 
     async def _setup_database(self) -> None:
         """Initialize database."""
-        db_path = os.path.join(self.mass.storage_path, "cache.db")
+        db_path = os.path.join(self.mass.cache_path, "cache.db")
         self.database = DatabaseConnection(db_path)
         await self.database.setup()
 
index 9eab843dbac51c28fb9033c27ce7378fe18567ef..1aadabe906e83717671a9134162e8a1e56c1d12e 100644 (file)
@@ -172,7 +172,7 @@ class MetaDataController(CoreController):
             # silence PIL logger
             logging.getLogger("PIL").setLevel(logging.WARNING)
         # make sure that our directory with collage images exists
-        self._collage_images_dir = os.path.join(self.mass.storage_path, "collage_images")
+        self._collage_images_dir = os.path.join(self.mass.cache_path, "collage_images")
         if not await asyncio.to_thread(os.path.exists, self._collage_images_dir):
             await asyncio.to_thread(os.mkdir, self._collage_images_dir)
         self.mass.streams.register_dynamic_route("/imageproxy", self.handle_imageproxy)
index f183c6835ac84ec66963df69252ee7da500b205c..5f2174d52fac807e5c0317ece5491b344b79d098 100644 (file)
@@ -58,6 +58,10 @@ if TYPE_CHECKING:
 
 isdir = wrap(os.path.isdir)
 isfile = wrap(os.path.isfile)
+mkdirs = wrap(os.makedirs)
+rmfile = wrap(os.remove)
+listdir = wrap(os.listdir)
+rename = wrap(os.rename)
 
 EventCallBackType = Callable[[MassEvent], None]
 EventSubscriptionType = tuple[
@@ -90,6 +94,7 @@ class MusicAssistant:
     def __init__(self, storage_path: str, safe_mode: bool = False) -> None:
         """Initialize the MusicAssistant Server."""
         self.storage_path = storage_path
+        self.cache_path = os.path.join(storage_path, ".cache")
         self.safe_mode = safe_mode
         # we dynamically register command handlers which can be consumed by the apis
         self.command_handlers: dict[str, APICommandHandler] = {}
@@ -123,6 +128,8 @@ class MusicAssistant:
         # setup config controller first and fetch important config values
         self.config = ConfigController(self)
         await self.config.setup()
+        # setup/migrate storage
+        await self._setup_storage()
         LOGGER.info(
             "Starting Music Assistant Server (%s) version %s - HA add-on: %s - Safe mode: %s",
             self.server_id,
@@ -770,3 +777,20 @@ class MusicAssistant:
                 },
             }
         )
+
+    async def _setup_storage(self) -> None:
+        """Handle Setup of storage/cache folder(s)."""
+        if not await isdir(self.storage_path):
+            await mkdirs(self.storage_path)
+        if not await isdir(self.cache_path):
+            await mkdirs(self.cache_path)
+        # cleanup old cache files from their old locations
+        # TODO: Remove this code after MA version 2.5+
+        old_cache_db = os.path.join(self.storage_path, "cache.db")
+        if await isfile(old_cache_db):
+            await rmfile(old_cache_db)
+        for filename in await listdir(self.storage_path):
+            if filename.startswith(("spotify", "collage")):
+                old_loc = os.path.join(self.storage_path, filename)
+                new_loc = os.path.join(self.cache_path, filename)
+                await rename(old_loc, new_loc)
index 1d78f9ebb93baa26b550be8b4d5bc6f4eb607904..abc1770ada22536b91137ce0e641b4adab3ead3c 100644 (file)
@@ -90,7 +90,6 @@ SCOPE = [
 
 CALLBACK_REDIRECT_URL = "https://music-assistant.io/callback"
 
-CACHE_DIR = "/tmp/spotify_cache"  # noqa: S108
 LIKED_SONGS_FAKE_PLAYLIST_ID_PREFIX = "liked_songs"
 SUPPORTED_FEATURES = (
     ProviderFeature.LIBRARY_ARTISTS,
@@ -246,7 +245,7 @@ class SpotifyProvider(MusicProvider):
 
     async def handle_async_init(self) -> None:
         """Handle async initialization of the provider."""
-        self.config_dir = os.path.join(self.mass.storage_path, self.instance_id)
+        self.cache_dir = os.path.join(self.mass.cache_path, self.instance_id)
         self.throttler = ThrottlerManager(rate_limit=1, period=2)
         if self.config.get_value(CONF_CLIENT_ID):
             # loosen the throttler a bit when a custom client id is used
@@ -558,9 +557,7 @@ class SpotifyProvider(MusicProvider):
         args = [
             librespot,
             "--cache",
-            CACHE_DIR,
-            "--system-cache",
-            self.config_dir,
+            self.cache_dir,
             "--cache-size-limit",
             "1G",
             "--passthrough",
@@ -824,8 +821,8 @@ class SpotifyProvider(MusicProvider):
         librespot = await self.get_librespot_binary()
         args = [
             librespot,
-            "--system-cache",
-            self.config_dir,
+            "--cache",
+            self.cache_dir,
             "--check-auth",
         ]
         ret_code, stdout = await check_output(*args)
@@ -833,11 +830,7 @@ class SpotifyProvider(MusicProvider):
             # cached librespot creds are invalid, re-authenticate
             # we can use the check-token option to send a new token to librespot
             # librespot will then get its own token from spotify (somehow) and cache that.
-            args = [
-                librespot,
-                "--system-cache",
-                self.config_dir,
-                "--check-auth",
+            args += [
                 "--access-token",
                 auth_info["access_token"],
             ]