From: Marcel van der Veldt Date: Sat, 20 Dec 2025 20:40:12 +0000 (+0100) Subject: Fix inconsistencies in name+version parsing that affects mapping X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=c0b1cf41c7ee885a589c5540d3163132a6459323;p=music-assistant-server.git Fix inconsistencies in name+version parsing that affects mapping --- diff --git a/music_assistant/providers/apple_music/__init__.py b/music_assistant/providers/apple_music/__init__.py index ac352a93..caf038a6 100644 --- a/music_assistant/providers/apple_music/__init__.py +++ b/music_assistant/providers/apple_music/__init__.py @@ -67,7 +67,7 @@ from music_assistant.helpers.auth import AuthenticationHelper from music_assistant.helpers.json import json_loads from music_assistant.helpers.playlists import fetch_playlist from music_assistant.helpers.throttle_retry import ThrottlerManager, throttle_with_retries -from music_assistant.helpers.util import infer_album_type +from music_assistant.helpers.util import infer_album_type, parse_title_and_version from music_assistant.models.music_provider import MusicProvider if TYPE_CHECKING: @@ -762,10 +762,12 @@ class AppleMusicProvider(MusicProvider): attributes.get("name"), ) return None + name, version = parse_title_and_version(attributes["name"]) album = Album( item_id=album_id, provider=self.domain, - name=attributes.get("name"), + name=name, + version=version, provider_mappings={ ProviderMapping( item_id=album_id, @@ -848,10 +850,12 @@ class AppleMusicProvider(MusicProvider): else: track_id = track_obj["id"] attributes = {} + name, version = parse_title_and_version(attributes.get("name", "")) track = Track( item_id=track_id, provider=self.domain, - name=attributes.get("name"), + name=name, + version=version, duration=attributes.get("durationInMillis", 0) / 1000, provider_mappings={ ProviderMapping( diff --git a/music_assistant/providers/deezer/__init__.py b/music_assistant/providers/deezer/__init__.py index f3831cf1..05dbead2 100644 --- a/music_assistant/providers/deezer/__init__.py +++ b/music_assistant/providers/deezer/__init__.py @@ -47,7 +47,7 @@ from music_assistant.controllers.cache import use_cache from music_assistant.helpers.app_vars import app_var # type: ignore[attr-defined] from music_assistant.helpers.auth import AuthenticationHelper from music_assistant.helpers.datetime import utc_timestamp -from music_assistant.helpers.util import infer_album_type +from music_assistant.helpers.util import infer_album_type, parse_title_and_version from music_assistant.models import ProviderInstanceType from music_assistant.models.music_provider import MusicProvider @@ -615,11 +615,13 @@ class DeezerProvider(MusicProvider): def parse_album(self, album: deezer.Album) -> Album: """Parse the deezer-python album to a Music Assistant album.""" + name, version = parse_title_and_version(album.title) return Album( album_type=self.get_album_type(album), item_id=str(album.id), provider=self.instance_id, - name=album.title, + name=name, + version=version, artists=UniqueList( [ ItemMapping( @@ -703,10 +705,12 @@ class DeezerProvider(MusicProvider): else: album = None + name, version = parse_title_and_version(track.title) item = Track( item_id=str(track.id), provider=self.instance_id, - name=track.title, + name=name, + version=version, sort_name=self.get_short_title(track), duration=track.duration, artists=UniqueList([artist]) if artist else UniqueList(), diff --git a/music_assistant/providers/jellyfin/parsers.py b/music_assistant/providers/jellyfin/parsers.py index bac9aaf8..e7c50825 100644 --- a/music_assistant/providers/jellyfin/parsers.py +++ b/music_assistant/providers/jellyfin/parsers.py @@ -21,6 +21,8 @@ from music_assistant_models.media_items import ( UniqueList, ) +from music_assistant.helpers.util import parse_title_and_version + from .const import ( DOMAIN, ITEM_KEY_ALBUM, @@ -65,10 +67,12 @@ def parse_album( ) -> Album: """Parse a Jellyfin Album response to an Album model object.""" album_id = jellyfin_album[ITEM_KEY_ID] + name, version = parse_title_and_version(jellyfin_album[ITEM_KEY_NAME]) album = Album( item_id=album_id, provider=DOMAIN, - name=jellyfin_album[ITEM_KEY_NAME], + name=name, + version=version, provider_mappings={ ProviderMapping( item_id=str(album_id), @@ -195,12 +199,13 @@ def parse_track( logger: Logger, instance_id: str, client: Connection, jellyfin_track: JellyTrack ) -> Track: """Parse a Jellyfin Track response to a Track model object.""" - available = False available = jellyfin_track[ITEM_KEY_CAN_DOWNLOAD] + name, version = parse_title_and_version(jellyfin_track[ITEM_KEY_NAME]) track = Track( item_id=jellyfin_track[ITEM_KEY_ID], provider=instance_id, - name=jellyfin_track[ITEM_KEY_NAME], + name=name, + version=version, provider_mappings={ ProviderMapping( item_id=jellyfin_track[ITEM_KEY_ID], diff --git a/music_assistant/providers/nugs/__init__.py b/music_assistant/providers/nugs/__init__.py index 380a8d34..a30c6214 100644 --- a/music_assistant/providers/nugs/__init__.py +++ b/music_assistant/providers/nugs/__init__.py @@ -41,7 +41,7 @@ from music_assistant_models.streamdetails import StreamDetails from music_assistant.constants import CONF_PASSWORD, CONF_USERNAME from music_assistant.controllers.cache import use_cache from music_assistant.helpers.json import json_loads -from music_assistant.helpers.util import infer_album_type +from music_assistant.helpers.util import infer_album_type, parse_title_and_version from music_assistant.models.music_provider import MusicProvider if TYPE_CHECKING: @@ -281,11 +281,12 @@ class NugsProvider(MusicProvider): """Parse nugs release/show/album object to generic album layout.""" item_id = album_obj.get("releaseId") or album_obj.get("id") or album_obj.get("containerID") title = album_obj.get("title") or album_obj.get("containerInfo") + name, version = parse_title_and_version(str(title)) album = Album( item_id=str(item_id), provider=self.instance_id, - name=str(title), - # version=album_obj["type"], + name=name, + version=version, provider_mappings={ ProviderMapping( item_id=str(item_id), @@ -327,7 +328,7 @@ class NugsProvider(MusicProvider): album.year = int(year) # No album type info in this provider so try and infer it - album.album_type = infer_album_type(album.name, "") + album.album_type = infer_album_type(album.name, album.version) return album @@ -371,11 +372,13 @@ class NugsProvider(MusicProvider): track_obj.get("trackId") or track_obj.get("trackID") or track_obj.get("trackLabel") ) track_name = track_obj.get("name") or track_obj.get("songTitle") + name, version = parse_title_and_version(str(track_name)) track = Track( item_id=str(track_id), provider=self.instance_id, - name=str(track_name), + name=name, + version=version, provider_mappings={ ProviderMapping( item_id=str(track_id), diff --git a/music_assistant/providers/opensubsonic/parsers.py b/music_assistant/providers/opensubsonic/parsers.py index 406812b5..50123406 100644 --- a/music_assistant/providers/opensubsonic/parsers.py +++ b/music_assistant/providers/opensubsonic/parsers.py @@ -23,6 +23,7 @@ from music_assistant_models.media_items import ( ) from music_assistant.constants import UNKNOWN_ARTIST +from music_assistant.helpers.util import parse_title_and_version if TYPE_CHECKING: from libopensonic.media import AlbumID3 as SonicAlbum @@ -108,10 +109,12 @@ def parse_track( for c in sonic_song.contributors: metadata.performers.add(c.artist.name) + name, version = parse_title_and_version(sonic_song.title) track = Track( item_id=sonic_song.id, provider=instance_id, - name=sonic_song.title, + name=name, + version=version, album=album, duration=sonic_song.duration or 0, disc_number=sonic_song.disc_number or 0, @@ -313,11 +316,13 @@ def parse_album( if sonic_album.moods: metadata.mood = sonic_album.moods[0] + name, version = parse_title_and_version(sonic_album.name) album = Album( item_id=sonic_album.id, provider=SUBSONIC_DOMAIN, metadata=metadata, - name=sonic_album.name, + name=name, + version=version, favorite=bool(sonic_album.starred), provider_mappings={ ProviderMapping( diff --git a/music_assistant/providers/tidal/parsers.py b/music_assistant/providers/tidal/parsers.py index 5911ae83..30ea0847 100644 --- a/music_assistant/providers/tidal/parsers.py +++ b/music_assistant/providers/tidal/parsers.py @@ -24,7 +24,7 @@ from music_assistant_models.media_items import ( UniqueList, ) -from music_assistant.helpers.util import infer_album_type +from music_assistant.helpers.util import infer_album_type, parse_title_and_version from .constants import BROWSE_URL, RESOURCES_URL @@ -70,8 +70,10 @@ def parse_artist(provider: TidalProvider, artist_obj: dict[str, Any]) -> Artist: def parse_album(provider: TidalProvider, album_obj: dict[str, Any]) -> Album: """Parse tidal album object to generic layout.""" - name = album_obj.get("title", "Unknown Album") - version = album_obj.get("version", "") or "" + name, version = parse_title_and_version( + album_obj.get("title", "Unknown Album"), + album_obj.get("version") or None, + ) album_id = str(album_obj.get("id", "")) album = Album( @@ -162,7 +164,10 @@ def parse_track( lyrics: dict[str, str] | None = None, ) -> Track: """Parse tidal track object to generic layout.""" - version = track_obj.get("version", "") or "" + name, version = parse_title_and_version( + track_obj.get("title", "Unknown"), + track_obj.get("version") or None, + ) track_id = str(track_obj.get("id", 0)) media_metadata = track_obj.get("mediaMetadata") or {} tags = media_metadata.get("tags", []) @@ -170,7 +175,7 @@ def parse_track( track = Track( item_id=track_id, provider=provider.instance_id, - name=track_obj.get("title", "Unknown"), + name=name, version=version, duration=track_obj.get("duration", 0), provider_mappings={ diff --git a/music_assistant/providers/ytmusic/__init__.py b/music_assistant/providers/ytmusic/__init__.py index 9cc5981a..592c7da4 100644 --- a/music_assistant/providers/ytmusic/__init__.py +++ b/music_assistant/providers/ytmusic/__init__.py @@ -54,7 +54,7 @@ from ytmusicapi.helpers import get_authorization, sapisid_from_cookie from music_assistant.constants import CONF_USERNAME, VERBOSE_LOG_LEVEL from music_assistant.controllers.cache import use_cache -from music_assistant.helpers.util import infer_album_type, install_package +from music_assistant.helpers.util import infer_album_type, install_package, parse_title_and_version from music_assistant.models.music_provider import MusicProvider from .helpers import ( @@ -740,12 +740,15 @@ class YoutubeMusicProvider(MusicProvider): raise InvalidDataError("Album ID is required but not found") if "title" in album_obj: - name = album_obj["title"] + name, version = parse_title_and_version(album_obj["title"]) elif "name" in album_obj: - name = album_obj["name"] + name, version = parse_title_and_version(album_obj["name"]) + else: + name, version = "", "" album = Album( item_id=album_id, name=name, + version=version, provider=self.instance_id, provider_mappings={ ProviderMapping( @@ -787,7 +790,7 @@ class YoutubeMusicProvider(MusicProvider): album.album_type = album_type # Try inference - override if it finds something more specific - inferred_type = infer_album_type(name, "") # YouTube doesn't seem to have version field + inferred_type = infer_album_type(name, version) if inferred_type in (AlbumType.SOUNDTRACK, AlbumType.LIVE): album.album_type = inferred_type @@ -873,10 +876,12 @@ class YoutubeMusicProvider(MusicProvider): msg = "Track is missing videoId" raise InvalidDataError(msg) track_id = str(track_obj["videoId"]) + name, version = parse_title_and_version(track_obj["title"]) track = Track( item_id=track_id, provider=self.instance_id, - name=track_obj["title"], + name=name, + version=version, provider_mappings={ ProviderMapping( item_id=track_id,