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

105
main.go
View File

@ -5703,36 +5703,47 @@ 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 { parts := strings.SplitN(line, "=", 2)
timeStr = timeStr[:spaceIdx] if len(parts) != 2 {
} continue
var h, m int }
var s float64 key, val := parts[0], parts[1]
if _, err := fmt.Sscanf(timeStr, "%d:%d:%f", &h, &m, &s); err == nil { if key == "out_time_ms" {
currentTime := float64(h*3600+m*60) + s ms, err := strconv.ParseFloat(val, 64)
progress := startPct + ((currentTime / duration) * (endPct - startPct)) if err != nil {
if progressCallback != nil { continue
progressCallback(progress) }
} currentTime := ms / 1000000.0
} progress := startPct + ((currentTime / duration) * (endPct - startPct))
if progressCallback != nil {
progressCallback(progress)
} }
} }
} }
@ -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,49 +5915,47 @@ 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 duration, ok := cfg["duration"].(float64); ok && duration > 0 {
if strings.Contains(line, "time=") { parts := strings.SplitN(line, "=", 2)
// Get duration from job config if len(parts) != 2 {
if duration, ok := cfg["duration"].(float64); ok && duration > 0 { continue
// Extract time from FFmpeg output }
if idx := strings.Index(line, "time="); idx != -1 { key, val := parts[0], parts[1]
timeStr := line[idx+5:] if key == "out_time_ms" {
if spaceIdx := strings.Index(timeStr, " "); spaceIdx != -1 { ms, err := strconv.ParseFloat(val, 64)
timeStr = timeStr[:spaceIdx] if err != nil {
} continue
}
// Parse time string (HH:MM:SS.ms) currentTime := ms / 1000000.0
var h, m int progress := (currentTime / duration) * 100.0
var s float64 if progress > 100.0 {
if _, err := fmt.Sscanf(timeStr, "%d:%d:%f", &h, &m, &s); err == nil { progress = 100.0
currentTime := float64(h*3600+m*60) + s }
progress := (currentTime / duration) * 100.0 if progressCallback != nil {
if progress > 100.0 { progressCallback(progress)
progress = 100.0
}
if progressCallback != nil {
progressCallback(progress)
}
}
} }
} }
} }