Rework thumbnail cache to use db

This commit is contained in:
Zoe Roux 2024-07-29 23:25:26 +02:00
parent 3ea2004267
commit a75bd48ccf
3 changed files with 83 additions and 35 deletions

View File

@ -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() {

View File

@ -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;

View File

@ -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
} }