Never serve a partial file: it makes Jellyfin transcode

The 12s first-byte grace was the wrong trade and a real play found it
within the hour. A 2-hour upload took 3 minutes to start, played 6
seconds, and stalled. Jellyfin had run ffmpeg with -probesize 1G against
the growing stream and then transcoded to HLS with libx264.

The cause is the container. A fragmented MP4 with empty_moov has no
duration in its header, so the only way to get one is to sum every
fragment -- probing a growing file reads all of it. Jellyfin cannot
establish duration, codec or bitrate, so it abandons direct play and
transcodes a stream it also cannot seek. It was targeting 4.83 Mbps
against a source measured at 3.29: re-encoding a stream that already fit,
because it could not measure it.

The same video once complete reports SupportsDirectPlay with the exact
runtime and bitrate.

So FIRST_BYTE_GRACE defaults to infinite again, with --wait-timeout raised
to 600s for a 2-hour upload. A cold long video is slow to start, which is
accepted: the fetch outlives the request so a retry is instant, and a
retryable stall beats a transcode that wastes a gigabyte and cannot work.
Both failure modes are recorded at the constant in the order measured so
the 12s cap is not reintroduced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-08-13 11:32:12 +01:00
parent c12837cbb9
commit 22d8828080
10 changed files with 514 additions and 46 deletions
+27 -4
View File
@@ -341,14 +341,37 @@ def test_growing_flag_means_no_wait_at_all(tmp_path):
server.close()
def test_status_reports_the_serving_mode_and_grace(manager_factory):
"""/healthz has to show this: it is the difference between 'plays' and
'playback error', and it is otherwise invisible without the unit file."""
def test_the_default_is_to_wait_for_a_complete_file(manager_factory):
"""The default must not serve a partial file. Jellyfin responds to one by
dragging a gigabyte through the proxy to probe a fragmented MP4 that has no
duration in its header, then transcoding a stream it cannot seek: measured
2026-08-13, 3 minutes to start, 6 seconds of video, permanent stall."""
assert proxy.FIRST_BYTE_GRACE == float("inf")
status = manager_factory().status()
assert status["mode"] == "wait-for-complete"
assert status["first_byte_grace_s"] is None
def test_status_is_valid_json_with_no_grace_cap(manager_factory):
"""json.dumps emits `Infinity` for an infinite float, which is not JSON and
breaks any client that parses /healthz strictly."""
payload = json.dumps(manager_factory().status())
assert "Infinity" not in payload
assert json.loads(payload)["first_byte_grace_s"] is None
def test_status_reports_a_finite_grace_when_one_is_set(manager_factory):
manager = manager_factory()
manager.strict = False
manager.first_byte_grace = 12.0
status = manager.status()
assert status["mode"] == "wait-then-stream"
assert status["first_byte_grace_s"] == proxy.FIRST_BYTE_GRACE
assert status["first_byte_grace_s"] == 12.0
def test_status_reports_growing_mode(manager_factory):
growing = manager_factory(growing=True)
assert growing.status()["mode"] == "growing"
assert growing.status()["first_byte_grace_s"] == 0.0