94 lines
2.3 KiB
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
|
||
|
}
|