|
|
@ -61,7 +61,7 @@ async def extract_media(m3u8_url: str, codec: str, song_metadata: SongMetadata, |
|
|
|
return stream.segment_map[0].absolute_uri, keys, selected_codec |
|
|
|
return stream.segment_map[0].absolute_uri, keys, selected_codec |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def extract_song(raw_song: bytes, codec: str) -> SongInfo: |
|
|
|
def extract_song(raw_song: bytes, codec: str) -> SongInfo: |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
mp4_name = uuid.uuid4().hex |
|
|
|
mp4_name = uuid.uuid4().hex |
|
|
|
raw_mp4 = Path(tmp_dir.name) / Path(f"{mp4_name}.mp4") |
|
|
|
raw_mp4 = Path(tmp_dir.name) / Path(f"{mp4_name}.mp4") |
|
|
@ -118,7 +118,7 @@ async def extract_song(raw_song: bytes, codec: str) -> SongInfo: |
|
|
|
return SongInfo(codec=codec, raw=raw_song, samples=samples, nhml=raw_nhml, decoderParams=decoder_params) |
|
|
|
return SongInfo(codec=codec, raw=raw_song, samples=samples, nhml=raw_nhml, decoderParams=decoder_params) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def encapsulate(song_info: SongInfo, decrypted_media: bytes, atmos_convent: bool) -> bytes: |
|
|
|
def encapsulate(song_info: SongInfo, decrypted_media: bytes, atmos_convent: bool) -> bytes: |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
name = uuid.uuid4().hex |
|
|
|
name = uuid.uuid4().hex |
|
|
|
media = Path(tmp_dir.name) / Path(name).with_suffix(".media") |
|
|
|
media = Path(tmp_dir.name) / Path(name).with_suffix(".media") |
|
|
@ -170,7 +170,7 @@ async def encapsulate(song_info: SongInfo, decrypted_media: bytes, atmos_convent |
|
|
|
return final_song |
|
|
|
return final_song |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def write_metadata(song: bytes, metadata: SongMetadata, embed_metadata: list[str], cover_format: str) -> bytes: |
|
|
|
def write_metadata(song: bytes, metadata: SongMetadata, embed_metadata: list[str], cover_format: str) -> bytes: |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
name = uuid.uuid4().hex |
|
|
|
name = uuid.uuid4().hex |
|
|
|
song_name = Path(tmp_dir.name) / Path(f"{name}.m4a") |
|
|
|
song_name = Path(tmp_dir.name) / Path(f"{name}.m4a") |
|
|
@ -199,7 +199,7 @@ async def write_metadata(song: bytes, metadata: SongMetadata, embed_metadata: li |
|
|
|
# There are suspected errors in M4A files encapsulated by MP4Box and GPAC, |
|
|
|
# There are suspected errors in M4A files encapsulated by MP4Box and GPAC, |
|
|
|
# causing some applications to be unable to correctly process Metadata (such as Android.media, Salt Music) |
|
|
|
# causing some applications to be unable to correctly process Metadata (such as Android.media, Salt Music) |
|
|
|
# Using FFMPEG re-encapsulating solves this problem |
|
|
|
# Using FFMPEG re-encapsulating solves this problem |
|
|
|
async def fix_encapsulate(song: bytes) -> bytes: |
|
|
|
def fix_encapsulate(song: bytes) -> bytes: |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
name = uuid.uuid4().hex |
|
|
|
name = uuid.uuid4().hex |
|
|
|
song_name = Path(tmp_dir.name) / Path(f"{name}.m4a") |
|
|
|
song_name = Path(tmp_dir.name) / Path(f"{name}.m4a") |
|
|
@ -217,7 +217,7 @@ async def fix_encapsulate(song: bytes) -> bytes: |
|
|
|
# FFMPEG will overwrite maxBitrate in DecoderConfigDescriptor |
|
|
|
# FFMPEG will overwrite maxBitrate in DecoderConfigDescriptor |
|
|
|
# Using raw song's esds box to fix it |
|
|
|
# Using raw song's esds box to fix it |
|
|
|
# see also https://trac.ffmpeg.org/ticket/4894 |
|
|
|
# see also https://trac.ffmpeg.org/ticket/4894 |
|
|
|
async def fix_esds_box(raw_song: bytes, song: bytes) -> bytes: |
|
|
|
def fix_esds_box(raw_song: bytes, song: bytes) -> bytes: |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
tmp_dir = TemporaryDirectory() |
|
|
|
name = uuid.uuid4().hex |
|
|
|
name = uuid.uuid4().hex |
|
|
|
esds_name = Path(tmp_dir.name) / Path(f"{name}.atom") |
|
|
|
esds_name = Path(tmp_dir.name) / Path(f"{name}.atom") |
|
|
|