fix(downloads): take the duration from the file when yt-dlp gives none
A Facebook or Instagram reel extracts with no duration, so the asset kept a NULL duration_s although the file plays for minutes, and the Library row had no trim action. The finalize probe now returns the duration with the codecs (the file wins, info fills a gap), and runs before the sidecars so the .nfo runtime gets it too.
This commit is contained in:
@@ -424,10 +424,36 @@ class TestResolveMediaFacts:
|
||||
],
|
||||
},
|
||||
)
|
||||
v, a, w, h = probemod.resolve_media_facts(Path("x"), {"width": None, "height": None})
|
||||
v, a, w, h, _d = probemod.resolve_media_facts(Path("x"), {"width": None, "height": None})
|
||||
assert (v, a, w, h) == ("h264", "aac", 720, 1280)
|
||||
assert mediakind.derive(v, a) == mediakind.VIDEO # NOT 'file'
|
||||
|
||||
def test_the_duration_comes_from_the_file_when_info_has_none(self, monkeypatch):
|
||||
# Facebook reel 257 (E11.5): yt-dlp gave no duration, the file is 128.4 s. A NULL duration hid
|
||||
# the Library row's trim action.
|
||||
self._ffprobe(
|
||||
monkeypatch,
|
||||
{
|
||||
"format": {"format_name": "mov,mp4,m4a", "duration": "128.400000"},
|
||||
"streams": [{"codec_type": "video", "codec_name": "h264"}],
|
||||
},
|
||||
)
|
||||
assert probemod.resolve_media_facts(Path("x"), {"duration": None})[4] == 128
|
||||
# The file wins over a differing info duration, like its codecs do.
|
||||
assert probemod.resolve_media_facts(Path("x"), {"duration": 300})[4] == 128
|
||||
|
||||
def test_info_fills_the_duration_the_probe_could_not_read(self, monkeypatch):
|
||||
self._ffprobe(
|
||||
monkeypatch,
|
||||
{
|
||||
"format": {"format_name": "mov,mp4,m4a"},
|
||||
"streams": [{"codec_type": "video", "codec_name": "h264"}],
|
||||
},
|
||||
)
|
||||
assert probemod.resolve_media_facts(Path("x"), {"duration": 61.7})[4] == 61
|
||||
self._ffprobe(monkeypatch, None)
|
||||
assert probemod.resolve_media_facts(Path("x"), {"duration": 61.7})[4] == 61
|
||||
|
||||
def test_probe_wins_over_a_DIFFERING_populated_info(self, monkeypatch):
|
||||
# Both the probe AND info are populated but DISAGREE (info carries a pre-remux/pre-merge codec).
|
||||
# The finished file on disk is ground truth, so the probe wins outright — pins the precedence a
|
||||
@@ -442,7 +468,7 @@ class TestResolveMediaFacts:
|
||||
],
|
||||
},
|
||||
)
|
||||
v, a, w, h = probemod.resolve_media_facts(
|
||||
v, a, w, h, _d = probemod.resolve_media_facts(
|
||||
Path("x"), {"vcodec": "vp9", "acodec": "opus", "width": 640, "height": 480}
|
||||
)
|
||||
assert (v, a, w, h) == ("h264", "aac", 1920, 1080) # probe, not info
|
||||
@@ -466,7 +492,7 @@ class TestResolveMediaFacts:
|
||||
],
|
||||
},
|
||||
)
|
||||
v, a, w, h = probemod.resolve_media_facts(
|
||||
v, a, w, h, _d = probemod.resolve_media_facts(
|
||||
Path("x"), {"vcodec": "avc1", "acodec": "mp4a", "width": 1280, "height": 720}
|
||||
)
|
||||
assert v is None and a == "mp3" # info's avc1 did NOT win
|
||||
@@ -482,7 +508,7 @@ class TestResolveMediaFacts:
|
||||
"streams": [{"codec_type": "video", "codec_name": "png", "width": 8, "height": 8}],
|
||||
},
|
||||
)
|
||||
v, a, w, h = probemod.resolve_media_facts(
|
||||
v, a, w, h, _d = probemod.resolve_media_facts(
|
||||
Path("x"), {"vcodec": "png", "width": 8, "height": 8}
|
||||
)
|
||||
assert v is None and mediakind.derive(v, a) == mediakind.FILE
|
||||
@@ -490,7 +516,7 @@ class TestResolveMediaFacts:
|
||||
def test_info_fills_in_only_when_ffprobe_could_not_read_the_file(self, monkeypatch):
|
||||
# ffprobe failed entirely (None) → fall back to info so we never regress the working path.
|
||||
self._ffprobe(monkeypatch, None)
|
||||
v, a, w, h = probemod.resolve_media_facts(
|
||||
v, a, w, h, _d = probemod.resolve_media_facts(
|
||||
Path("x"), {"vcodec": "vp9", "acodec": "opus", "width": 1920, "height": 1080}
|
||||
)
|
||||
assert (v, a, w, h) == ("vp9", "opus", 1920, 1080)
|
||||
@@ -498,6 +524,8 @@ class TestResolveMediaFacts:
|
||||
def test_info_none_codec_string_is_treated_as_absent(self, monkeypatch):
|
||||
# yt-dlp writes the literal "none" for a missing stream — it must not become a codec name.
|
||||
self._ffprobe(monkeypatch, None) # probe failed → info path, where the "none" scrub applies
|
||||
v, a, w, h = probemod.resolve_media_facts(Path("x"), {"vcodec": "none", "acodec": "aac"})
|
||||
v, a, w, h, _d = probemod.resolve_media_facts(
|
||||
Path("x"), {"vcodec": "none", "acodec": "aac"}
|
||||
)
|
||||
assert (v, a) == (None, "aac")
|
||||
assert mediakind.derive(v, a) == mediakind.AUDIO
|
||||
|
||||
Reference in New Issue
Block a user