mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 08:22:24 +01:00
105 lines
2.9 KiB
Go
105 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))
|
|
}
|