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