Add credits to metadata (#77)

 close #73
This commit is contained in:
Oskar Dudziński
2025-02-05 20:20:24 +01:00
committed by GitHub
parent f4223f908d
commit 5e02815e69
5 changed files with 129 additions and 28 deletions
+4
View File
@@ -52,6 +52,10 @@ class TestApi(unittest.TestCase):
album_items = self.api.getAlbumItems(103805723, limit=10, offset=10)
self.assertEqual(len(album_items.items), 4)
def test_album_items_credits(self):
album_items = self.api.getAlbumItemsCredits(103805723, limit=10)
self.assertEqual(len(album_items.items), 10)
def test_playlist(self):
playlist = self.api.getPlaylist("84974059-76af-406a-aede-ece2b78fa372")
self.assertEqual(playlist.title, "Kanye West Essentials")
+14
View File
@@ -9,6 +9,7 @@ from requests import Session
from tiddl.models.api import (
Album,
AlbumItems,
AlbumItemsCredits,
Artist,
ArtistAlbumsItems,
Favorites,
@@ -107,6 +108,19 @@ class TidalApi:
},
)
def getAlbumItemsCredits(
self, album_id: str | int, limit=LIMITS.ALBUM_ITEMS, offset=0
):
return self.fetch(
AlbumItemsCredits,
f"albums/{album_id}/items/credits",
{
"countryCode": self.country_code,
"limit": ensureLimit(limit, self.LIMITS.ALBUM_ITEMS_MAX),
"offset": offset,
},
)
def getArtist(self, artist_id: str | int):
return self.fetch(
Artist, f"artists/{artist_id}", {"countryCode": self.country_code}
+16 -6
View File
@@ -20,7 +20,7 @@ from tiddl.metadata import addMetadata, Cover
from tiddl.exceptions import ApiError, AuthError
from tiddl.models.constants import TrackArg, ARG_TO_QUALITY
from tiddl.models.resource import Track, Album
from tiddl.models.api import PlaylistItems, AlbumItems
from tiddl.models.api import PlaylistItems, AlbumItemsCredits
SinglesFilter = Literal["none", "only", "include"]
@@ -60,7 +60,12 @@ def DownloadCommand(
api = ctx.obj.getApi()
def downloadTrack(track: Track, file_name: str, cover_data=b""):
def downloadTrack(
track: Track,
file_name: str,
cover_data=b"",
credits: List[AlbumItemsCredits.ItemWithCredits.CreditsEntry] = [],
):
if not track.allowStreaming:
click.echo(
f"{click.style('', 'yellow')} Track {click.style(file_name, 'yellow')} does not allow streaming"
@@ -109,16 +114,18 @@ def DownloadCommand(
if not cover_data and track.album.cover:
cover_data = Cover(track.album.cover).content
addMetadata(full_path, track, cover_data)
addMetadata(full_path, track, cover_data=cover_data, credits=credits)
def downloadAlbum(album: Album):
click.echo(f"★ Album {album.title}")
all_items: List[Union[AlbumItems.VideoItem, AlbumItems.TrackItem]] = []
all_items: List[
Union[AlbumItemsCredits.VideoItem, AlbumItemsCredits.TrackItem]
] = []
offset = 0
while True:
album_items = api.getAlbumItems(album.id, offset=offset)
album_items = api.getAlbumItemsCredits(album.id, offset=offset)
all_items.extend(album_items.items)
if (
@@ -142,7 +149,10 @@ def DownloadCommand(
)
downloadTrack(
track=track, file_name=file_name, cover_data=cover_data
track=track,
file_name=file_name,
cover_data=cover_data,
credits=item.credits,
)
def handleResource(resource: TidalResource):
+72 -22
View File
@@ -3,17 +3,26 @@ import requests
from pathlib import Path
from mutagen.flac import FLAC as MutagenFLAC, Picture
from mutagen.easymp4 import EasyMP4 as MutagenEasyMP4
from mutagen.mp4 import MP4Cover, MP4 as MutagenMP4
from mutagen.flac import FLAC as MutagenFLAC
from mutagen.flac import Picture
from mutagen.mp4 import MP4 as MutagenMP4
from mutagen.mp4 import MP4Cover
from tiddl.models.resource import Track
from tiddl.models.api import AlbumItemsCredits
from typing import List
logger = logging.getLogger(__name__)
def addMetadata(track_path: Path, track: Track, cover_data=b""):
def addMetadata(
track_path: Path,
track: Track,
cover_data=b"",
credits: List[AlbumItemsCredits.ItemWithCredits.CreditsEntry] = [],
):
extension = track_path.suffix
if extension == ".flac":
@@ -23,32 +32,73 @@ def addMetadata(track_path: Path, track: Track, cover_data=b""):
picture.data = cover_data
picture.mime = "image/jpeg"
metadata.add_picture(picture)
metadata["TITLE"] = track.title
metadata["WORK"] = track.title
metadata["TRACKNUMBER"] = str(track.trackNumber)
metadata["DISCNUMBER"] = str(track.volumeNumber)
if track.artist:
metadata["ARTIST"] = track.artist.name
metadata["ARTISTS"] = [artist.name for artist in track.artists]
metadata["ALBUM"] = track.album.title
metadata["ALBUMARTIST"] = ", ".join(
[artist.name.strip() for artist in track.artists]
)
if track.streamStartDate:
metadata["DATE"] = track.streamStartDate.strftime("%Y-%m-%d")
metadata["ORIGINALDATE"] = track.streamStartDate.strftime(
"%Y-%m-%d"
)
metadata["ORIGINALYEAR"] = str(track.streamStartDate.strftime("%Y"))
metadata["COPYRIGHT"] = track.copyright
metadata["ISRC"] = track.isrc
if track.bpm:
metadata["BPM"] = str(track.bpm)
for entry in credits:
metadata[entry.type.upper()] = [
contributor.name for contributor in entry.contributors
]
elif extension == ".m4a":
if cover_data:
metadata = MutagenMP4(track_path)
metadata["covr"] = [MP4Cover(cover_data, imageformat=MP4Cover.FORMAT_JPEG)]
metadata["covr"] = [
MP4Cover(cover_data, imageformat=MP4Cover.FORMAT_JPEG)
]
metadata.save(track_path)
metadata = MutagenEasyMP4(track_path)
metadata.update(
{
"title": track.title,
"tracknumber": str(track.trackNumber),
"discnumber": str(track.volumeNumber),
"copyright": track.copyright,
"albumartist": track.artist.name if track.artist else "",
"artist": ";".join(
[artist.name.strip() for artist in track.artists]
),
"album": track.album.title,
"date": str(track.streamStartDate)
if track.streamStartDate
else "",
"bpm": str(track.bpm or ""),
}
)
else:
raise ValueError(f"Unknown file extension: {extension}")
new_metadata: dict[str, str] = {
"title": track.title,
"trackNumber": str(track.trackNumber),
"discnumber": str(track.volumeNumber),
"copyright": track.copyright,
"albumartist": track.artist.name if track.artist else "",
"artist": ";".join([artist.name.strip() for artist in track.artists]),
"album": track.album.title,
"date": str(track.streamStartDate) if track.streamStartDate else "",
}
metadata.update(new_metadata)
try:
metadata.save(track_path)
except Exception as e:
logger.error(f"Failed to set metadata for {extension}: {e}")
logger.error(f"Failed to add metadata to {track_path}: {e}")
class Cover:
@@ -62,9 +112,7 @@ class Cover:
self.uid = uid
formatted_uid = uid.replace("-", "/")
self.url = (
f"https://resources.tidal.com/images/{formatted_uid}/{size}x{size}.jpg"
)
self.url = f"https://resources.tidal.com/images/{formatted_uid}/{size}x{size}.jpg"
logger.debug((self.uid, self.url))
@@ -74,7 +122,9 @@ class Cover:
req = requests.get(self.url)
if req.status_code != 200:
logger.error(f"could not download cover. ({req.status_code}) {self.url}")
logger.error(
f"could not download cover. ({req.status_code}) {self.url}"
)
return b""
logger.debug(f"got cover: {self.uid}")
+23
View File
@@ -54,6 +54,29 @@ class AlbumItems(Items):
items: List[Union[TrackItem, VideoItem]]
class AlbumItemsCredits(Items):
class ItemWithCredits(BaseModel):
class CreditsEntry(BaseModel):
class Contributor(BaseModel):
name: str
id: Optional[int] = None
type: str
contributors: List[Contributor]
credits: List[CreditsEntry]
class VideoItem(ItemWithCredits):
item: Video
type: ItemType = "video"
class TrackItem(ItemWithCredits):
item: Track
type: ItemType = "track"
items: List[Union[TrackItem, VideoItem]]
class PlaylistItems(Items):
class PlaylistVideoItem(BaseModel):
class PlaylistVideo(Video):