Add thumbnail progress updates
This commit is contained in:
parent
8994d2020b
commit
f9161de1a9
|
|
@ -1,6 +1,8 @@
|
|||
package thumbnail
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
|
@ -30,6 +32,7 @@ type Config struct {
|
|||
Rows int // Contact sheet rows (if ContactSheet=true)
|
||||
ShowTimestamp bool // Overlay timestamp on thumbnails
|
||||
ShowMetadata bool // Show metadata header on contact sheet
|
||||
Progress func(float64)
|
||||
}
|
||||
|
||||
// Generator creates thumbnails from videos
|
||||
|
|
@ -322,6 +325,7 @@ func (g *Generator) generateIndividual(ctx context.Context, config Config, durat
|
|||
|
||||
// Calculate timestamps
|
||||
timestamps := g.calculateTimestamps(config, duration)
|
||||
total := len(timestamps)
|
||||
|
||||
// Generate each thumbnail
|
||||
for i, ts := range timestamps {
|
||||
|
|
@ -361,6 +365,9 @@ func (g *Generator) generateIndividual(ctx context.Context, config Config, durat
|
|||
Height: thumbHeight,
|
||||
Size: fi.Size(),
|
||||
})
|
||||
if config.Progress != nil && total > 0 {
|
||||
config.Progress((float64(i+1) / float64(total)) * 100)
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnails, nil
|
||||
|
|
@ -425,9 +432,16 @@ func (g *Generator) generateContactSheet(ctx context.Context, config Config, dur
|
|||
|
||||
args = append(args, outputPath)
|
||||
|
||||
cmd := exec.CommandContext(ctx, g.FFmpegPath, args...)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("failed to generate contact sheet: %w", err)
|
||||
if config.Progress != nil {
|
||||
args = append(args, "-progress", "pipe:1", "-nostats")
|
||||
if err := runFFmpegWithProgress(ctx, g.FFmpegPath, args, duration, config.Progress); err != nil {
|
||||
return "", fmt.Errorf("failed to generate contact sheet: %w", err)
|
||||
}
|
||||
} else {
|
||||
cmd := exec.CommandContext(ctx, g.FFmpegPath, args...)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return "", fmt.Errorf("failed to generate contact sheet: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return outputPath, nil
|
||||
|
|
@ -551,6 +565,65 @@ func escapeFilterPath(path string) string {
|
|||
return escaped
|
||||
}
|
||||
|
||||
func runFFmpegWithProgress(ctx context.Context, ffmpegPath string, args []string, totalDuration float64, progress func(float64)) error {
|
||||
cmd := exec.CommandContext(ctx, ffmpegPath, args...)
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ffmpeg stdout pipe: %w", err)
|
||||
}
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if progress != nil {
|
||||
progress(0)
|
||||
}
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return fmt.Errorf("ffmpeg start failed: %w (%s)", err, strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
|
||||
go func() {
|
||||
if progress == nil || totalDuration <= 0 {
|
||||
return
|
||||
}
|
||||
scanner := bufio.NewScanner(stdout)
|
||||
var lastPct float64
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
if parts[0] != "out_time_ms" {
|
||||
continue
|
||||
}
|
||||
if ms, err := strconv.ParseFloat(parts[1], 64); err == nil {
|
||||
currentSec := ms / 1000000.0
|
||||
pct := (currentSec / totalDuration) * 100
|
||||
if pct > 100 {
|
||||
pct = 100
|
||||
}
|
||||
if pct-lastPct >= 0.5 || pct >= 100 {
|
||||
lastPct = pct
|
||||
progress(pct)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
err = cmd.Wait()
|
||||
if progress != nil {
|
||||
progress(100)
|
||||
}
|
||||
if err != nil {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
return fmt.Errorf("ffmpeg failed: %w (%s)", err, strings.TrimSpace(stderr.String()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculateTimestamps generates timestamps for thumbnail extraction
|
||||
func (g *Generator) calculateTimestamps(config Config, duration float64) []float64 {
|
||||
var timestamps []float64
|
||||
|
|
|
|||
|
|
@ -452,6 +452,11 @@ func (s *appState) executeThumbJob(ctx context.Context, job *queue.Job, progress
|
|||
Rows: rows,
|
||||
ShowTimestamp: showTimestamp,
|
||||
ShowMetadata: contactSheet,
|
||||
Progress: func(pct float64) {
|
||||
if progressCallback != nil {
|
||||
progressCallback(pct)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
result, err := generator.Generate(ctx, config)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user