Use concurrent map for subtitles

This commit is contained in:
Zoe Roux 2024-02-18 22:23:39 +01:00
parent b687d8ea95
commit 2afed432f7
4 changed files with 47 additions and 46 deletions

View File

@ -176,12 +176,16 @@ func (h *Handler) GetInfo(c echo.Context) error {
return err return err
} }
sha, err := src.GetHash(path)
if err != nil {
return err
}
ret, err := src.GetInfo(path) ret, err := src.GetInfo(path)
if err != nil { if err != nil {
return err return err
} }
// Run extractors to have them in cache // Run extractors to have them in cache
h.extractor.RunExtractor(ret.Path, ret.Sha, &ret.Subtitles) src.Extract(ret.Path, sha)
go h.thumbnails.ExtractThumbnail( go h.thumbnails.ExtractThumbnail(
ret.Path, ret.Path,
fmt.Sprintf("%s/thumbnails.png", c.Request().Header.Get("X-Route")), fmt.Sprintf("%s/thumbnails.png", c.Request().Header.Get("X-Route")),
@ -195,46 +199,56 @@ func (h *Handler) GetInfo(c echo.Context) error {
// //
// Path: /attachment/:name // Path: /attachment/:name
func (h *Handler) GetAttachment(c echo.Context) error { func (h *Handler) GetAttachment(c echo.Context) error {
path, err := GetPath(c)
if err != nil {
return err
}
name := c.Param("name") name := c.Param("name")
if err := SanitizePath(name); err != nil { if err := SanitizePath(name); err != nil {
return err return err
} }
wait, ok := h.extractor.Extract(sha) sha, err := src.GetHash(path)
if !ok { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Not extracted yet. Call /info to extract.") return err
}
wait, err := src.Extract(path, sha)
if err != nil {
return err
} }
<-wait <-wait
path := fmt.Sprintf("%s/%s/att/%s", src.Settings.Metadata, sha, name) ret := fmt.Sprintf("%s/%s/att/%s", src.Settings.Metadata, sha, name)
return c.File(path) return c.File(ret)
} }
// Get subtitle // Get subtitle
// //
// Get a specific subtitle. // Get a specific subtitle.
// //
// Path: /:sha/subtitle/:name // Path: /subtitle/:name
func (h *Handler) GetSubtitle(c echo.Context) error { func (h *Handler) GetSubtitle(c echo.Context) error {
sha := c.Param("sha") path, err := GetPath(c)
name := c.Param("name") if err != nil {
if err := SanitizePath(sha); err != nil {
return err return err
} }
name := c.Param("name")
if err := SanitizePath(name); err != nil { if err := SanitizePath(name); err != nil {
return err return err
} }
wait, ok := h.extractor.Extract(sha) sha, err := src.GetHash(path)
if !ok { if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "Not extracted yet. Call /info to extract.") return err
}
wait, err := src.Extract(path, sha)
if err != nil {
return err
} }
<-wait <-wait
path := fmt.Sprintf("%s/%s/sub/%s", src.Settings.Metadata, sha, name) ret := fmt.Sprintf("%s/%s/sub/%s", src.Settings.Metadata, sha, name)
return c.File(path) return c.File(ret)
} }
// Get thumbnail sprite // Get thumbnail sprite
@ -284,7 +298,6 @@ func (h *Handler) GetThumbnailsVtt(c echo.Context) error {
type Handler struct { type Handler struct {
transcoder *src.Transcoder transcoder *src.Transcoder
extractor *src.Extractor
thumbnails *src.ThumbnailsCreator thumbnails *src.ThumbnailsCreator
} }
@ -300,7 +313,6 @@ func main() {
} }
h := Handler{ h := Handler{
transcoder: transcoder, transcoder: transcoder,
extractor: src.NewExtractor(),
thumbnails: src.NewThumbnailsCreator(), thumbnails: src.NewThumbnailsCreator(),
} }

View File

@ -4,36 +4,24 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"sync"
) )
type Extractor struct { var extracted = NewCMap[string, <-chan struct{}]()
extracted map[string]<-chan struct{}
lock sync.RWMutex
}
func NewExtractor() *Extractor { func Extract(path string, sha string) (<-chan struct{}, error) {
return &Extractor{ ret := make(chan struct{})
extracted: make(map[string]<-chan struct{}), existing, created := extracted.GetOrSet(sha, ret)
} if !created {
}
func (e *Extractor) Extract(path string, subs *[]Subtitle) (<-chan struct{}, error) {
sha, err := getHash(path)
if err != nil {
return nil, err
}
e.lock.Lock()
existing, ok := e.extracted[sha]
if ok {
return existing, nil return existing, nil
} }
ret := make(chan struct{})
e.extracted[sha] = ret
e.lock.Unlock()
go func() { go func() {
info, err := GetInfo(path)
if err != nil {
extracted.Remove(sha)
close(ret)
return
}
attachment_path := fmt.Sprintf("%s/%s/att", Settings.Metadata, sha) attachment_path := fmt.Sprintf("%s/%s/att", Settings.Metadata, sha)
subs_path := fmt.Sprintf("%s/%s/sub", Settings.Metadata, sha) subs_path := fmt.Sprintf("%s/%s/sub", Settings.Metadata, sha)
os.MkdirAll(attachment_path, 0o644) os.MkdirAll(attachment_path, 0o644)
@ -49,7 +37,7 @@ func (e *Extractor) Extract(path string, subs *[]Subtitle) (<-chan struct{}, err
) )
cmd.Dir = attachment_path cmd.Dir = attachment_path
for _, sub := range *subs { for _, sub := range info.Subtitles {
if ext := sub.Extension; ext != nil { if ext := sub.Extension; ext != nil {
cmd.Args = append( cmd.Args = append(
cmd.Args, cmd.Args,
@ -61,8 +49,9 @@ func (e *Extractor) Extract(path string, subs *[]Subtitle) (<-chan struct{}, err
} }
fmt.Printf("Starting extraction with the command: %s", cmd) fmt.Printf("Starting extraction with the command: %s", cmd)
cmd.Stdout = nil cmd.Stdout = nil
err := cmd.Run() err = cmd.Run()
if err != nil { if err != nil {
extracted.Remove(sha)
fmt.Println("Error starting ffmpeg extract:", err) fmt.Println("Error starting ffmpeg extract:", err)
} }
close(ret) close(ret)

View File

@ -68,7 +68,7 @@ func (t *ThumbnailsCreator) ExtractThumbnail(path string, name string) (string,
func extractThumbnail(path string, name string) (string, error) { func extractThumbnail(path string, name string) (string, error) {
defer printExecTime("extracting thumbnails for %s", path)() defer printExecTime("extracting thumbnails for %s", path)()
sha, err := getHash(path) sha, err := GetHash(path)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -19,7 +19,7 @@ func printExecTime(message string, args ...any) func() {
} }
} }
func getHash(path string) (string, error) { func GetHash(path string) (string, error) {
info, err := os.Stat(path) info, err := os.Stat(path)
if err != nil { if err != nil {
return "", err return "", err