Support song codec priority list

This commit is contained in:
Rafael Moraes
2026-02-25 18:16:34 -03:00
parent d32781b23f
commit 7281f5c949
5 changed files with 50 additions and 36 deletions
+7 -3
View File
@@ -142,7 +142,7 @@ async def main(config: CliConfig):
song_downloader = AppleMusicSongDownloader(
base_downloader=base_downloader,
interface=song_interface,
codec=config.song_codec,
codec_priority=config.song_codec_piority,
synced_lyrics_format=config.synced_lyrics_format,
no_synced_lyrics=config.no_synced_lyrics,
synced_lyrics_only=config.synced_lyrics_only,
@@ -197,7 +197,8 @@ async def main(config: CliConfig):
)
if not base_downloader.full_mp4decrypt_path and (
config.song_codec not in (SongCodec.AAC_LEGACY, SongCodec.AAC_HE_LEGACY)
config.song_codec_piority
not in (SongCodec.AAC_LEGACY, SongCodec.AAC_HE_LEGACY)
or config.music_video_remux_mode == RemuxMode.MP4BOX
):
missing_music_video_paths.append(
@@ -210,7 +211,10 @@ async def main(config: CliConfig):
+ "\n".join(missing_music_video_paths)
)
if not config.song_codec.is_legacy() and not config.use_wrapper:
if (
any(not codec.is_legacy() for codec in config.song_codec_piority)
and not config.use_wrapper
):
logger.warning(
"You have chosen an experimental song codec "
"without enabling wrapper. "
+6 -6
View File
@@ -362,13 +362,13 @@ class CliConfig:
),
]
# DownloaderSong specific options
song_codec: Annotated[
SongCodec,
song_codec_piority: Annotated[
list[SongCodec],
option(
"--song-codec",
help="Song codec",
default=song_downloader_sig.parameters["codec"].default,
type=SongCodec,
"--song-codec-priority",
help="Comma-separated codec priority",
default=song_downloader_sig.parameters["codec_priority"].default,
type=Csv(SongCodec),
),
]
synced_lyrics_format: Annotated[
+22 -24
View File
@@ -13,7 +13,7 @@ class AppleMusicSongDownloader(AppleMusicBaseDownloader):
self,
base_downloader: AppleMusicBaseDownloader,
interface: AppleMusicSongInterface,
codec: SongCodec = SongCodec.AAC_LEGACY,
codec_priority: SongCodec = [SongCodec.AAC_LEGACY],
synced_lyrics_format: SyncedLyricsFormat = SyncedLyricsFormat.LRC,
no_synced_lyrics: bool = False,
synced_lyrics_only: bool = False,
@@ -22,7 +22,7 @@ class AppleMusicSongDownloader(AppleMusicBaseDownloader):
):
self.__dict__.update(base_downloader.__dict__)
self.interface = interface
self.codec = codec
self.codec_priority = codec_priority
self.synced_lyrics_format = synced_lyrics_format
self.no_synced_lyrics = no_synced_lyrics
self.synced_lyrics_only = synced_lyrics_only
@@ -78,33 +78,31 @@ class AppleMusicSongDownloader(AppleMusicBaseDownloader):
if self.synced_lyrics_only:
return download_item
if self.codec.is_legacy():
download_item.stream_info = await self.interface.get_stream_info_legacy(
for codec in self.codec_priority:
download_item.stream_info = await self.interface.get_stream_info(
codec,
song_metadata,
webplayback,
self.codec,
)
if download_item.stream_info:
break
if download_item.stream_info.audio_track.legacy:
download_item.decryption_key = (
await self.interface.get_decryption_key_legacy(
download_item.stream_info,
self.cdm,
)
)
else:
download_item.stream_info = await self.interface.get_stream_info(
song_metadata,
self.codec,
elif (
not self.use_wrapper
and download_item.stream_info
and download_item.stream_info.audio_track.widevine_pssh
):
download_item.decryption_key = await self.interface.get_decryption_key(
download_item.stream_info,
self.cdm,
)
if (
not self.use_wrapper
and download_item.stream_info
and download_item.stream_info.audio_track.widevine_pssh
):
download_item.decryption_key = await self.interface.get_decryption_key(
download_item.stream_info,
self.cdm,
)
else:
download_item.decryption_key = None
download_item.cover_url_template = self.interface.get_cover_url_template(
song_metadata,
@@ -173,11 +171,11 @@ class AppleMusicSongDownloader(AppleMusicBaseDownloader):
encrypted_path: str,
staged_path: str,
decryption_key: DecryptionKeyAv,
codec: SongCodec,
legacy: bool,
media_id: str,
fairplay_key: str,
):
if self.use_wrapper and not codec.is_legacy():
if self.use_wrapper and not legacy:
await self.decrypt_amdecrypt(
encrypted_path,
staged_path,
@@ -189,7 +187,7 @@ class AppleMusicSongDownloader(AppleMusicBaseDownloader):
encrypted_path,
staged_path,
decryption_key.audio_track.key,
legacy=codec.is_legacy(),
legacy,
)
def get_lyrics_synced_path(self, final_path: str) -> str:
@@ -232,7 +230,7 @@ class AppleMusicSongDownloader(AppleMusicBaseDownloader):
encrypted_path,
download_item.staged_path,
download_item.decryption_key,
self.codec,
download_item.stream_info.audio_track.legacy,
download_item.media_metadata["id"],
download_item.stream_info.audio_track.fairplay_key,
)
+14 -3
View File
@@ -226,6 +226,17 @@ class AppleMusicSongInterface(AppleMusicInterface):
return tags
async def get_stream_info(
self,
codec: SongCodec,
song_metadata: dict | None = None,
webplayback: dict | None = None,
) -> StreamInfoAv | None:
if codec.is_legacy():
return await self._get_stream_info_legacy(webplayback, codec)
else:
return await self._get_stream_info(song_metadata, codec)
async def _get_stream_info(
self,
song_metadata: dict,
codec: SongCodec,
@@ -257,7 +268,7 @@ class AppleMusicSongInterface(AppleMusicInterface):
if playlist is None:
return None
stream_info = StreamInfo()
stream_info = StreamInfo(legacy=False)
stream_info.stream_url = (
f"{m3u8_master_url.rpartition('/')[0]}/{playlist['uri']}"
)
@@ -386,14 +397,14 @@ class AppleMusicSongInterface(AppleMusicInterface):
return key.uri
return None
async def get_stream_info_legacy(
async def _get_stream_info_legacy(
self,
webplayback: dict,
codec: SongCodec,
) -> StreamInfoAv:
flavor = "32:ctrp64" if codec == SongCodec.AAC_HE_LEGACY else "28:ctrp256"
stream_info = StreamInfo()
stream_info = StreamInfo(legacy=True)
stream_info.stream_url = next(
i for i in webplayback["songList"][0]["assets"] if i["flavor"] == flavor
)["URL"]
+1
View File
@@ -121,6 +121,7 @@ class StreamInfo:
codec: str = None
width: int = None
height: int = None
legacy: bool = None
@dataclass