website/pkg/photo/gifv.go

94 lines
2.3 KiB
Go

package photo
import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
"code.nonshy.com/nonshy/website/pkg/log"
)
// GifToMP4 converts the image into an mp4 gifv. Requires the `ffmpeg` program to be installed, or returns an error.
func GifToMP4(filename string, cfg *UploadConfig) error {
var (
gifSize int64
mp4Size int64
)
// Write GIF to temp file
fh, err := os.CreateTemp("", "nonshy-*.gif")
if err != nil {
return err
}
defer os.Remove(fh.Name())
if n, err := fh.Write(cfg.Data); err != nil {
return err
} else {
gifSize = int64(n)
log.Debug("GifToMP4: written %d bytes to %s", gifSize, fh.Name())
}
// Prepare an mp4 tempfile to write to
var mp4 = strings.TrimSuffix(fh.Name(), ".gif") + ".mp4"
// Run ffmpeg
command := []string{
"ffmpeg",
"-i", fh.Name(), // .gif name
"-movflags", "faststart",
"-pix_fmt", "yuv420p",
"-vf", `scale=trunc(iw/2)*2:trunc(ih/2)*2`,
mp4, // .mp4 name
}
log.Debug("GifToMP4: Run command: %s", command)
cmd := exec.Command(command[0], command[1:]...)
if stdoutErr, err := cmd.CombinedOutput(); err != nil {
log.Error("ffmpeg failed:\n%s", stdoutErr)
return fmt.Errorf("GIF conversion didn't work (ffmpeg might not be installed): %s", err)
}
// Make sure the output file isn't empty.
if stat, err := os.Stat(mp4); !os.IsNotExist(err) {
mp4Size = stat.Size()
log.Debug("GifToMP4: stats of generated file %s: %d bytes", mp4, mp4Size)
if stat.Size() == 0 {
return errors.New("GIF conversion failed: output mp4 file was empty")
}
}
// Place the .mp4 (not .gif) in the static/photos/ folder
if !strings.HasSuffix(filename, ".mp4") {
filename = strings.TrimSuffix(filename, ".gif") + ".mp4"
}
if path, err := EnsurePath(filename); err == nil {
// Copy the mp4 tempfile into the right place
srcFile, err := ioutil.ReadFile(mp4)
if err != nil {
return err
}
destFile, err := os.Create(path)
if err != nil {
return err
}
defer destFile.Close()
w, err := destFile.Write(srcFile)
log.Debug("GifToMP4: Copy tempfile %s => %s; w=%d err=%s", mp4, path, w, err)
}
log.Info("GifToMP4: converted GIF (%d bytes, %f MB) to MP4 (%d bytes, %f MB) for a %f%% savings",
gifSize, float64(gifSize)/1024/1024,
mp4Size, float64(mp4Size)/1024/1024,
float64(gifSize)/float64(mp4Size),
)
return nil
}