Commit Graph
4 Commits
Author SHA1 Message Date
Esa Kataja 6a564aabb2 fix: detect mp4 and other common containers in watcher
The watcher only globbed *.mkv, silently ignoring every other source
container. Expand the glob to a list of common input extensions
(mkv, mp4, m4v, mov, avi, ts, m2ts, mts, mpg, mpeg, vob, webm, wmv,
flv). Downstream is unaffected: workdir naming uses filepath.Ext, the
encoder always emits .mkv output, and the metadata regexes are not
end-anchored.
2026-05-18 08:34:55 +03:00
Esa Kataja 459ab30d94 fix: cancel in-flight encode on SIGINT/SIGTERM via context plumbing
Thread context.Context from main through watcher, encoder, and metadata
clients so a Ctrl-C during a multi-hour encode immediately kills the
ffmpeg/ffprobe/opusenc children instead of waiting for them to finish.

- main: signal.NotifyContext replaces the manual sigChan + done goroutine
- watcher.Start: takes ctx, exits on ctx.Done(); processFn signature is
  now func(context.Context, string) error
- encoder: Transcode and every helper (extractAudio, encodeOpus,
  encodeVideo, GetMediaInfo, GetStreamLanguages, calculateZscaleWidth)
  take ctx; every exec.Command becomes exec.CommandContext so the child
  is SIGKILL'd on cancel
- metadata: FetchMovieMetadata, FetchSeriesMetadata, fetchTVMazeJSON
  take ctx and use http.NewRequestWithContext

Mover stays ctx-free intentionally: a rename is fast enough that
mid-cancel cleanup is the next-restart's problem. processFile's
deferred RemoveAll(workDir) and failToFailed still run after cancel,
so partial output dies in the work dir and the source moves to failed/.
2026-05-16 20:38:26 +03:00
Esa Kataja 6b72ef03ac fix: add mtime+size stability check and failure quarantine to watcher
Two related hardening fixes for the input folder poller (bug #22):

1. Partial-write protection. A freshly-dropped .mkv now has to present
   the same (mtime, size) on two consecutive ticks before processFn is
   called. A file mid-rsync that grows or has a moving mtime is skipped
   until it settles. The per-file (mtime, size) state lives in
   Watcher.seen and is rebuilt from the current glob each tick so the
   map cannot grow unboundedly.

2. Failure quarantine. When processFn returns an error, the file's
   (failedAt, mtime) is recorded in Watcher.failed and subsequent ticks
   skip it until either the backoff elapses or its mtime changes (user
   replaced or touched it). Previously a permanently-broken input -- e.g.
   a video-only mkv that trips the "no audio streams found" path added
   in the multi-audio fix -- would be retried and logged every 15 s
   forever.

Backoff is 5 minutes: comfortably longer than the 10-30 s polling
interval clamp so we are not effectively retrying every tick, but
short enough that an operator fixing the underlying problem by
replacing the file sees it picked up promptly on the next stable scan.
2026-05-16 20:32:16 +03:00
Esa Kataja 0f11c62c4d add: add encoder, mover, watcher, and logger packages 2026-04-04 18:07:37 +03:00