From: Marcel van der Veldt Date: Tue, 1 Aug 2023 22:33:27 +0000 (+0200) Subject: Prevent duplicate filesystem mappings (#811) X-Git-Url: https://git.kitaultman.com/?a=commitdiff_plain;h=5f5fd3cdff8810b9e58da42714887b252e2615b3;p=music-assistant-server.git Prevent duplicate filesystem mappings (#811) --- diff --git a/music_assistant/common/helpers/util.py b/music_assistant/common/helpers/util.py index 343cbd4d..1a73f44c 100755 --- a/music_assistant/common/helpers/util.py +++ b/music_assistant/common/helpers/util.py @@ -47,7 +47,7 @@ def try_parse_bool(possible_bool: Any) -> str: def create_sort_name(input_str: str) -> str: """Create sort name/title from string.""" input_str = input_str.lower().strip() - for item in ["the ", "de ", "les ", "dj "]: + for item in ["the ", "de ", "les ", "dj ", ".", "-", "'", "`"]: if input_str.startswith(item): input_str = input_str.replace(item, "") return input_str.strip() diff --git a/music_assistant/common/models/media_items.py b/music_assistant/common/models/media_items.py index a7887de0..ed0ecc0a 100755 --- a/music_assistant/common/models/media_items.py +++ b/music_assistant/common/models/media_items.py @@ -80,16 +80,13 @@ class ProviderMapping(DataClassDictMixin): def __hash__(self) -> int: """Return custom hash.""" - return hash((self.provider_instance, self.item_id.lower())) + return hash((self.provider_instance, self.item_id)) def __eq__(self, other: ProviderMapping) -> bool: """Check equality of two items.""" if not other: return False - return ( - self.provider_instance == other.provider_instance - and self.item_id.lower() == other.item_id.lower() - ) + return self.provider_instance == other.provider_instance and self.item_id == other.item_id @dataclass(frozen=True) diff --git a/music_assistant/server/controllers/media/base.py b/music_assistant/server/controllers/media/base.py index 9b818943..6156bcf0 100644 --- a/music_assistant/server/controllers/media/base.py +++ b/music_assistant/server/controllers/media/base.py @@ -102,6 +102,7 @@ class MediaControllerBase(Generic[ItemCls], metaclass=ABCMeta): if self.media_type in (MediaType.ALBUM, MediaType.TRACK): query_parts.append( f"({self.db_table}.name LIKE :search " + f" OR {self.db_table}.sort_name LIKE :search" f" OR {self.db_table}.artists LIKE :search)" ) else: diff --git a/music_assistant/server/providers/filesystem_local/base.py b/music_assistant/server/providers/filesystem_local/base.py index f9bc6d60..3d204124 100644 --- a/music_assistant/server/providers/filesystem_local/base.py +++ b/music_assistant/server/providers/filesystem_local/base.py @@ -11,7 +11,7 @@ from dataclasses import dataclass import cchardet import xmltodict -from music_assistant.common.helpers.util import parse_title_and_version +from music_assistant.common.helpers.util import create_sort_name, parse_title_and_version from music_assistant.common.models.config_entries import ( ConfigEntry, ConfigEntryType, @@ -790,7 +790,26 @@ class FileSystemProviderBase(MusicProvider): """Lookup metadata in Artist folder.""" assert name or artist_path if not artist_path: - artist_path = name + # check if we have an existing item + sort_name = create_sort_name(name) + async for item in self.mass.music.artists.iter_library_items(search=sort_name): + if not compare_strings(sort_name, item.sort_name): + continue + for prov_mapping in item.provider_mappings: + if prov_mapping.provider_instance == self.instance_id: + artist_path = prov_mapping.url + break + if artist_path: + break + else: + # check if we have an artist folder for this artist at root level + if await self.exists(name): + artist_path = name + elif await self.exists(name.title()): + artist_path = name.title() + else: + # use fake artist path as item id which is just the name + artist_path = name if not name: name = artist_path.split(os.sep)[-1] diff --git a/music_assistant/server/providers/musicbrainz/__init__.py b/music_assistant/server/providers/musicbrainz/__init__.py index e146b6b8..cf72824f 100644 --- a/music_assistant/server/providers/musicbrainz/__init__.py +++ b/music_assistant/server/providers/musicbrainz/__init__.py @@ -125,7 +125,8 @@ class MusicbrainzProvider(MetadataProvider): assert albumname or album_barcode for searchartist in ( artistname, - re.sub(LUCENE_SPECIAL, r"\\\1", create_sort_name(artistname)), + re.sub(LUCENE_SPECIAL, r"\\\1", artistname), + create_sort_name(artistname), ): if album_barcode: # search by album barcode (EAN or UPC) diff --git a/music_assistant/server/providers/tidal/__init__.py b/music_assistant/server/providers/tidal/__init__.py index e024348a..945a732a 100644 --- a/music_assistant/server/providers/tidal/__init__.py +++ b/music_assistant/server/providers/tidal/__init__.py @@ -17,8 +17,6 @@ from tidalapi import Session as TidalSession from tidalapi import Track as TidalTrack from tidalapi.media import Lyrics as TidalLyrics -from music_assistant.common.helpers.uri import create_uri -from music_assistant.common.helpers.util import create_sort_name from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType from music_assistant.common.models.enums import ( AlbumType, @@ -425,8 +423,6 @@ class TidalProvider(MusicProvider): item_id=key, provider=self.instance_id, name=name, - uri=create_uri(media_type, self.instance_id, key), - sort_name=create_sort_name(self.name), ) async def _get_tidal_session(self) -> TidalSession: diff --git a/music_assistant/server/providers/ytmusic/__init__.py b/music_assistant/server/providers/ytmusic/__init__.py index 65441890..e215ce87 100644 --- a/music_assistant/server/providers/ytmusic/__init__.py +++ b/music_assistant/server/providers/ytmusic/__init__.py @@ -10,8 +10,6 @@ from urllib.parse import unquote import pytube -from music_assistant.common.helpers.uri import create_uri -from music_assistant.common.helpers.util import create_sort_name from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType from music_assistant.common.models.enums import ConfigEntryType, ProviderFeature from music_assistant.common.models.errors import ( @@ -834,12 +832,10 @@ class YoutubeMusicProvider(MusicProvider): def _get_item_mapping(self, media_type: MediaType, key: str, name: str) -> ItemMapping: return ItemMapping( - media_type, - key, - self.instance_id, - name, - create_uri(media_type, self.instance_id, key), - create_sort_name(self.name), + media_type=media_type, + item_id=key, + provider=self.instance_id, + name=name, ) def _get_artist_item_mapping(self, artist_obj: dict) -> ItemMapping: