package util import ( "context" "os/exec" "syscall" "time" ) const killWaitDelay = time.Second // KillableCommand builds a command whose cancellation actually stops it, and is // the single place that plumbing lives — every context-aware external command // goes through here. // // yt-dlp, ffmpeg and ffprobe spawn helpers that inherit the output pipe, and // both Wait and Output block reading it until every holder exits. So killing // only the direct child does not end the call; the group signal does, with // WaitDelay as the backstop for whatever survives it. func KillableCommand(ctx context.Context, path string, args ...string) *exec.Cmd { cmd := exec.CommandContext(ctx, path, args...) cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} cmd.Cancel = func() error { return syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) } cmd.WaitDelay = killWaitDelay return cmd }