Few smart fades fixes (#2442)
authorMarvin Schenkel <marvinschenkel@gmail.com>
Mon, 29 Sep 2025 19:33:32 +0000 (21:33 +0200)
committerGitHub <noreply@github.com>
Mon, 29 Sep 2025 19:33:32 +0000 (21:33 +0200)
music_assistant/controllers/music.py

index f7bd003a06440efd7019f1e70f27bee48f547cff..33e609d05347a01637af3e598f20fbfb4bbca4c6 100644 (file)
@@ -3,7 +3,6 @@
 from __future__ import annotations
 
 import asyncio
-import json
 import logging
 import os
 import shutil
@@ -67,7 +66,8 @@ from music_assistant.helpers.api import api_command
 from music_assistant.helpers.compare import compare_strings, compare_version, create_safe_string
 from music_assistant.helpers.database import DatabaseConnection
 from music_assistant.helpers.datetime import utc_timestamp
-from music_assistant.helpers.json import json_loads, serialize_to_json
+from music_assistant.helpers.json import json_dumps, json_loads, serialize_to_json
+from music_assistant.helpers.smart_fades import SMART_CROSSFADE_DURATION
 from music_assistant.helpers.tags import split_artists
 from music_assistant.helpers.uri import parse_uri
 from music_assistant.helpers.util import TaskManager, parse_title_and_version
@@ -846,16 +846,23 @@ class MusicController(CoreController):
         """Store Smart Fades BPM analysis for a track in db."""
         if not (provider := self.mass.get_provider(provider_instance_id_or_domain)):
             return
-        if analysis.bpm <= 0 or analysis.confidence < 0:
-            # skip invalid values
+        if (
+            analysis.duration <= 0.75 * SMART_CROSSFADE_DURATION
+            or analysis.bpm <= 0
+            or analysis.confidence < 0
+        ):
+            # skip invalid values, we skip analysis that were performed on
+            # a short amount of audio as those are often unreliable
             return
+        beats_json = await asyncio.to_thread(lambda: json_dumps(analysis.beats.tolist()))
+        downbeats_json = await asyncio.to_thread(lambda: json_dumps(analysis.downbeats.tolist()))
         values = {
             "fragment": analysis.fragment.value,
             "item_id": item_id,
             "provider": provider.lookup_key,
             "bpm": analysis.bpm,
-            "beats": json.dumps(analysis.beats.tolist()),
-            "downbeats": json.dumps(analysis.downbeats.tolist()),
+            "beats": beats_json,
+            "downbeats": downbeats_json,
             "confidence": analysis.confidence,
             "duration": analysis.duration,
         }
@@ -879,11 +886,13 @@ class MusicController(CoreController):
             },
         )
         if db_row and db_row["bpm"] > 0:
+            beats = await asyncio.to_thread(lambda: np.array(json_loads(db_row["beats"])))
+            downbeats = await asyncio.to_thread(lambda: np.array(json_loads(db_row["downbeats"])))
             return SmartFadesAnalysis(
                 fragment=SmartFadesAnalysisFragment(db_row["fragment"]),
                 bpm=float(db_row["bpm"]),
-                beats=np.array(json.loads(db_row["beats"])),
-                downbeats=np.array(json.loads(db_row["downbeats"])),
+                beats=beats,
+                downbeats=downbeats,
                 confidence=float(db_row["confidence"]),
                 duration=float(db_row["duration"]),
             )