diff options
Diffstat (limited to 'internal/thumb')
| -rw-r--r-- | internal/thumb/thumb.go | 13 | ||||
| -rw-r--r-- | internal/thumb/thumb_test.go | 38 |
2 files changed, 34 insertions, 17 deletions
diff --git a/internal/thumb/thumb.go b/internal/thumb/thumb.go index 53a139b..8b49e41 100644 --- a/internal/thumb/thumb.go +++ b/internal/thumb/thumb.go @@ -39,15 +39,22 @@ func (g *FFmpegGenerator) Generate(ctx context.Context, inputPath, outputPath st } } - cmd := g.execer(ctx, "ffmpeg", - "-ss", fmt.Sprintf("%.3f", offset), - "-i", inputPath, + // Build args: only add -ss when we have a real duration (video). + // For static images, -ss before -i produces no output frame on some + // ffmpeg versions (it skips past the single image2 frame). + args := []string{"-i", inputPath} + if duration > 0 { + // Prepend -ss before -i for fast seek when we have a video. + args = append([]string{"-ss", fmt.Sprintf("%.3f", offset)}, args...) + } + args = append(args, "-vf", "scale=320:-1", "-frames:v", "1", "-q:v", "2", "-y", outputPath, ) + cmd := g.execer(ctx, "ffmpeg", args...) if err := cmd.Run(); err != nil { return fmt.Errorf("ffmpeg generate thumbnail for %s: %w", inputPath, err) } diff --git a/internal/thumb/thumb_test.go b/internal/thumb/thumb_test.go index d6272a9..3ed9dfd 100644 --- a/internal/thumb/thumb_test.go +++ b/internal/thumb/thumb_test.go @@ -13,14 +13,14 @@ import ( func TestFFmpegGenerator_Generate(t *testing.T) { ctx := context.Background() - called := false - fakeExecer := func(_ context.Context, name string, arg ...string) *exec.Cmd { - called = true + // Video with duration > 0 should include -ss. + videoCalled := false + fakeExecerVideo := func(_ context.Context, name string, arg ...string) *exec.Cmd { + videoCalled = true if name != "ffmpeg" { t.Errorf("expected ffmpeg, got %s", name) } - // Verify some expected flags exist. args := strings.Join(arg, " ") if !strings.Contains(args, "-ss") { t.Error("missing -ss flag") @@ -34,25 +34,35 @@ func TestFFmpegGenerator_Generate(t *testing.T) { if !strings.Contains(args, "-y") { t.Error("missing -y flag") } - // Return a command that does nothing successfully. return exec.Command("true") } - - g := &FFmpegGenerator{execer: fakeExecer, rnd: rand.New(rand.NewSource(1))} - if err := g.Generate(ctx, "input.mp4", "output.jpg", 120.0); err != nil { + g := &FFmpegGenerator{execer: fakeExecerVideo, rnd: rand.New(rand.NewSource(1))} + if err := g.Generate(ctx, "input.mp4", "out.jpg", 120.0); err != nil { t.Fatalf("unexpected error: %v", err) } - if !called { + if !videoCalled { t.Fatal("expected fake execer to be called") } - // Duration zero or negative should still call execer with valid offset. - called = false - if err := g.Generate(ctx, "input.mp4", "output.jpg", 0); err != nil { + // Image with duration == 0 should NOT include -ss. + imgCalled := false + fakeExecerImage := func(_ context.Context, name string, arg ...string) *exec.Cmd { + imgCalled = true + args := strings.Join(arg, " ") + if strings.Contains(args, "-ss") { + t.Error("unexpected -ss flag for image") + } + if !strings.Contains(args, "-i") { + t.Error("missing -i flag") + } + return exec.Command("true") + } + gi := &FFmpegGenerator{execer: fakeExecerImage, rnd: rand.New(rand.NewSource(1))} + if err := gi.Generate(ctx, "photo.jpg", "thumb.jpg", 0); err != nil { t.Fatalf("unexpected error: %v", err) } - if !called { - t.Fatal("expected fake execer to be called for zero duration") + if !imgCalled { + t.Fatal("expected fake execer to be called for image") } } |
