Files

106 lines
2.9 KiB
Go

package plg_video_thumbnail
import (
"bytes"
"encoding/base64"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"strings"
. "github.com/mickael-kerjean/filestash/server/common"
)
const (
VideoCachePath = "data/cache/video-thumbnail/"
)
var plugin_enable = func() bool {
return Config.Get("features.video.enable_thumbnail").Schema(func(f *FormElement) *FormElement {
if f == nil {
f = &FormElement{}
}
f.Name = "enable_thumbnail"
f.Type = "enable"
f.Target = []string{}
f.Description = "Enable/Disable on video thumbnail generation"
f.Default = false
return f
}).Bool()
}
func init() {
if _, err := exec.LookPath("ffmpeg"); err != nil {
Hooks.Register.Onload(func() {
Log.Warning("plg_video_thumbnail::init error=ffmpeg_not_installed")
})
return
}
Hooks.Register.Onload(func() {
if plugin_enable() == false {
return
}
cachePath := GetAbsolutePath(VideoCachePath)
os.RemoveAll(cachePath)
os.MkdirAll(cachePath, os.ModePerm)
Hooks.Register.Thumbnailer("video/mp4", &ffmpegThumbnail{})
Hooks.Register.Thumbnailer("video/x-matroska", &ffmpegThumbnail{})
Hooks.Register.Thumbnailer("video/x-msvideo", &ffmpegThumbnail{})
})
}
type ffmpegThumbnail struct{}
func (this *ffmpegThumbnail) Generate(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) {
var (
errBuff bytes.Buffer
fullURL = strings.Replace(
fmt.Sprintf("http://127.0.0.1:%d%s?%s", Config.Get("general.port").Int(), req.URL.Path, req.URL.RawQuery),
"&thumbnail=true", "&origin=plg_video_thumbnail", 1,
)
cacheName = "thumb_" + GenerateID(ctx.Session) + "_" + QuickHash(req.URL.Query().Get("path"), 10) + ".jpeg"
cachePath = GetAbsolutePath(VideoCachePath, cacheName)
)
reader.Close()
thumbnail, err := os.OpenFile(cachePath, os.O_RDONLY, os.ModePerm)
if err == nil {
this.setHeader(res)
return thumbnail, nil
}
cmd := exec.CommandContext(req.Context(), "ffmpeg", []string{
"-hide_banner", "-headers", "cookie: " + req.Header.Get("Cookie") + "\r\n",
"-skip_frame", "nokey",
"-i", fullURL, "-y",
"-an", "-sn",
"-vf", "thumbnail, scale=320:320: force_original_aspect_ratio=decrease", "-fps_mode", "passthrough", "-frames:v", "1",
"-c:v", "mjpeg", cachePath,
}...)
cmd.Stderr = &errBuff
if err := cmd.Run(); err != nil {
if req.Context().Err() == nil {
Log.Debug("plg_video_thumbnail::generate::run path=%s err=%s", req.URL.Query().Get("path"), base64.StdEncoding.EncodeToString(errBuff.Bytes()))
return nil, err
}
return nil, err
}
cmd.Wait()
thumbnail, err = os.OpenFile(cachePath, os.O_RDONLY, os.ModePerm)
if err != nil {
Log.Error("plg_video_thumbnail::generate::open path=%s err=%s", cachePath, err.Error())
return nil, err
}
this.setHeader(res)
return thumbnail, nil
}
func (this *ffmpegThumbnail) setHeader(res *http.ResponseWriter) {
(*res).Header().Set("Content-Type", "image/jpeg")
(*res).Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", 3600*12))
}