feat: keep creation_time

pull/13/head
WorldObservationLog 5 months ago
parent 120496d431
commit 9081672546
  1. 24
      src/mp4.py
  2. 3
      src/rip.py
  3. 3
      src/types.py
  4. 6
      src/utils.py

@ -1,7 +1,6 @@
import subprocess import subprocess
import sys import sys
import uuid import uuid
from datetime import datetime
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from tempfile import TemporaryDirectory from tempfile import TemporaryDirectory
@ -16,7 +15,7 @@ from src.api import download_m3u8
from src.exceptions import CodecNotFoundException from src.exceptions import CodecNotFoundException
from src.metadata import SongMetadata from src.metadata import SongMetadata
from src.types import * from src.types import *
from src.utils import find_best_codec, get_codec_from_codec_id, get_suffix from src.utils import find_best_codec, get_codec_from_codec_id, get_suffix, convent_mac_timestamp_to_datetime
def if_shell(): def if_shell():
@ -109,6 +108,7 @@ async def extract_song(raw_song: bytes, codec: str) -> SongInfo:
moofs = info_xml.find_all("MovieFragmentBox") moofs = info_xml.find_all("MovieFragmentBox")
nhnt_sample_number = 0 nhnt_sample_number = 0
nhnt_samples = {} nhnt_samples = {}
params = {}
for sample in nhml.find_all("NHNTSample"): for sample in nhml.find_all("NHNTSample"):
nhnt_samples.update({int(sample.get("number")): sample}) nhnt_samples.update({int(sample.get("number")): sample})
for i, moof in enumerate(moofs): for i, moof in enumerate(moofs):
@ -122,8 +122,11 @@ async def extract_song(raw_song: bytes, codec: str) -> SongInfo:
sample_data = media.read(int(nhnt_sample.get("dataLength"))) sample_data = media.read(int(nhnt_sample.get("dataLength")))
duration = int(nhnt_sample.get("duration")) duration = int(nhnt_sample.get("duration"))
samples.append(SampleInfo(descIndex=index, data=sample_data, duration=int(duration))) samples.append(SampleInfo(descIndex=index, data=sample_data, duration=int(duration)))
mvhd = info_xml.find("MovieHeaderBox")
params.update({"CreationTime": convent_mac_timestamp_to_datetime(int(mvhd.get("CreationTime"))),
"ModificationTime": convent_mac_timestamp_to_datetime(int(mvhd.get("ModificationTime")))})
tmp_dir.cleanup() tmp_dir.cleanup()
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, params=params)
async def encapsulate(song_info: SongInfo, decrypted_media: bytes, atmos_convent: bool) -> bytes: async def encapsulate(song_info: SongInfo, decrypted_media: bytes, atmos_convent: bool) -> bytes:
@ -178,7 +181,8 @@ 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: async def write_metadata(song: bytes, metadata: SongMetadata, embed_metadata: list[str],
cover_format: str, params: dict[str, Any]) -> 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")
@ -190,12 +194,10 @@ async def write_metadata(song: bytes, metadata: SongMetadata, embed_metadata: li
absolute_cover_path = cover_path.absolute() absolute_cover_path = cover_path.absolute()
with open(cover_path.absolute(), "wb") as f: with open(cover_path.absolute(), "wb") as f:
f.write(metadata.cover) f.write(metadata.cover)
if metadata.created: subprocess.run(["mp4box",
time = datetime.strptime(metadata.created, "%Y-%m-%d").strftime("%d/%m/%Y") "-time", params.get("CreationTime").strftime("%d/%m/%Y-%H:%M:%S"),
else: "-mtime", params.get("ModificationTime").strftime("%d/%m/%Y-%H:%M:%S"), "-keep-utc",
time = "" "-name", f"1={metadata.title}", "-itags", ":".join(["tool=", f"cover={absolute_cover_path}",
subprocess.run(["mp4box", "-time", time, "-mtime", time, "-name", f"1={metadata.title}", "-itags",
":".join(["tool=", f"cover={absolute_cover_path}",
metadata.to_itags_params(embed_metadata)]), metadata.to_itags_params(embed_metadata)]),
song_name.absolute()], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) song_name.absolute()], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
with open(song_name.absolute(), "rb") as f: with open(song_name.absolute(), "rb") as f:
@ -215,7 +217,7 @@ async def fix_encapsulate(song: bytes) -> bytes:
with open(song_name.absolute(), "wb") as f: with open(song_name.absolute(), "wb") as f:
f.write(song) f.write(song)
subprocess.run( subprocess.run(
f"ffmpeg -y -i {song_name.absolute()} -fflags +bitexact -c:a copy -c:v copy {new_song_name.absolute()}", f"ffmpeg -y -i {song_name.absolute()} -fflags +bitexact -map_metadata 0 -c:a copy -c:v copy {new_song_name.absolute()}",
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=if_shell()) stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, shell=if_shell())
with open(new_song_name.absolute(), "rb") as f: with open(new_song_name.absolute(), "rb") as f:
encapsulated_song = f.read() encapsulated_song = f.read()

@ -99,7 +99,8 @@ async def rip_song(song: Song, auth_params: GlobalAuthParams, codec: str, config
decrypted_song = await decrypt(song_info, keys, song_data, device) decrypted_song = await decrypt(song_info, keys, song_data, device)
song = await encapsulate(song_info, decrypted_song, config.download.atmosConventToM4a) song = await encapsulate(song_info, decrypted_song, config.download.atmosConventToM4a)
if not if_raw_atmos(codec, config.download.atmosConventToM4a): if not if_raw_atmos(codec, config.download.atmosConventToM4a):
metadata_song = await write_metadata(song, song_metadata, config.metadata.embedMetadata, config.download.coverFormat) metadata_song = await write_metadata(song, song_metadata, config.metadata.embedMetadata,
config.download.coverFormat, song_info.params)
song = await fix_encapsulate(metadata_song) song = await fix_encapsulate(metadata_song)
if codec == Codec.AAC or codec == Codec.AAC_DOWNMIX or codec == Codec.AAC_BINAURAL: if codec == Codec.AAC or codec == Codec.AAC_DOWNMIX or codec == Codec.AAC_BINAURAL:
song = await fix_esds_box(song_info.raw, song) song = await fix_esds_box(song_info.raw, song)

@ -1,4 +1,4 @@
from typing import Optional from typing import Optional, Any
from pydantic import BaseModel from pydantic import BaseModel
@ -18,6 +18,7 @@ class SongInfo(BaseModel):
samples: list[SampleInfo] samples: list[SampleInfo]
nhml: str nhml: str
decoderParams: Optional[bytes] = None decoderParams: Optional[bytes] = None
params: dict[str, Any]
class Codec: class Codec:

@ -1,6 +1,7 @@
import asyncio import asyncio
import sys import sys
import time import time
from datetime import datetime, timedelta
from itertools import islice from itertools import islice
from pathlib import Path from pathlib import Path
@ -176,3 +177,8 @@ def playlist_write_song_index(playlist: PlaylistInfo):
for track_index, track in enumerate(playlist.data[0].relationships.tracks.data): for track_index, track in enumerate(playlist.data[0].relationships.tracks.data):
playlist.songIdIndexMapping[track.id] = track_index + 1 playlist.songIdIndexMapping[track.id] = track_index + 1
return playlist return playlist
def convent_mac_timestamp_to_datetime(timestamp: int):
d = datetime.strptime("01-01-1904", "%m-%d-%Y")
return d + timedelta(seconds=timestamp)

Loading…
Cancel
Save