fix(dash): handle N_m3u8DL-RE merge and decryption

- Add skip_merge flag for N_m3u8DL-RE to prevent duplicate init data
- Pass content_keys to N_m3u8DL-RE for internal decryption handling
- Use shutil.move() instead of manual merge when skip_merge is True
- Skip manual decryption when N_m3u8DL-RE handles it internally

Fixes audio corruption ("Box 'OG 2' size is too large") when using N_m3u8DL-RE with DASH manifests that have SegmentBase init data. The init segment was being written twice: once by N_m3u8DL-RE during its internal merge, and again by dash.py during post-processing.
This commit is contained in:
Andy
2026-01-16 13:25:34 +00:00
parent b01fc3c8d1
commit a01f335cfc

View File

@@ -5,6 +5,7 @@ import html
import logging import logging
import math import math
import re import re
import shutil
import sys import sys
from copy import copy from copy import copy
from functools import partial from functools import partial
@@ -527,8 +528,16 @@ class DASH:
max_workers=max_workers, max_workers=max_workers,
) )
skip_merge = False
if downloader.__name__ == "n_m3u8dl_re": if downloader.__name__ == "n_m3u8dl_re":
downloader_args.update({"filename": track.id, "track": track}) skip_merge = True
downloader_args.update(
{
"filename": track.id,
"track": track,
"content_keys": drm.content_keys if drm else None,
}
)
debug_logger = get_debug_logger() debug_logger = get_debug_logger()
if debug_logger: if debug_logger:
@@ -543,6 +552,7 @@ class DASH:
"downloader": downloader.__name__, "downloader": downloader.__name__,
"has_drm": bool(track.drm), "has_drm": bool(track.drm),
"drm_types": [drm.__class__.__name__ for drm in (track.drm or [])], "drm_types": [drm.__class__.__name__ for drm in (track.drm or [])],
"skip_merge": skip_merge,
"save_path": str(save_path), "save_path": str(save_path),
"has_init_data": bool(init_data), "has_init_data": bool(init_data),
}, },
@@ -563,6 +573,14 @@ class DASH:
control_file.unlink() control_file.unlink()
segments_to_merge = [x for x in sorted(save_dir.iterdir()) if x.is_file()] segments_to_merge = [x for x in sorted(save_dir.iterdir()) if x.is_file()]
if skip_merge:
# N_m3u8DL-RE handles merging and decryption internally
shutil.move(segments_to_merge[0], save_path)
if drm:
track.drm = None
events.emit(events.Types.TRACK_DECRYPTED, track=track, drm=drm, segment=None)
else:
with open(save_path, "wb") as f: with open(save_path, "wb") as f:
if init_data: if init_data:
f.write(init_data) f.write(init_data)
@@ -591,14 +609,20 @@ class DASH:
track.path = save_path track.path = save_path
events.emit(events.Types.TRACK_DOWNLOADED, track=track) events.emit(events.Types.TRACK_DOWNLOADED, track=track)
if drm: if not skip_merge and drm:
progress(downloaded="Decrypting", completed=0, total=100) progress(downloaded="Decrypting", completed=0, total=100)
drm.decrypt(save_path) drm.decrypt(save_path)
track.drm = None track.drm = None
events.emit(events.Types.TRACK_DECRYPTED, track=track, drm=drm, segment=None) events.emit(events.Types.TRACK_DECRYPTED, track=track, drm=drm, segment=None)
progress(downloaded="Decrypting", advance=100) progress(downloaded="Decrypting", advance=100)
# Clean up empty segment directory
if save_dir.exists() and save_dir.name.endswith("_segments"):
try:
save_dir.rmdir() save_dir.rmdir()
except OSError:
# Directory might not be empty, try removing recursively
shutil.rmtree(save_dir, ignore_errors=True)
progress(downloaded="Downloaded") progress(downloaded="Downloaded")