mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-03 05:34:23 -04:00
Rework thumbnail cache to use db
This commit is contained in:
parent
3ea2004267
commit
a75bd48ccf
@ -176,13 +176,10 @@ func (h *Handler) GetInfo(c echo.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret, err := src.GetInfo(path, sha)
|
ret, err := h.metadata.GetMetadata(path, sha)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Run extractors to have them in cache
|
|
||||||
src.Extract(ret.Path, sha)
|
|
||||||
go src.ExtractThumbnail(ret.Path, sha)
|
|
||||||
return c.JSON(http.StatusOK, ret)
|
return c.JSON(http.StatusOK, ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,13 +243,12 @@ func (h *Handler) GetThumbnails(c echo.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
sprite, _, err := h.metadata.GetThumb(path, sha)
|
||||||
out, err := src.ExtractThumbnail(path, sha)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.File(fmt.Sprintf("%s/sprite.png", out))
|
return c.File(sprite)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get thumbnail vtt
|
// Get thumbnail vtt
|
||||||
@ -266,17 +262,17 @@ func (h *Handler) GetThumbnailsVtt(c echo.Context) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
_, vtt, err := h.metadata.GetThumb(path, sha)
|
||||||
out, err := src.ExtractThumbnail(path, sha)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.File(fmt.Sprintf("%s/sprite.vtt", out))
|
return c.File(vtt)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
transcoder *src.Transcoder
|
transcoder *src.Transcoder
|
||||||
|
metadata src.MetadataService
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -5,11 +5,25 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type MetadataService struct {
|
type MetadataService struct {
|
||||||
database *sqlx.DB
|
database *sqlx.DB
|
||||||
lock *RunLock[string, *MediaInfo]
|
lock *RunLock[string, *MediaInfo]
|
||||||
|
thumbLock *RunLock[string, interface{}]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s MetadataService) GetMetadata(path string, sha string) (*MediaInfo, error) {
|
func (s MetadataService) GetMetadata(path string, sha string) (*MediaInfo, error) {
|
||||||
|
ret, err := s.getMetadata(path, sha)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ret.Versions.Thumbs < ThumbsVersion {
|
||||||
|
go s.ExtractThumbs(path, sha)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s MetadataService) getMetadata(path string, sha string) (*MediaInfo, error) {
|
||||||
var ret MediaInfo
|
var ret MediaInfo
|
||||||
rows, err := s.database.Queryx(`
|
rows, err := s.database.Queryx(`
|
||||||
select * from info as i where i.sha=$1;
|
select * from info as i where i.sha=$1;
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
@ -28,32 +29,60 @@ type Thumbnail struct {
|
|||||||
|
|
||||||
var thumbnails = NewCMap[string, *Thumbnail]()
|
var thumbnails = NewCMap[string, *Thumbnail]()
|
||||||
|
|
||||||
func ExtractThumbnail(path string, sha string) (string, error) {
|
const ThumbsVersion = 1
|
||||||
ret, _ := thumbnails.GetOrCreate(sha, func() *Thumbnail {
|
|
||||||
ret := &Thumbnail{
|
func getThumbGlob(sha string) string {
|
||||||
path: fmt.Sprintf("%s/%s", Settings.Metadata, sha),
|
return fmt.Sprintf("%s/%s/thumbs-v*.*", Settings.Metadata, sha)
|
||||||
}
|
|
||||||
ret.ready.Add(1)
|
|
||||||
go func() {
|
|
||||||
extractThumbnail(path, ret.path)
|
|
||||||
ret.ready.Done()
|
|
||||||
}()
|
|
||||||
return ret
|
|
||||||
})
|
|
||||||
ret.ready.Wait()
|
|
||||||
return ret.path, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractThumbnail(path string, out string) error {
|
func getThumbPath(sha string) string {
|
||||||
defer printExecTime("extracting thumbnails for %s", path)()
|
return fmt.Sprintf("%s/%s/thumbs-v%d.png", Settings.Metadata, sha, ThumbsVersion)
|
||||||
os.MkdirAll(out, 0o755)
|
}
|
||||||
sprite_path := fmt.Sprintf("%s/sprite.png", out)
|
|
||||||
vtt_path := fmt.Sprintf("%s/sprite.vtt", out)
|
func getThumbVttPath(sha string) string {
|
||||||
|
return fmt.Sprintf("%s/%s/thumbs-v%d.vtt", Settings.Metadata, sha, ThumbsVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s MetadataService) GetThumb(path string, sha string) (string, string, error) {
|
||||||
|
sprite_path := getThumbPath(sha)
|
||||||
|
vtt_path := getThumbVttPath(sha)
|
||||||
|
|
||||||
if _, err := os.Stat(sprite_path); err == nil {
|
if _, err := os.Stat(sprite_path); err == nil {
|
||||||
return nil
|
return sprite_path, vtt_path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_, err := s.ExtractThumbs(path, sha)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return sprite_path, vtt_path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s MetadataService) ExtractThumbs(path string, sha string) (interface{}, error) {
|
||||||
|
get_running, set := s.thumbLock.Start(sha)
|
||||||
|
if get_running != nil {
|
||||||
|
return get_running()
|
||||||
|
}
|
||||||
|
|
||||||
|
err := extractThumbnail(path, sha)
|
||||||
|
if err != nil {
|
||||||
|
return set(nil, err)
|
||||||
|
}
|
||||||
|
_, err = s.database.NamedExec(
|
||||||
|
`update info set ver_thumbs = :version where sha = :sha`,
|
||||||
|
map[string]interface{}{
|
||||||
|
"sha": sha,
|
||||||
|
"version": ThumbsVersion,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return set(nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractThumbnail(path string, sha string) error {
|
||||||
|
defer printExecTime("extracting thumbnails for %s", path)()
|
||||||
|
|
||||||
|
os.MkdirAll(fmt.Sprintf("%s/%s", Settings.Metadata), 0o755)
|
||||||
|
|
||||||
gen, err := screengen.NewGenerator(path)
|
gen, err := screengen.NewGenerator(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error reading video file: %v", err)
|
log.Printf("Error reading video file: %v", err)
|
||||||
@ -102,7 +131,7 @@ func extractThumbnail(path string, out string) error {
|
|||||||
tsToVttTime(timestamps),
|
tsToVttTime(timestamps),
|
||||||
tsToVttTime(ts),
|
tsToVttTime(ts),
|
||||||
Settings.RoutePrefix,
|
Settings.RoutePrefix,
|
||||||
base64.StdEncoding.EncodeToString([]byte(path)),
|
base64.RawURLEncoding.EncodeToString([]byte(path)),
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width,
|
width,
|
||||||
@ -110,11 +139,20 @@ func extractThumbnail(path string, out string) error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.WriteFile(vtt_path, []byte(vtt), 0o644)
|
// Cleanup old versions of thumbnails
|
||||||
|
files, err := filepath.Glob(getThumbGlob(sha))
|
||||||
|
if err == nil {
|
||||||
|
for _, f := range files {
|
||||||
|
// ignore errors
|
||||||
|
os.Remove(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.WriteFile(getThumbVttPath(sha), []byte(vtt), 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = imaging.Save(sprite, sprite_path)
|
err = imaging.Save(sprite, getThumbPath(sha))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user