add: show metadata, streams, and clock-time ETA in status UI
Surface the fetched job metadata (show/episode or movie title, season, episode, release/airdate, media type) and the source audio/subtitle streams through the tracker and /status, and render them in the dashboard's current-job card. Audio channel counts are now probed and shown as 5.1/2.0/etc. ETA now reads as a wall-clock finish time plus remaining duration, e.g. "17:49 (3h52m)". Recent events drop the per-row date for a WhatsApp-style Today/Yesterday/date divider with time-only rows. All dynamic strings are HTML-escaped, since metadata and filenames are user-controlled.
This commit is contained in:
@@ -20,12 +20,34 @@ const (
|
||||
PhaseEncoding = "encoding"
|
||||
)
|
||||
|
||||
// JobMeta is the fetched metadata for the active job, shaped for display.
|
||||
type JobMeta struct {
|
||||
IsSeries bool `json:"is_series"`
|
||||
Title string `json:"title"` // movie title or episode title
|
||||
Collection string `json:"collection,omitempty"` // show name (series)
|
||||
Season string `json:"season,omitempty"`
|
||||
Episode string `json:"episode,omitempty"`
|
||||
DateReleased string `json:"date_released,omitempty"` // release date / airdate
|
||||
MediaType string `json:"media_type,omitempty"`
|
||||
}
|
||||
|
||||
// Stream is one source audio or subtitle stream, for display.
|
||||
type Stream struct {
|
||||
Kind string `json:"kind"` // "audio" | "subtitle"
|
||||
Language string `json:"language,omitempty"`
|
||||
Codec string `json:"codec,omitempty"`
|
||||
Channels int `json:"channels,omitempty"` // audio only
|
||||
Title string `json:"title,omitempty"`
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -37,6 +59,8 @@ type Tracker struct {
|
||||
type Snapshot struct {
|
||||
File string `json:"file"`
|
||||
Phase string `json:"phase"`
|
||||
Meta *JobMeta `json:"meta"`
|
||||
Streams []Stream `json:"streams"`
|
||||
Percent float64 `json:"percent"`
|
||||
FPS float64 `json:"fps"`
|
||||
Speed float64 `json:"speed"`
|
||||
@@ -55,6 +79,8 @@ func (t *Tracker) Begin(file string) {
|
||||
defer t.mu.Unlock()
|
||||
t.file = file
|
||||
t.phase = PhaseProbing
|
||||
t.meta = nil
|
||||
t.streams = nil
|
||||
t.totalSec, t.outTime, t.fps, t.speed = 0, 0, 0, 0
|
||||
t.startedAt = time.Now()
|
||||
}
|
||||
@@ -65,6 +91,20 @@ func (t *Tracker) SetPhase(p string) {
|
||||
t.phase = p
|
||||
}
|
||||
|
||||
// SetMeta records the fetched metadata for the active job.
|
||||
func (t *Tracker) SetMeta(m JobMeta) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.meta = &m
|
||||
}
|
||||
|
||||
// SetStreams records the source audio/subtitle streams for the active job.
|
||||
func (t *Tracker) SetStreams(s []Stream) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
t.streams = s
|
||||
}
|
||||
|
||||
// SetTotal records the source duration in seconds (from ffprobe).
|
||||
func (t *Tracker) SetTotal(seconds float64) {
|
||||
t.mu.Lock()
|
||||
@@ -85,6 +125,8 @@ func (t *Tracker) Idle() {
|
||||
defer t.mu.Unlock()
|
||||
t.file = ""
|
||||
t.phase = PhaseIdle
|
||||
t.meta = nil
|
||||
t.streams = nil
|
||||
t.totalSec, t.outTime, t.fps, t.speed = 0, 0, 0, 0
|
||||
t.startedAt = time.Time{}
|
||||
}
|
||||
@@ -95,6 +137,8 @@ func (t *Tracker) Snapshot() Snapshot {
|
||||
s := Snapshot{
|
||||
File: t.file,
|
||||
Phase: t.phase,
|
||||
Meta: t.meta,
|
||||
Streams: t.streams,
|
||||
FPS: t.fps,
|
||||
Speed: t.speed,
|
||||
StartedAt: t.startedAt,
|
||||
|
||||
Reference in New Issue
Block a user