From afd1213e5ffa1012539d8318df7af4be6bc9625e Mon Sep 17 00:00:00 2001 From: WorldObservationLog Date: Sun, 19 May 2024 03:36:15 +0800 Subject: [PATCH] fix: aac convent vbr to cbr bug --- src/mp4.py | 27 +++++++++++++++++++++++++-- src/rip.py | 4 +++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/mp4.py b/src/mp4.py index 72da6c6..8c4ee82 100644 --- a/src/mp4.py +++ b/src/mp4.py @@ -87,8 +87,9 @@ def extract_song(raw_song: bytes, codec: str) -> SongInfo: match codec: case Codec.ALAC: alac_atom_name = (Path(tmp_dir.name) / Path(mp4_name).with_suffix('.atom')).absolute() - subprocess.run(f"mp4extract moov/trak/mdia/minf/stbl/stsd/enca[0]/alac {raw_mp4.absolute()} {alac_atom_name}", - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run( + f"mp4extract moov/trak/mdia/minf/stbl/stsd/enca[0]/alac {raw_mp4.absolute()} {alac_atom_name}", + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) with open(alac_atom_name, "rb") as f: decoder_params = f.read() case Codec.AAC | Codec.AAC_DOWNMIX | Codec.AAC_BINAURAL: @@ -209,3 +210,25 @@ def fix_encapsulate(song: bytes) -> bytes: new_song_name.absolute()], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) with open(new_song_name.absolute(), "rb") as f: return f.read() + + +# FFMPEG will overwrite maxBitrate in DecoderConfigDescriptor +# Using raw song's esds box to fix it +# see also https://trac.ffmpeg.org/ticket/4894 +def fix_esds_box(raw_song: bytes, song: bytes) -> bytes: + tmp_dir = TemporaryDirectory() + name = uuid.uuid4().hex + esds_name = Path(tmp_dir.name) / Path(f"{name}.atom") + raw_song_name = Path(tmp_dir.name) / Path(f"{name}_raw.m4a") + song_name = Path(tmp_dir.name) / Path(f"{name}.m4a") + final_song_name = Path(tmp_dir.name) / Path(f"{name}_final.m4a") + with open(raw_song_name.absolute(), "wb") as f: + f.write(raw_song) + with open(song_name.absolute(), "wb") as f: + f.write(song) + subprocess.run( + f"mp4extract moov/trak/mdia/minf/stbl/stsd/enca[0]/esds {raw_song_name.absolute()} {esds_name.absolute()}", stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + subprocess.run(f"mp4edit --replace moov/trak/mdia/minf/stbl/stsd/mp4a/esds:{esds_name.absolute()} {song_name.absolute()} {final_song_name.absolute()}", + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + with open(final_song_name.absolute(), "rb") as f: + return f.read() \ No newline at end of file diff --git a/src/rip.py b/src/rip.py index 32245f0..2844c98 100644 --- a/src/rip.py +++ b/src/rip.py @@ -11,7 +11,7 @@ from src.adb import Device from src.decrypt import decrypt from src.metadata import SongMetadata from src.models import PlaylistInfo -from src.mp4 import extract_media, extract_song, encapsulate, write_metadata, fix_encapsulate +from src.mp4 import extract_media, extract_song, encapsulate, write_metadata, fix_encapsulate, fix_esds_box from src.save import save from src.types import GlobalAuthParams, Codec from src.url import Song, Album, URLType, Artist, Playlist @@ -75,6 +75,8 @@ async def rip_song(song: Song, auth_params: GlobalAuthParams, codec: str, config if not if_raw_atmos(codec, config.download.atmosConventToM4a): metadata_song = write_metadata(song, song_metadata, config.metadata.embedMetadata, config.download.coverFormat) song = fix_encapsulate(metadata_song) + if codec == Codec.AAC or codec == Codec.AAC_DOWNMIX or codec == Codec.AAC_BINAURAL: + song = fix_esds_box(song_info.raw, song) filename = save(song, codec, song_metadata, config.download, playlist) logger.info(f"Song {song_metadata.artist} - {song_metadata.title} saved!") if config.download.afterDownloaded: