[feature] use webp for thumbnails (#3116)

* update to use webp for thumbnails

* bump webp quality up to 40% from 12% (it's a bit different to jpeg quality setting)

* update to use yuva colorspace, and use thumbnail=n=10 to select frame

* fix missing comma in ffmpeg args

* add links to appropriate ffmpeg docs

* update tests

* add file size tests for thumbnails

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
This commit is contained in:
kim 2024-07-19 15:28:43 +00:00 committed by GitHub
commit 50c9b5498b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
72 changed files with 4496 additions and 225 deletions

View file

@ -47,10 +47,21 @@ func ffmpegClearMetadata(ctx context.Context, filepath string, ext string) error
// Clear metadata with ffmpeg.
if err := ffmpeg(ctx, dirpath,
"-loglevel", "error",
// Input file.
"-i", filepath,
// Drop all metadata.
"-map_metadata", "-1",
// Copy input codecs,
// i.e. no transcode.
"-codec", "copy",
// Overwrite.
"-y",
// Output.
outpath,
); err != nil {
return err
@ -64,23 +75,54 @@ func ffmpegClearMetadata(ctx context.Context, filepath string, ext string) error
return nil
}
// ffmpegGenerateThumb generates a thumbnail jpeg from input media of any type, useful for any media.
// ffmpegGenerateThumb generates a thumbnail webp from input media of any type, useful for any media.
func ffmpegGenerateThumb(ctx context.Context, filepath string, width, height int) (string, error) {
// Get directory from filepath.
dirpath := path.Dir(filepath)
// Generate output frame file path.
outpath := filepath + "_thumb.jpg"
outpath := filepath + "_thumb.webp"
// Thumbnail size scaling argument.
scale := strconv.Itoa(width) + ":" +
strconv.Itoa(height)
// Generate thumb with ffmpeg.
if err := ffmpeg(ctx, dirpath,
"-loglevel", "error",
// Input file.
"-i", filepath,
"-filter:v", "thumbnail=n=10",
"-filter:v", "scale="+strconv.Itoa(width)+":"+strconv.Itoa(height),
"-qscale:v", "12", // ~ 70% quality
// Encode using libwebp.
// (NOT as libwebp_anim).
"-codec:v", "libwebp",
// Select thumb from first 10 frames
// (thumb filter: https://ffmpeg.org/ffmpeg-filters.html#thumbnail)
"-filter:v", "thumbnail=n=10,"+
// scale to dimensions
// (scale filter: https://ffmpeg.org/ffmpeg-filters.html#scale)
"scale="+scale+","+
// YUVA 4:2:0 pixel format
// (format filter: https://ffmpeg.org/ffmpeg-filters.html#format)
"format=pix_fmts=yuva420p",
// Only one frame
"-frames:v", "1",
// ~40% webp quality
// (codec options: https://ffmpeg.org/ffmpeg-codecs.html#toc-Codec-Options)
// (libwebp codec: https://ffmpeg.org/ffmpeg-codecs.html#Options-36)
"-qscale:v", "40",
// Overwrite.
"-y",
// Output.
outpath,
); err != nil {
return "", err
@ -100,10 +142,21 @@ func ffmpegGenerateStatic(ctx context.Context, filepath string) (string, error)
// Generate static with ffmpeg.
if err := ffmpeg(ctx, dirpath,
"-loglevel", "error",
// Input file.
"-i", filepath,
"-codec:v", "png", // specifically NOT 'apng'
"-frames:v", "1", // in case animated, only take 1 frame
// Only first frame.
"-frames:v", "1",
// Encode using png.
// (NOT as apng).
"-codec:v", "png",
// Overwrite.
"-y",
// Output.
outpath,
); err != nil {
return "", err