fix(upscale): report ffmpeg progress via pipe

This commit is contained in:
Stu Leak 2026-01-06 16:49:39 -05:00
parent c517ec09a2
commit 784d6cba52

81
main.go
View File

@ -5703,39 +5703,50 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
} }
runFFmpegWithProgress := func(args []string, duration float64, startPct, endPct float64) error { runFFmpegWithProgress := func(args []string, duration float64, startPct, endPct float64) error {
if len(args) > 0 {
last := args[len(args)-1]
args = append(args[:len(args)-1], "-progress", "pipe:1", "-nostats", last)
}
cmd := exec.CommandContext(ctx, utils.GetFFmpegPath(), args...) cmd := exec.CommandContext(ctx, utils.GetFFmpegPath(), args...)
utils.ApplyNoWindow(cmd) utils.ApplyNoWindow(cmd)
stderr, err := cmd.StderrPipe() stdout, err := cmd.StdoutPipe()
if err != nil { if err != nil {
return fmt.Errorf("failed to create stderr pipe: %w", err) return fmt.Errorf("failed to create stdout pipe: %w", err)
}
if logFile != nil {
cmd.Stderr = logFile
} else {
cmd.Stderr = io.Discard
} }
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start ffmpeg: %w", err) return fmt.Errorf("failed to start ffmpeg: %w", err)
} }
scanner := bufio.NewScanner(stderr) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
if logFile != nil { if logFile != nil {
fmt.Fprintln(logFile, line) fmt.Fprintln(logFile, line)
} }
if strings.Contains(line, "time=") && duration > 0 { if duration <= 0 {
if idx := strings.Index(line, "time="); idx != -1 { continue
timeStr := line[idx+5:]
if spaceIdx := strings.Index(timeStr, " "); spaceIdx != -1 {
timeStr = timeStr[:spaceIdx]
} }
var h, m int parts := strings.SplitN(line, "=", 2)
var s float64 if len(parts) != 2 {
if _, err := fmt.Sscanf(timeStr, "%d:%d:%f", &h, &m, &s); err == nil { continue
currentTime := float64(h*3600+m*60) + s }
key, val := parts[0], parts[1]
if key == "out_time_ms" {
ms, err := strconv.ParseFloat(val, 64)
if err != nil {
continue
}
currentTime := ms / 1000000.0
progress := startPct + ((currentTime / duration) * (endPct - startPct)) progress := startPct + ((currentTime / duration) * (endPct - startPct))
if progressCallback != nil { if progressCallback != nil {
progressCallback(progress) progressCallback(progress)
} }
} }
} }
}
}
return cmd.Wait() return cmd.Wait()
} }
@ -5895,6 +5906,8 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
"-crf", strconv.Itoa(crfValue), "-crf", strconv.Itoa(crfValue),
"-pix_fmt", "yuv420p", "-pix_fmt", "yuv420p",
"-c:a", "copy", "-c:a", "copy",
"-progress", "pipe:1",
"-nostats",
outputPath, outputPath,
) )
@ -5902,41 +5915,41 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
cmd := exec.CommandContext(ctx, utils.GetFFmpegPath(), args...) cmd := exec.CommandContext(ctx, utils.GetFFmpegPath(), args...)
utils.ApplyNoWindow(cmd) utils.ApplyNoWindow(cmd)
// Create progress reader for stderr stdout, err := cmd.StdoutPipe()
stderr, err := cmd.StderrPipe()
if err != nil { if err != nil {
return fmt.Errorf("failed to create stderr pipe: %w", err) return fmt.Errorf("failed to create stdout pipe: %w", err)
}
if logFile != nil {
cmd.Stderr = logFile
} else {
cmd.Stderr = io.Discard
} }
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to start upscale: %w", err) return fmt.Errorf("failed to start upscale: %w", err)
} }
// Parse progress from FFmpeg stderr // Parse progress from FFmpeg stdout (-progress pipe:1)
go func() { go func() {
scanner := bufio.NewScanner(stderr) scanner := bufio.NewScanner(stdout)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
if logFile != nil { if logFile != nil {
fmt.Fprintln(logFile, line) fmt.Fprintln(logFile, line)
} }
// Parse progress from "time=00:01:23.45"
if strings.Contains(line, "time=") {
// Get duration from job config
if duration, ok := cfg["duration"].(float64); ok && duration > 0 { if duration, ok := cfg["duration"].(float64); ok && duration > 0 {
// Extract time from FFmpeg output parts := strings.SplitN(line, "=", 2)
if idx := strings.Index(line, "time="); idx != -1 { if len(parts) != 2 {
timeStr := line[idx+5:] continue
if spaceIdx := strings.Index(timeStr, " "); spaceIdx != -1 {
timeStr = timeStr[:spaceIdx]
} }
key, val := parts[0], parts[1]
// Parse time string (HH:MM:SS.ms) if key == "out_time_ms" {
var h, m int ms, err := strconv.ParseFloat(val, 64)
var s float64 if err != nil {
if _, err := fmt.Sscanf(timeStr, "%d:%d:%f", &h, &m, &s); err == nil { continue
currentTime := float64(h*3600+m*60) + s }
currentTime := ms / 1000000.0
progress := (currentTime / duration) * 100.0 progress := (currentTime / duration) * 100.0
if progress > 100.0 { if progress > 100.0 {
progress = 100.0 progress = 100.0
@ -5947,8 +5960,6 @@ func (s *appState) executeUpscaleJob(ctx context.Context, job *queue.Job, progre
} }
} }
} }
}
}
}() }()
err = cmd.Wait() err = cmd.Wait()