Round 5 added a guard so a request for a segment beyond the produced region
answers instead of polling out the full timeout — the route is a SYNC one, so
every such wait parks a threadpool worker and, through `current_user`, a pooled
DB connection. Measured against real ffmpeg, the guard could not fire where it
mattered.
`-hls_segment_filename seg_%v_%d.ts` substitutes the VARIANT first: a dual-audio
session writes seg_0_0.ts (video) and seg_a0_0.ts / seg_a1_0.ts (the `name:aN`
renditions). The regex read those as sequence-first, so every video segment
parsed as segment 0 and every audio segment did not parse at all — the guard was
inert on every multi-audio item, which is most of this library. `_seg()` carried
the same inversion: the warm-up waited 8 s for `seg_1_0.ts`, a name ffmpeg never
writes, on every multi-audio session start AND every seek-restart.
The layout is now one constant plus one parser (`segment_name` /
`_SEG_NAME_RE`), with a test pinning them to each other, instead of three
comments that could drift — and they had drifted, all three ways.
The guard is also a positive rule now. It was a blocklist, so anything that was
not a single-audio segment name was waved through: `GET …/anything.m3u8` still
parked a worker for 30 s, while the route's comment claimed a flood of hopeless
names was what it prevented. A session produces its playlists and its segments
and nothing else. `.vtt` leaves `_HLS_CT` with it — ffmpeg runs with `-sn` and
subtitles are a separate route, so that extension could only ever name a file
that will not arrive.
Also here:
- A 5000-digit segment index reached the client as a 500: `int()` on an
unbounded digit run raises past 4300 digits and only OSError was caught. The
name is bounded on the route and the index in the regex — the same bug class
the subtitle routes were bounded for.
- A session torn down between the wait and the read is a 404, not a 500. `_kill`
rmtrees the directory and no lock spans those two steps; the player's recovery
is keyed on the STATUS, so a 500 skipped the reclaim and ended playback with
"couldn't play" instead of restarting at the playhead. The trigger is ordinary
— `loadSession` awaits the new session before destroying the old hls instance.
- Two comments claimed an open player checkpoints every 10 s "whether or not it
is playing", which is the stated reason the 30 s finished-idle reap is safe.
`flush` sends nothing once the finish latch is up, or before a position is
known. The states it does cover — playing or paused mid-item — are the ones
the rule needs; the comments now say that instead of more.
- The route's tests leave the DB lane: the route reads nothing from the database
since the auto-start was removed, and there those four regressions SKIP on a
machine with no Postgres while pytest prints green.
14 tests, mutation-checked: the old parse, the old warm-up name, the blocklist,
both bounds off the absurd name, the unguarded read, and the route dropping
`proc` — each red on the test that names it.