From b826c0266038625c7eaaa7dc33b78fa518dc35a7 Mon Sep 17 00:00:00 2001 From: Stu Leak Date: Wed, 17 Dec 2025 14:47:37 -0500 Subject: [PATCH] Improve snippet progress reporting and speed up striped bars --- internal/ui/queueview.go | 9 ++++--- main.go | 58 +++++++++++++++++++++++++++++++++------- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/internal/ui/queueview.go b/internal/ui/queueview.go index c18ae4f..917ea64 100644 --- a/internal/ui/queueview.go +++ b/internal/ui/queueview.go @@ -51,12 +51,12 @@ func (s *stripedProgress) CreateRenderer() fyne.WidgetRenderer { fillRect := canvas.NewRectangle(applyAlpha(s.color, 200)) stripes := canvas.NewRaster(func(w, h int) image.Image { img := image.NewRGBA(image.Rect(0, 0, w, h)) - light := applyAlpha(s.color, 60) - dark := applyAlpha(s.color, 200) + light := applyAlpha(s.color, 80) + dark := applyAlpha(s.color, 220) for y := 0; y < h; y++ { for x := 0; x < w; x++ { // animate diagonal stripes using offset - if (((x + y) + int(s.offset)) / 6 % 2) == 0 { + if (((x + y) + int(s.offset)) / 4 % 2) == 0 { img.Set(x, y, light) } else { img.Set(x, y, dark) @@ -106,9 +106,10 @@ func (r *stripedProgressRenderer) MinSize() fyne.Size { func (r *stripedProgressRenderer) Refresh() { // small drift to animate stripes - r.bar.offset += 1 + r.bar.offset += 2 r.Layout(r.bg.Size()) canvas.Refresh(r.bg) + canvas.Refresh(r.stripes) } func (r *stripedProgressRenderer) BackgroundColor() color.Color { return color.Transparent } diff --git a/main.go b/main.go index 82cb630..e257001 100644 --- a/main.go +++ b/main.go @@ -3712,10 +3712,6 @@ func (s *appState) executeSnippetJob(ctx context.Context, job *queue.Job, progre center := math.Max(0, src.Duration/2-halfLength) start := fmt.Sprintf("%.2f", center) - if progressCallback != nil { - progressCallback(0) - } - var args []string if useSourceFormat { @@ -3780,7 +3776,7 @@ func (s *appState) executeSnippetJob(ctx context.Context, job *queue.Job, progre } } - args = append(args, "-y", "-hide_banner", "-loglevel", "error", outputPath) + args = append(args, "-y", "-hide_banner", "-loglevel", "error") } else { // Conversion format mode: Use configured conversion settings // This allows previewing what the final converted output will look like @@ -3876,20 +3872,62 @@ func (s *appState) executeSnippetJob(ctx context.Context, job *queue.Job, progre args = append(args, "-c:a", "aac", "-b:a", "192k") } - args = append(args, outputPath) + // Common args appended after progress flags + } + + // Add progress output for live updates (stdout) and finish with output path + args = append(args, "-progress", "pipe:1", "-nostats", outputPath) + + if progressCallback != nil { + progressCallback(0) } logFile, logPath, _ := createConversionLog(inputPath, outputPath, args) cmd := exec.CommandContext(ctx, platformConfig.FFmpegPath, args...) utils.ApplyNoWindow(cmd) - out, err := cmd.CombinedOutput() + stdout, err := cmd.StdoutPipe() + if err != nil { + return fmt.Errorf("snippet stdout pipe: %w", err) + } + var stderr bytes.Buffer + cmd.Stderr = &stderr + + if err := cmd.Start(); err != nil { + return fmt.Errorf("snippet start failed: %w (%s)", err, strings.TrimSpace(stderr.String())) + } + + // Track progress based on snippet length + go func() { + scanner := bufio.NewScanner(stdout) + for scanner.Scan() { + line := scanner.Text() + if logFile != nil { + fmt.Fprintln(logFile, line) + } + if strings.HasPrefix(line, "out_time_ms=") && snippetLength > 0 { + val := strings.TrimPrefix(line, "out_time_ms=") + if ms, err := strconv.ParseFloat(val, 64); err == nil { + currentSec := ms / 1_000_000.0 + pct := (currentSec / float64(snippetLength)) * 100.0 + if pct > 100 { + pct = 100 + } + if progressCallback != nil { + progressCallback(pct) + } + } + } + } + }() + + err = cmd.Wait() if err != nil { if logFile != nil { - fmt.Fprintf(logFile, "\nStatus: failed at %s\nError: %v\nFFmpeg output:\n%s\n", time.Now().Format(time.RFC3339), err, string(out)) + fmt.Fprintf(logFile, "\nStatus: failed at %s\nError: %v\nFFmpeg stderr:\n%s\n", time.Now().Format(time.RFC3339), err, strings.TrimSpace(stderr.String())) _ = logFile.Close() } - return fmt.Errorf("snippet failed: %w\nffmpeg output:\n%s", err, string(out)) + return fmt.Errorf("snippet failed: %w\nFFmpeg stderr:\n%s", err, strings.TrimSpace(stderr.String())) } if logFile != nil { fmt.Fprintf(logFile, "\nStatus: completed at %s\n", time.Now().Format(time.RFC3339)) @@ -3897,7 +3935,7 @@ func (s *appState) executeSnippetJob(ctx context.Context, job *queue.Job, progre job.LogPath = logPath } if progressCallback != nil { - progressCallback(1) + progressCallback(100) } return nil }