feat(downloads): guarantee every download is browser-playable (mp4/H.264/AAC)
The Download module's purpose is download -> share -> play in-browser, so the output must play in EVERY browser (iOS/Safari/WebKit included). The only universally-safe combo is mp4 + H.264 + AAC/MP3. - formats.normalize() now CLAMPS every spec to that profile (container->mp4, vcodec->None, audio_format opus->m4a) at the single point all paths flow through (picker, stored profile, raw API) — not just by hiding UI options. A source exposing no H.264 is still caught by the worker's browser-safe re-encode, so playability is guaranteed end-to-end. - build_ydl_opts drops the now-unreachable non-compat branches (+ _VCODEC_SORT). - _SIG_VERSION 3->4 so assets cached under an old non-compat spec — or a stale non-H.264 mp4 that slipped the v3 net (the HEVC Facebook dls) — re-derive. - ProfileEditor drops the container/codec pickers and Opus; audio is M4A/MP3. - i18n: remove the now-unused container/vcodec/any keys (HU+EN parity).
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"""build_ydl_opts: the mp4 compat profile must SELECT H.264 (not merely prefer it), so shared
|
||||
downloads play on iOS/Safari, while custom vcodec / non-mp4 profiles opt out. See the iPad AV1
|
||||
broken-player fix."""
|
||||
"""build_ydl_opts: EVERY download is clamped to the mp4/H.264 compat profile (E7 browser-
|
||||
playability guarantee) and must SELECT H.264 (not merely prefer it), so shared downloads play in
|
||||
every browser incl. iOS/Safari. Non-mp4 containers and non-H.264 vcodecs are coerced, not honoured.
|
||||
See the iPad AV1 broken-player fix + E7 format narrowing."""
|
||||
|
||||
from app.downloads import edit as editmod
|
||||
from app.downloads import formats
|
||||
@@ -56,16 +57,37 @@ def test_max_height_caps_every_branch():
|
||||
assert fmt.count("[height<=?720]") >= 3 # applied to each avc1/fallback branch
|
||||
|
||||
|
||||
def test_explicit_vcodec_opts_out_of_avc1_selector():
|
||||
def test_explicit_vcodec_is_clamped_to_the_compat_selector():
|
||||
# E7 browser-playability guarantee: a non-H.264 vcodec no longer opts out — normalize() coerces
|
||||
# vcodec→None, so the avc1 selector (and mp4 merge) is used regardless of what was requested.
|
||||
opts = _opts({"mode": "av", "container": "mp4", "vcodec": "vp9"})
|
||||
assert "vcodec^=avc1" not in opts["format"]
|
||||
assert opts["format"] == "bestvideo+bestaudio/best"
|
||||
assert "vcodec:vp9" in opts["format_sort"]
|
||||
assert opts["format"].startswith("bestvideo[vcodec^=avc1]")
|
||||
assert opts["merge_output_format"] == "mp4"
|
||||
assert "vcodec:vp9" not in opts["format_sort"]
|
||||
|
||||
|
||||
def test_non_mp4_container_opts_out():
|
||||
fmt = _opts({"mode": "av", "container": "webm"})["format"]
|
||||
assert "vcodec^=avc1" not in fmt
|
||||
def test_non_mp4_container_is_clamped_to_mp4():
|
||||
# A webm/mkv request is coerced to the mp4/H.264 compat profile (nothing plays mkv in-browser;
|
||||
# webm/VP9 fails iOS), so the avc1 selector is used and the merge target is mp4.
|
||||
opts = _opts({"mode": "av", "container": "webm"})
|
||||
assert opts["format"].startswith("bestvideo[vcodec^=avc1]")
|
||||
assert opts["merge_output_format"] == "mp4"
|
||||
|
||||
|
||||
def test_normalize_clamps_to_browser_playable():
|
||||
n = formats.normalize({"container": "mkv", "vcodec": "av1", "audio_format": "opus"})
|
||||
assert n["container"] == "mp4"
|
||||
assert n["vcodec"] is None
|
||||
assert n["audio_format"] == "m4a"
|
||||
# A playable audio choice is preserved.
|
||||
assert formats.normalize({"audio_format": "mp3"})["audio_format"] == "mp3"
|
||||
|
||||
|
||||
def test_non_playable_specs_dedup_to_the_compat_identity():
|
||||
# Two requests that differ only in (now-clamped) non-playable fields share ONE cache asset.
|
||||
a = formats.format_sig({"mode": "av", "max_height": 720, "container": "webm", "vcodec": "av1"})
|
||||
b = formats.format_sig({"mode": "av", "max_height": 720, "container": "mp4"})
|
||||
assert a == b
|
||||
|
||||
|
||||
def test_audio_only_unaffected():
|
||||
|
||||
Reference in New Issue
Block a user