add: processing controls — start/hold, pause/resume, retry
Three lifecycle controls on the dashboard, sharing one /api surface and the status feed: - Start/hold gate (#11): the watcher tracks the queue but holds processing until POST /api/start. Held by default; AV1DAE_AUTOSTART=1 restores start- on-boot. NOTE: flips the previous auto-start default. - Pause/resume the active encode (#10): SIGSTOP/SIGCONT on the ffmpeg process (libsvtav1 is in-process, so one signal suspends all its threads). Resumes exactly where it left off; the tracker excludes paused time from elapsed. - Retry a failed file (#3): POST /api/retry?file=NAME moves it from failed/ back to input/, with a base-name guard against path traversal. /status now reports running + the failed list; snapshots carry a paused flag. Verified live: held queue, retry move, traversal -> 400, and a real encode suspending to process state T on pause and S on resume. Closes #3 Closes #10 Closes #11
This commit is contained in:
+46
-11
@@ -43,16 +43,19 @@ type Stream struct {
|
||||
// Tracker holds live progress for the active job. Safe for concurrent use:
|
||||
// the encode goroutine writes, HTTP/log readers call Snapshot.
|
||||
type Tracker struct {
|
||||
mu sync.RWMutex
|
||||
file string
|
||||
phase string
|
||||
meta *JobMeta
|
||||
streams []Stream
|
||||
totalSec float64 // source duration; 0 until known
|
||||
outTime float64 // encoded position in seconds
|
||||
fps float64
|
||||
speed float64
|
||||
startedAt time.Time
|
||||
mu sync.RWMutex
|
||||
file string
|
||||
phase string
|
||||
meta *JobMeta
|
||||
streams []Stream
|
||||
totalSec float64 // source duration; 0 until known
|
||||
outTime float64 // encoded position in seconds
|
||||
fps float64
|
||||
speed float64
|
||||
startedAt time.Time
|
||||
paused bool
|
||||
pausedAt time.Time // when the current pause began
|
||||
pausedTotal time.Duration // accumulated paused time this job
|
||||
}
|
||||
|
||||
// Snapshot is an immutable view of the tracker for readers.
|
||||
@@ -64,6 +67,7 @@ type Snapshot struct {
|
||||
Percent float64 `json:"percent"`
|
||||
FPS float64 `json:"fps"`
|
||||
Speed float64 `json:"speed"`
|
||||
Paused bool `json:"paused"`
|
||||
ElapsedSec int `json:"elapsed_sec"`
|
||||
ETASec int `json:"eta_sec"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
@@ -83,6 +87,26 @@ func (t *Tracker) Begin(file string) {
|
||||
t.streams = nil
|
||||
t.totalSec, t.outTime, t.fps, t.speed = 0, 0, 0, 0
|
||||
t.startedAt = time.Now()
|
||||
t.paused = false
|
||||
t.pausedAt = time.Time{}
|
||||
t.pausedTotal = 0
|
||||
}
|
||||
|
||||
// SetPaused records pause/resume transitions so elapsed time excludes the
|
||||
// paused span. Idempotent on repeated same-state calls.
|
||||
func (t *Tracker) SetPaused(p bool) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if p == t.paused {
|
||||
return
|
||||
}
|
||||
if p {
|
||||
t.pausedAt = time.Now()
|
||||
} else if !t.pausedAt.IsZero() {
|
||||
t.pausedTotal += time.Since(t.pausedAt)
|
||||
t.pausedAt = time.Time{}
|
||||
}
|
||||
t.paused = p
|
||||
}
|
||||
|
||||
func (t *Tracker) SetPhase(p string) {
|
||||
@@ -129,6 +153,9 @@ func (t *Tracker) Idle() {
|
||||
t.streams = nil
|
||||
t.totalSec, t.outTime, t.fps, t.speed = 0, 0, 0, 0
|
||||
t.startedAt = time.Time{}
|
||||
t.paused = false
|
||||
t.pausedAt = time.Time{}
|
||||
t.pausedTotal = 0
|
||||
}
|
||||
|
||||
func (t *Tracker) Snapshot() Snapshot {
|
||||
@@ -141,10 +168,18 @@ func (t *Tracker) Snapshot() Snapshot {
|
||||
Streams: t.streams,
|
||||
FPS: t.fps,
|
||||
Speed: t.speed,
|
||||
Paused: t.paused,
|
||||
StartedAt: t.startedAt,
|
||||
}
|
||||
if !t.startedAt.IsZero() {
|
||||
s.ElapsedSec = int(time.Since(t.startedAt).Seconds())
|
||||
elapsed := time.Since(t.startedAt) - t.pausedTotal
|
||||
if t.paused && !t.pausedAt.IsZero() {
|
||||
elapsed -= time.Since(t.pausedAt)
|
||||
}
|
||||
if elapsed < 0 {
|
||||
elapsed = 0
|
||||
}
|
||||
s.ElapsedSec = int(elapsed.Seconds())
|
||||
}
|
||||
if t.totalSec > 0 {
|
||||
s.Percent = t.outTime / t.totalSec * 100
|
||||
|
||||
Reference in New Issue
Block a user