mirror of
https://github.com/zhaarey/AppleMusicDecrypt.git
synced 2025-10-23 15:11:06 +00:00
feat: support playlistSongIndex
This commit is contained in:
@@ -25,6 +25,7 @@ class SongMetadata(BaseModel):
|
||||
record_company: Optional[str] = None
|
||||
upc: Optional[str] = None
|
||||
isrc: Optional[str] = None
|
||||
playlistIndex: Optional[int] = None
|
||||
|
||||
def to_itags_params(self, embed_metadata: list[str]):
|
||||
tags = []
|
||||
@@ -32,6 +33,8 @@ class SongMetadata(BaseModel):
|
||||
if not value:
|
||||
continue
|
||||
if key in embed_metadata and value:
|
||||
if "playlist" in key:
|
||||
continue
|
||||
if key == "cover":
|
||||
continue
|
||||
if key == "lyrics":
|
||||
@@ -60,3 +63,6 @@ class SongMetadata(BaseModel):
|
||||
|
||||
async def get_cover(self, cover_format: str, cover_size: str):
|
||||
self.cover = await get_cover(self.cover_url, cover_format, cover_size)
|
||||
|
||||
def set_playlist_index(self, index: int):
|
||||
self.playlistIndex = index
|
||||
|
||||
@@ -132,3 +132,4 @@ class Datum(BaseModel):
|
||||
|
||||
class PlaylistInfo(BaseModel):
|
||||
data: List[Datum]
|
||||
songIdIndexMapping: dict[str, int] = {}
|
||||
|
||||
14
src/rip.py
14
src/rip.py
@@ -14,7 +14,7 @@ from src.mp4 import extract_media, extract_song, encapsulate, write_metadata
|
||||
from src.save import save
|
||||
from src.types import GlobalAuthParams, Codec
|
||||
from src.url import Song, Album, URLType, Artist, Playlist
|
||||
from src.utils import check_song_exists, if_raw_atmos
|
||||
from src.utils import check_song_exists, if_raw_atmos, playlist_write_song_index
|
||||
|
||||
|
||||
@logger.catch
|
||||
@@ -24,6 +24,8 @@ async def rip_song(song: Song, auth_params: GlobalAuthParams, codec: str, config
|
||||
token = auth_params.anonymousAccessToken
|
||||
song_data = await get_song_info(song.id, token, song.storefront, config.region.language)
|
||||
song_metadata = SongMetadata.parse_from_song_data(song_data)
|
||||
if playlist:
|
||||
song_metadata.set_playlist_index(playlist.songIdIndexMapping.get(song.id))
|
||||
logger.info(f"Ripping song: {song_metadata.artist} - {song_metadata.title}")
|
||||
if not force_save and check_song_exists(song_metadata, config.download, codec, playlist):
|
||||
logger.info(f"Song: {song_metadata.artist} - {song_metadata.title} already exists")
|
||||
@@ -74,13 +76,17 @@ async def rip_album(album: Album, auth_params: GlobalAuthParams, codec: str, con
|
||||
|
||||
async def rip_playlist(playlist: Playlist, auth_params: GlobalAuthParams, codec: str, config: Config, device: Device,
|
||||
force_save: bool = False):
|
||||
playlist_info = await get_playlist_info_and_tracks(playlist.id, auth_params.anonymousAccessToken, playlist.storefront,
|
||||
playlist_info = await get_playlist_info_and_tracks(playlist.id, auth_params.anonymousAccessToken,
|
||||
playlist.storefront,
|
||||
config.region.language)
|
||||
logger.info(f"Ripping Playlist: {playlist_info.data[0].attributes.curatorName} - {playlist_info.data[0].attributes.name}")
|
||||
playlist_info = playlist_write_song_index(playlist_info)
|
||||
logger.info(
|
||||
f"Ripping Playlist: {playlist_info.data[0].attributes.curatorName} - {playlist_info.data[0].attributes.name}")
|
||||
async with asyncio.TaskGroup() as tg:
|
||||
for track in playlist_info.data[0].relationships.tracks.data:
|
||||
song = Song(id=track.id, storefront=playlist.storefront, url="", type=URLType.Song)
|
||||
tg.create_task(rip_song(song, auth_params, codec, config, device, force_save=force_save, playlist=playlist_info))
|
||||
tg.create_task(
|
||||
rip_song(song, auth_params, codec, config, device, force_save=force_save, playlist=playlist_info))
|
||||
logger.info(
|
||||
f"Playlist: {playlist_info.data[0].attributes.curatorName} - {playlist_info.data[0].attributes.name} finished ripping")
|
||||
|
||||
|
||||
15
src/utils.py
15
src/utils.py
@@ -147,11 +147,12 @@ def playlist_metadata_to_params(playlist: PlaylistInfo):
|
||||
"playlistCuratorName": playlist.data[0].attributes.curatorName}
|
||||
|
||||
|
||||
def get_song_name_and_dir_path(codec: str, config: Download, metadata, playlist: PlaylistMeta = None):
|
||||
def get_song_name_and_dir_path(codec: str, config: Download, metadata, playlist: PlaylistInfo = None):
|
||||
if playlist:
|
||||
song_name = config.playlistSongNameFormat.format(codec=codec, **metadata.model_dump(),
|
||||
**playlist_metadata_to_params(playlist))
|
||||
dir_path = Path(config.playlistDirPathFormat.format(codec=codec, **metadata.model_dump(),
|
||||
song_name = config.playlistSongNameFormat.format(codec=codec, playlistSongIndex=metadata.playlistIndex,
|
||||
**metadata.model_dump())
|
||||
dir_path = Path(config.playlistDirPathFormat.format(codec=codec,
|
||||
**metadata.model_dump(),
|
||||
**playlist_metadata_to_params(playlist)))
|
||||
else:
|
||||
song_name = config.songNameFormat.format(codec=codec, **metadata.model_dump())
|
||||
@@ -160,3 +161,9 @@ def get_song_name_and_dir_path(codec: str, config: Download, metadata, playlist:
|
||||
song_name = get_valid_filename(song_name)
|
||||
dir_path = Path(*[get_valid_filename(part) if ":\\" not in part else part for part in dir_path.parts])
|
||||
return song_name, dir_path
|
||||
|
||||
|
||||
def playlist_write_song_index(playlist: PlaylistInfo):
|
||||
for track_index, track in enumerate(playlist.data[0].relationships.tracks.data):
|
||||
playlist.songIdIndexMapping[track.id] = track_index + 1
|
||||
return playlist
|
||||
|
||||
Reference in New Issue
Block a user