pegasus/backend/upload_handler.go

103 lines
2.7 KiB
Go
Raw Normal View History

2026-04-11 00:02:59 -03:00
// backend/upload_handler.go
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"strings"
"github.com/tus/tusd/pkg/filestore"
handler "github.com/tus/tusd/pkg/handler"
"github.com/tus/tusd/pkg/memorylocker"
"scm.bstein.dev/bstein/Pegasus/backend/internal"
)
func newTusHandler(um *internal.UserMap, jf jellyfinClient) *handler.UnroutedHandler {
store := filestore.FileStore{Path: tusDir}
locker := memorylocker.New()
composer := handler.NewStoreComposer()
store.UseIn(composer)
locker.UseIn(composer)
config := handler.Config{
BasePath: "/tus/",
StoreComposer: composer,
NotifyCompleteUploads: true,
MaxSize: 8 * 1024 * 1024 * 1024,
RespectForwardedHeaders: true,
}
tusHandler, err := handler.NewUnroutedHandler(config)
must(err, "init tus handler")
go watchUploadCompletions(tusHandler.CompleteUploads, um, jf)
return tusHandler
}
func watchUploadCompletions(completeC <-chan handler.HookEvent, um *internal.UserMap, jf jellyfinClient) {
for ev := range completeC {
if err := processCompletedUpload(ev, um, jf); err != nil {
log.Printf("tus upload processing failed: %v", err)
}
}
}
func processCompletedUpload(ev handler.HookEvent, um *internal.UserMap, jf jellyfinClient) error {
claims, err := claimsFromHook(ev)
if err != nil {
internal.Logf("tus: no session: %v", err)
return err
}
meta := ev.Upload.MetaData
desc := strings.TrimSpace(meta["desc"])
date := strings.TrimSpace(meta["date"])
subdir := strings.Trim(strings.TrimSpace(meta["subdir"]), "/")
lib := strings.Trim(strings.TrimSpace(meta["lib"]), "/")
orig := meta["filename"]
if orig == "" {
orig = "upload.bin"
}
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(orig), "."))
isVideo := map[string]bool{
"mp4": true, "mkv": true, "mov": true, "avi": true, "m4v": true,
"webm": true, "mpg": true, "mpeg": true, "ts": true, "m2ts": true,
}[ext]
if isVideo && desc == "" {
return fmt.Errorf("missing desc for video")
}
if desc == "" {
desc = "upload"
}
rootRel, err := resolveLibraryRoot(um, claims.Username, lib)
if err != nil {
return err
}
libRoot, _ := internal.SafeJoin(mediaRoot, rootRel)
if fi, err := os.Stat(libRoot); err != nil || !fi.IsDir() {
return fmt.Errorf("library root missing or not accessible")
}
destRoot := libRoot
if subdir != "" {
subdir = sanitizeSegment(subdir)
destRoot = filepath.Join(libRoot, subdir)
if err := os.MkdirAll(destRoot, 0o2775); err != nil {
return err
}
}
finalName := composeFinalName(date, desc, orig)
dst := filepath.Join(destRoot, finalName)
if err := moveFromTus(ev, dst); err != nil {
return err
}
jf.RefreshLibrary(claims.JFToken)
return nil
}