diff --git a/.gitignore b/.gitignore index e33609d..b2db80a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ *.png +*.webp +main diff --git a/cmd/thumbnail-gen/main.go b/cmd/thumbnail-gen/main.go index 4837770..78487eb 100644 --- a/cmd/thumbnail-gen/main.go +++ b/cmd/thumbnail-gen/main.go @@ -14,6 +14,9 @@ var ( intervalFlag = flag.Int("interval", 20, "Interval in seconds between thumbnails") maxThumbnailsFlag = flag.Int("max", 0, "Max Thumbnails, default as much as possible with interval") blackFilterFlag = flag.Bool("filter", true, "Try to filter out black frames, might be an expensive operation") + formatFlag = flag.String("format", "png", "Output format") + segmentsFlag = flag.Bool("segments", false, "If active uses interval for how many segments are supposed to be done") + widthFlag = flag.Int("width", -1, "Set the width of the picture to scale to, -1 = max") ) func main() { @@ -21,7 +24,15 @@ func main() { if *pathFlag != "" { // thumbnails, num, err := thumbnailgen.GetThumbnail(*pathFlag, *intervalFlag, *maxThumbnailsFlag, *blackFilterFlag) - thumbnails, num, err := thumbnailgen.GetThumbnailSegments(*pathFlag, 12, *blackFilterFlag) + opts := thumbnailgen.NewDefaultOptions() + thumbnails, num, err := opts.Apply(func(o *thumbnailgen.Options) { + o.EnableFilter = *blackFilterFlag + o.Interval = *intervalFlag + o.Format = *formatFlag + o.UseSegments = *segmentsFlag + o.Segments = *intervalFlag + o.Scale = fmt.Sprintf("%d:-1", *widthFlag) + }).GetThumbnail(*pathFlag) if err != nil { panic(err) } @@ -30,7 +41,7 @@ func main() { name := filepath.Base(*pathFlag) for i, thumbnail := range thumbnails[:num] { - err := os.WriteFile(fmt.Sprintf("%s-%d.png", name, i), thumbnail, 0600) + err := os.WriteFile(fmt.Sprintf("%s-%d.%s", name, i, *formatFlag), thumbnail, 0600) if err != nil { panic(err) } diff --git a/generator.go b/generator.go index 229836e..5710a2c 100644 --- a/generator.go +++ b/generator.go @@ -16,9 +16,39 @@ type TimeFilter struct { End float64 } -func GetThumbnailSegments(path string, segments int, enableFilter bool) ([][]byte, int, error) { +type Options struct { + Interval int + Segments int + UseSegments bool + Format string + Scale string + EnableFilter bool + MaxThumbnails int +} + +func NewDefaultOptions() Options { + return Options{ + Interval: 5, + Segments: 0, + UseSegments: false, + Format: "webp", + Scale: "-1:-1", + EnableFilter: true, + MaxThumbnails: 0, + } +} + +func (options *Options) Apply(opts ...func(*Options)) *Options { + for _, opt := range opts { + opt(options) + } + + return options +} + +func (opts *Options) GetThumbnail(path string) ([][]byte, int, error) { var filters []TimeFilter - if enableFilter { + if opts.EnableFilter { f, err := GetFilter(path) if err != nil { return nil, 0, err @@ -31,30 +61,17 @@ func GetThumbnailSegments(path string, segments int, enableFilter bool) ([][]byt return nil, 0, err } - interval := length / float64(segments) - - return getThumbnailUnderlying(path, 0, filters, length, int(interval), enableFilter) -} - -func GetThumbnail(path string, intervalSeconds int, maxThumbnails int, enableFilter bool) ([][]byte, int, error) { - var filters []TimeFilter - if enableFilter { - f, err := GetFilter(path) - if err != nil { - return nil, 0, err - } - filters = f + var interval int + if opts.UseSegments { + interval = int(length / float64(opts.Segments)) + } else { + interval = opts.Interval } - length, err := GetVideoLength(path) - if err != nil { - return nil, 0, err - } - - return getThumbnailUnderlying(path, maxThumbnails, filters, length, intervalSeconds, enableFilter) + return GetThumbnailUnderlying(path, opts.MaxThumbnails, filters, length, interval, opts.EnableFilter, opts.Format, opts.Scale) } -func getThumbnailUnderlying(path string, maxThumbnails int, filters []TimeFilter, length float64, intervalSeconds int, enableFilter bool) ([][]byte, int, error) { +func GetThumbnailUnderlying(path string, maxThumbnails int, filters []TimeFilter, length float64, intervalSeconds int, enableFilter bool, format string, scale string) ([][]byte, int, error) { buf := bytes.NewBuffer(nil) framesExtracted := 0 @@ -78,7 +95,7 @@ func getThumbnailUnderlying(path string, maxThumbnails int, filters []TimeFilter } } - err := GetImage(buf, path, int(time), "png") + err := GetImage(buf, path, int(time), format, scale) if err != nil { return nil, 0, err } @@ -110,10 +127,10 @@ func FrameLiesWithinFilter(time float64, filters []TimeFilter) (bool, float64) { return false, time } -func GetImage(buf *bytes.Buffer, path string, timestamp int, format string) error { +func GetImage(buf *bytes.Buffer, path string, timestamp int, format string, scale string) error { var t time.Time t = t.Add(time.Duration(timestamp) * time.Second) - cmd := exec.Command("ffmpeg", "-ss", t.Format("15:04:05"), "-i", path, "-vframes", "1", "-c:v", format, "-f", "image2pipe", "-") + cmd := exec.Command("ffmpeg", "-ss", t.Format("15:04:05"), "-i", path, "-vframes", "1", "-c:v", format, "-filter:v", fmt.Sprintf("scale=%s", scale), "-f", "image2pipe", "-") cmd.Stdout = buf err := cmd.Run() return err