feat(stream): cache extracted subtitles to a hidden .unarr sidecar
On-demand WebVTT extraction re-ran ffmpeg on every /sub request and, for 50GB+ remuxes, couldn't finish a full text track within the 60s HTTP timeout → the web player got a 500 and no subtitles. Extract each text subtitle ONCE — during the library scan (no HTTP deadline, generous per-file timeout) and write-through on the first on-demand request — into a hidden ".unarr/<name>.s<index>.vtt" sidecar next to the media file. The /sub handler serves a fresh sidecar instantly (mtime-invalidated when the media is replaced), so playback subtitles are instant and huge files work. - mediainfo.sidecar: cache paths, mtime freshness, atomic write, ExtractSubtitleVTT, IsTextSubtitleCodec (shared classifier, mirrors engine + web whitelists). - library.PrewarmSidecars: bounded, idempotent, ctx-cancellable background pass run after every scan (manual + daemon auto-scan). - subtitleHandler: cache-read → hit; miss → extract → write-through. - config: library.cache_subtitles (default true), wired via SetCacheSubtitles. Local-only by design: nothing extracted is uploaded — the sidecar is the user's own content, private to their disk.
This commit is contained in:
parent
7417fad45f
commit
178c16f458
6 changed files with 353 additions and 33 deletions
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/torrentclaw/unarr/internal/agent"
|
||||
"github.com/torrentclaw/unarr/internal/config"
|
||||
"github.com/torrentclaw/unarr/internal/library"
|
||||
"github.com/torrentclaw/unarr/internal/library/mediainfo"
|
||||
)
|
||||
|
||||
func newScanCmd() *cobra.Command {
|
||||
|
|
@ -139,6 +140,20 @@ func runScan(dirPath string, workers int, ffprobePath string, noSync bool) error
|
|||
return enc.Encode(cache)
|
||||
}
|
||||
|
||||
// Pre-extract subtitle sidecars (text subs → WebVTT in a hidden ".unarr" dir)
|
||||
// so playback gets instant subtitles and huge remuxes never hit the on-demand
|
||||
// timeout. Best-effort + Ctrl-C interruptible (the scan itself is already saved).
|
||||
if cfg.Library.CacheSubtitles {
|
||||
if ff, err := mediainfo.ResolveFFmpeg(cfg.Library.FFmpegPath); err == nil {
|
||||
fmt.Fprintf(os.Stderr, " Pre-extracting subtitles to cache… (Ctrl-C to skip)\n")
|
||||
library.PrewarmSidecars(ctx, cache, library.PrewarmOptions{
|
||||
FFmpegPath: ff,
|
||||
CacheSubtitles: true,
|
||||
Workers: 2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Sync to server
|
||||
if !noSync {
|
||||
return syncToServer(ctx, cfg, cache)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue