New metadata.album_review config setting (#216)

* add `get_album_review` endpoint

* add `metadata.album_review` config option

* add comment to `add_track_metadata`

* move `AlbumReview` model to its own file and clean up imports

* fix API endpoint for fetching album reviews

* add normalized_text method to AlbumReview model for text processing

* add unit test for `normalize_review_text` function and refactor `AlbumReview` model

* add `album_review` to metadata

* update comment

* add comment
This commit is contained in:
Oskar Dudziński
2025-11-13 13:38:04 +01:00
committed by GitHub
parent e0fe7e1655
commit 96fb746fd9
9 changed files with 75 additions and 0 deletions
+4
View File
@@ -91,6 +91,10 @@ embed_lyrics = false
# embed track cover in the track file
cover = false
# embed album review text to track COMMENT metadata field.
# only works when downloading album
album_review = false
[cover]
# please don't confuse the cover from metadata with cover as a distinct file.
+8
View File
@@ -0,0 +1,8 @@
from tiddl.core.api.models.review import normalize_review_text
def test_normalize_review_text():
text_before = 'Dropping on Halloween of 2017 with only a single day\'s advance notice, [wimpLink albumId="80611906"]Without Warning[/wimpLink] is a collaborative full-length between [wimpLink artistId="7279286"]21 Savage[/wimpLink], [wimpLink artistId="3958646"]Offset[/wimpLink] (of [wimpLink artistId="5024748"]Migos[/wimpLink]), and producer [wimpLink artistId="5012586"]Metro Boomin[/wimpLink], three of the most successful rap artists of the year. The release plays up its Halloween theme, with [wimpLink artistId="5012586"]Metro Boomin[/wimpLink] filling the tracks with scary sound effects and ominous beats, and the MCs delivering ghastly, violent lyrics. [wimpLink artistId="5198891"]Travis Scott[/wimpLink] and [wimpLink artistId="5906497"]Quavo[/wimpLink] contribute guest verses, and additional producers include Dre Moon, [wimpLink artistId="25917"]Southside[/wimpLink], and Cubeatz. [wimpLink albumId="80611906"]Without Warning[/wimpLink] was an immediate success, hitting the Top Five of the Billboard 200 albums chart following its release.'
text_after = "Dropping on Halloween of 2017 with only a single day's advance notice, Without Warning is a collaborative full-length between 21 Savage, Offset (of Migos), and producer Metro Boomin, three of the most successful rap artists of the year. The release plays up its Halloween theme, with Metro Boomin filling the tracks with scary sound effects and ominous beats, and the MCs delivering ghastly, violent lyrics. Travis Scott and Quavo contribute guest verses, and additional producers include Dre Moon, Southside, and Cubeatz. Without Warning was an immediate success, hitting the Top Five of the Billboard 200 albums chart following its release."
assert normalize_review_text(text=text_before) == text_after
+14
View File
@@ -172,11 +172,13 @@ def download_callback(
artist: str = "",
credits: list[AlbumItemsCredits.ItemWithCredits.CreditsEntry] = [],
cover_data: bytes | None = None,
album_review: str = "",
) -> None:
self.date = date
self.artist = artist
self.credits = credits
self.cover_data = cover_data
self.album_review = album_review
async def handle_resource(resource: TidalResource):
async def handle_item(
@@ -222,6 +224,7 @@ def download_callback(
cover_data=cover_data,
date=track_metadata.date,
credits=track_metadata.credits,
comment=track_metadata.album_review,
)
elif isinstance(item, Video):
@@ -245,6 +248,16 @@ def download_callback(
if album.cover and (CONFIG.metadata.cover or save_cover):
cover = Cover(album.cover, size=CONFIG.cover.size)
album_review = ""
if CONFIG.metadata.album_review:
try:
album_review = ctx.obj.api.get_album_review(
album_id=resource.id
).normalized_text()
except Exception as e:
log.error(e)
while True:
album_items = ctx.obj.api.get_album_items_credits(
album_id=album.id, offset=offset
@@ -264,6 +277,7 @@ def download_callback(
date=str(album.releaseDate),
artist=album.artist.name if album.artist else "",
credits=album_item.credits,
album_review=album_review,
),
)
)
+1
View File
@@ -27,6 +27,7 @@ class Config(BaseModel):
enable: bool = True
lyrics: bool = False
cover: bool = False
album_review: bool = False
metadata: MetadataConfig = MetadataConfig()
+9
View File
@@ -26,6 +26,7 @@ from .models.base import (
TrackStream,
VideoStream,
)
from .models.review import AlbumReview
ID: TypeAlias = str | int
@@ -96,6 +97,14 @@ class TidalAPI:
expire_after=3600,
)
def get_album_review(self, album_id: ID):
return self.client.fetch(
AlbumReview,
f"albums/{album_id}/review",
{"countryCode": self.country_code},
expire_after=3600,
)
def get_artist(self, artist_id: ID):
return self.client.fetch(
Artist,
+2
View File
@@ -28,9 +28,11 @@ class Items(BaseModel):
class ArtistAlbumsItems(Items):
items: List[Album]
class ArtistVideosItems(Items):
items: List[Video]
ItemType = Literal["track", "video"]
+30
View File
@@ -0,0 +1,30 @@
import re
from datetime import datetime
from pydantic import BaseModel
def normalize_review_text(text: str | None = None) -> str:
if not text:
return ""
text = re.sub(
r"\[wimpLink\b[^\]]*\](.*?)\[/wimpLink\]",
r"\1",
text,
flags=re.DOTALL | re.IGNORECASE,
)
text = re.sub(r"\[/?wimpLink\b[^\]]*\]", "", text, flags=re.IGNORECASE)
return text.strip()
class AlbumReview(BaseModel):
source: str
lastUpdated: datetime
text: str
summary: str
def normalized_text(self) -> str:
return normalize_review_text(self.text)
+5
View File
@@ -26,6 +26,7 @@ class Metadata:
default_factory=list
)
cover_data: bytes | None = None
comment: str = ""
def add_flac_metadata(track_path: Path, metadata: Metadata) -> None:
@@ -55,6 +56,7 @@ def add_flac_metadata(track_path: Path, metadata: Metadata) -> None:
"YEAR": (str(date.year) if date else ""),
"COPYRIGHT": metadata.copyright or "",
"ISRC": metadata.isrc,
"COMMENT": metadata.comment,
}
)
@@ -94,6 +96,7 @@ def add_m4a_metadata(track_path: Path, metadata: Metadata) -> None:
"artist": metadata.artists,
"date": metadata.date,
"copyright": metadata.copyright or "",
"comment": metadata.comment,
}
)
@@ -111,6 +114,7 @@ def add_track_metadata(
lyrics: str = "",
cover_data: bytes | None = None,
credits: list[AlbumItemsCredits.ItemWithCredits.CreditsEntry] | None = None,
comment: str = "",
) -> None:
"""Add FLAC or M4A metadata based on file extension."""
@@ -128,6 +132,7 @@ def add_track_metadata(
lyrics=lyrics or None,
cover_data=cover_data,
credits=credits or [],
comment=comment,
)
ext = path.suffix.lower()
+2
View File
@@ -63,6 +63,8 @@ def generate_template_data(
copyright_ = item.copyright or ""
bpm = item.bpm or 0
isrc = item.isrc or ""
# FIX audio quality should be returned from `get_existing_track_filename`.
# `item.audioQuality` tells highest quality of track - not quality we downloaded
quality = item.audioQuality or ""
else: # Video
version = ""