feat(stream): serve embedded text subtitles as on-demand WebVTT

Add GET /sub?p=&i=&t= that extracts an embedded text subtitle stream to
WebVTT via ffmpeg (-map 0:s:N -c:s webvtt), token-gated with a per-track
sub:<sha256(path)>:<index> scope. The web player attaches these as
external <track>s for both direct-play and HLS, native and hls.js.

Removes the old per-session extraction path (extractSubtitles,
ServeSubtitle, manifest SUBTITLES renditions, subs/ mkdir, Close() wait):
native HLS playback never surfaced manifest subs, so that work was wasted.
The on-demand /sub endpoint is now the single subtitle source.
This commit is contained in:
Deivid Soto 2026-06-01 23:50:39 +02:00
parent 08cb58073d
commit 7417fad45f
4 changed files with 126 additions and 202 deletions

View file

@ -60,6 +60,17 @@ func streamScopeThumb(filePath string) string {
return "thumb:" + hex.EncodeToString(sum[:])
}
// streamScopeSub is the token scope for on-demand WebVTT extraction of one text
// subtitle stream from a specific file (the /sub endpoint, used identically by
// direct-play and HLS so subtitles are consistent across both). Binds the file
// path's SHA-256 + the subtitle stream index, so a leaked URL exposes only that
// one track of that one file. The web mints the matching scope in
// src/lib/stream-token.ts (streamScopeSub), byte-for-byte.
func streamScopeSub(filePath string, index int) string {
sum := sha256.Sum256([]byte(filePath))
return "sub:" + hex.EncodeToString(sum[:]) + ":" + strconv.Itoa(index)
}
// newStreamSecret returns 32 cryptographically-random bytes used to sign stream
// tokens for the lifetime of the daemon. Regenerated each start, so tokens from
// a previous run stop validating (the web re-resolves the URL on demand).