From 54dbd150cb650595fcdfff6c3925b61edd2afdcb Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Sun, 17 May 2026 23:02:41 -0700 Subject: [PATCH] Preserve full release dates in audio tags --- core/metadata/source.py | 28 +++++++++++++- tests/metadata/test_metadata_enrichment.py | 45 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/core/metadata/source.py b/core/metadata/source.py index 8903eb0e..65ac780c 100644 --- a/core/metadata/source.py +++ b/core/metadata/source.py @@ -183,6 +183,27 @@ def _names_match(a: str, b: str, threshold: float = 0.75) -> bool: return SequenceMatcher(None, norm(a), norm(b)).ratio() >= threshold +def _normalize_release_date_tag(value: Any) -> str: + """Return a tag-safe release date without inventing missing precision.""" + raw = str(value or "").strip() + if not raw: + return "" + + # Source APIs commonly return ISO timestamps. Audio DATE/TDRC tags should + # receive only the date precision the source actually provided. + raw = raw.split("T", 1)[0].strip() + match = re.match(r"^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$", raw) + if not match: + return "" + + year, month, day = match.groups() + if day: + return f"{year}-{month}-{day}" + if month: + return f"{year}-{month}" + return year + + def _collect_source_ids(metadata: dict, cfg) -> dict: source_ids = {} source = (metadata.get("source") or "").strip().lower() @@ -1058,7 +1079,9 @@ def extract_source_metadata(context: dict, artist: dict, album_info: dict) -> di metadata["disc_number"] = disc_num if disc_num is not None else 1 if album_ctx and album_ctx.get("release_date"): - metadata["date"] = album_ctx["release_date"][:4] + release_date = _normalize_release_date_tag(album_ctx.get("release_date")) + if release_date: + metadata["date"] = release_date genres = artist_dict.get("genres") or [] if genres: @@ -1077,11 +1100,12 @@ def extract_source_metadata(context: dict, artist: dict, album_info: dict) -> di metadata["album_art_url"] = album_image logger.info( - "[Metadata Summary] title='%s' | artist='%s' | album_artist='%s' | album='%s' | track=%s/%s | disc=%s", + "[Metadata Summary] title='%s' | artist='%s' | album_artist='%s' | album='%s' | date=%s | track=%s/%s | disc=%s", metadata.get("title"), metadata.get("artist"), metadata.get("album_artist"), metadata.get("album"), + metadata.get("date", ""), metadata.get("track_number"), metadata.get("total_tracks"), metadata.get("disc_number"), diff --git a/tests/metadata/test_metadata_enrichment.py b/tests/metadata/test_metadata_enrichment.py index 3255e8c1..b1230ef7 100644 --- a/tests/metadata/test_metadata_enrichment.py +++ b/tests/metadata/test_metadata_enrichment.py @@ -188,9 +188,54 @@ def test_extract_source_metadata_keeps_neutral_fields_and_skips_itunes_fallback_ assert metadata["track_number"] == 3 assert metadata["total_tracks"] == 12 assert metadata["disc_number"] == 2 + assert metadata["date"] == "2024-01-02" assert metadata["album_art_url"] == "https://img.example/album.jpg" +@pytest.mark.parametrize( + ("raw_release_date", "expected_tag_date"), + [ + ("2024-05-06", "2024-05-06"), + ("2024-05", "2024-05"), + ("2024", "2024"), + ("2024-05-06T07:08:09Z", "2024-05-06"), + ], +) +def test_extract_source_metadata_preserves_available_release_date_precision(monkeypatch, raw_release_date, expected_tag_date): + monkeypatch.setattr(ms, "get_config_manager", lambda: _Config({"file_organization.collab_artist_mode": "first"})) + + context = { + "source": "spotify", + "artist": {"name": "Artist One", "id": "123", "genres": []}, + "album": { + "name": "Album One", + "total_tracks": 12, + "release_date": raw_release_date, + }, + "track_info": { + "artists": [{"name": "Artist One"}], + "_source": "spotify", + "track_number": 3, + "disc_number": 1, + }, + "original_search_result": { + "title": "Song One", + "artists": [{"name": "Artist One"}], + "clean_title": "Song One", + "clean_album": "Album One", + "clean_artist": "Artist One", + }, + } + + metadata = me.extract_source_metadata( + context, + context["artist"], + {"is_album": True, "album_name": "Album One", "track_number": 3, "disc_number": 1}, + ) + + assert metadata["date"] == expected_tag_date + + def test_embed_source_ids_uses_current_source_ids_and_legacy_fallback(monkeypatch): audio = _FakeAudio() symbols = _fake_symbols(audio)