Files
unshackle/tests/test_rolling_merge.py
T
imSp4rky 6e681af513 feat(manifests): extend merge_segments rolling merge to HLS and ISM
RollingMerge takes the segment path and an append hook. ISM synthesises the init from segment 0 and converts PIFF per fragment; HLS qualifies only with one init, one key and no discontinuity. Both parsers check the merge reached every segment, and resume is decided per track.
2026-09-21 20:53:37 -06:00

52 lines
1.7 KiB
Python

import io
import random
from concurrent.futures import Future
from pathlib import Path
from unshackle.core.manifests.dash import RollingMerge, append_segment
def write_segments(save_dir: Path, payloads: list[bytes]) -> int:
save_dir.mkdir()
width = len(str(len(payloads)))
for i, payload in enumerate(payloads):
(save_dir / f"{i:0{width}d}.mp4").write_bytes(payload)
return width
def expected(init: bytes, payloads: list[bytes], is_text_subtitle: bool, tmp_path: Path) -> bytes:
scratch = tmp_path / "scratch"
width = write_segments(scratch, payloads)
out = io.BytesIO()
out.write(init)
for i in range(len(payloads)):
append_segment(out, scratch / f"{i:0{width}d}.mp4", is_text_subtitle)
return out.getvalue()
def test_out_of_order_segments_match_whole_merge(tmp_path: Path) -> None:
rng = random.Random(7)
payloads = [rng.randbytes(rng.randint(1, 4096)) for _ in range(23)]
init = b"ftyp-moov"
want = expected(init, payloads, False, tmp_path)
save_dir = tmp_path / "segments"
width = write_segments(save_dir, payloads)
dst = io.BytesIO()
dst.write(init)
merger = RollingMerge(dst)
order = list(range(len(payloads)))
rng.shuffle(order)
for index in order:
future: Future = Future()
future.set_result(None)
merger.add(index, save_dir / f"{index:0{width}d}.mp4", future if index % 2 else None)
# the cursor never rests on a segment that is ready: every contiguous run is drained
assert merger.merged not in merger.ready
assert merger.merged == len(payloads)
assert dst.getvalue() == want
assert dst.getvalue().startswith(init)
assert list(save_dir.iterdir()) == []