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/.
This commit is contained in:
Esa Kataja
2026-05-16 20:38:26 +03:00
parent 191bc955e4
commit 459ab30d94
4 changed files with 54 additions and 48 deletions
+7 -6
View File
@@ -1,6 +1,7 @@
package watcher
import (
"context"
"fmt"
"os"
"path/filepath"
@@ -60,23 +61,23 @@ func New(inputDir string, intervalSeconds int) *Watcher {
}
}
func (w *Watcher) Start(processFn func(string) error, done chan struct{}) {
func (w *Watcher) Start(ctx context.Context, processFn func(context.Context, string) error) {
ticker := time.NewTicker(w.interval)
defer ticker.Stop()
w.scanAndProcess(processFn)
w.scanAndProcess(ctx, processFn)
for {
select {
case <-done:
case <-ctx.Done():
return
case <-ticker.C:
w.scanAndProcess(processFn)
w.scanAndProcess(ctx, processFn)
}
}
}
func (w *Watcher) scanAndProcess(processFn func(string) error) {
func (w *Watcher) scanAndProcess(ctx context.Context, processFn func(context.Context, string) error) {
files, err := filepath.Glob(filepath.Join(w.inputDir, "*.mkv"))
if err != nil {
fmt.Printf("Error scanning input directory: %v\n", err)
@@ -118,7 +119,7 @@ func (w *Watcher) scanAndProcess(processFn func(string) error) {
continue
}
if err := processFn(file); err != nil {
if err := processFn(ctx, file); err != nil {
fmt.Printf("Error processing %s: %v\n", file, err)
nextFailed[file] = failureRecord{
failedAt: now,