package dav import ( "encoding/xml" "fmt" "log" "net/http" "os" "strings" "github.com/aerogo/aero" "github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/torrent" "github.com/debridmediamanager.com/zurg/pkg/dav" "github.com/debridmediamanager.com/zurg/pkg/davextra" "github.com/debridmediamanager.com/zurg/pkg/realdebrid" ) func Setup(app *aero.Application, c config.ConfigInterface, t *torrent.TorrentManager) { // hack to make PROPFIND work app.Rewrite(func(ctx aero.RewriteContext) { newCtx := ctx.(aero.Context) if newCtx.Request().Internal().Method == "PROPFIND" { newCtx.Request().Internal().Method = http.MethodGet } }) app.Router().Add(http.MethodOptions, "/", func(ctx aero.Context) error { ctx.SetStatus(http.StatusOK) return nil }) // hardcode the root directory app.Get("/", func(ctx aero.Context) error { var responses []dav.Response responses = append(responses, dav.Directory("/")) for _, directory := range c.GetDirectories() { responses = append(responses, dav.Directory(fmt.Sprintf("/%s", directory))) } resp := dav.MultiStatus{ XMLNS: "DAV:", Response: responses, } return xmlResponse(ctx, resp) }) for _, directoryPtr := range c.GetDirectories() { directory := directoryPtr app.Get(fmt.Sprintf("/%s/", directory), func(ctx aero.Context) error { torrentsInDirectory := t.GetByDirectory(directory) resp, err := createMultiTorrentResponse(fmt.Sprintf("/%s", directory), torrentsInDirectory) if err != nil { log.Printf("Cannot read directory (%s): %v\n", directory, err.Error()) return ctx.Error(http.StatusInternalServerError, "Cannot read directory") } return xmlResponse(ctx, *resp) }) app.Get(fmt.Sprintf("/%s/:torrentName/", directory), func(ctx aero.Context) error { torrentName := ctx.Get("torrentName") sameNameTorrents := findAllTorrentsWithName(t, directory, torrentName) resp, err := createSingleTorrentResponse(fmt.Sprintf("/%s", directory), sameNameTorrents, t) if err != nil { log.Printf("Cannot read directory (%s): %v\n", directory, err.Error()) return ctx.Error(http.StatusInternalServerError, "Cannot read directory") } return xmlResponse(ctx, *resp) }) app.Get(fmt.Sprintf("/%s/:torrentName/:filename", directory), func(ctx aero.Context) error { torrentName := strings.TrimSpace(ctx.Get("torrentName")) filename := strings.TrimSpace(ctx.Get("filename")) torrents := findAllTorrentsWithName(t, directory, torrentName) if torrents == nil { log.Println("Cannot find torrent", torrentName) return ctx.Error(http.StatusNotFound, "Cannot find file") } filenameV2, linkFragment := davextra.ExtractLinkFragment(filename) link := getLink(torrents, filenameV2, linkFragment) if link == "" { log.Println("Link not found", filename) return ctx.Error(http.StatusNotFound, "Cannot find file") } unrestrictFn := func() (*realdebrid.UnrestrictResponse, error) { return realdebrid.UnrestrictLink(os.Getenv("RD_TOKEN"), link) } resp := realdebrid.RetryUntilOk(unrestrictFn) if resp == nil { // TODO: Readd the file // when unrestricting fails, it means the file is not available anymore // if it's the only file, tough luck log.Println("Cannot unrestrict link", link, filenameV2) return ctx.Error(http.StatusNotFound, "Cannot find file") } if resp.Filename != filenameV2 { // TODO: Redo the logic to handle mismatch // [SRS] Pokemon S22E01-35 1080p WEBRip AAC 2.0 x264 CC.rar // Pokemon.S22E24.The.Secret.Princess.DUBBED.1080p.WEBRip.AAC.2.0.x264-SRS.mkv // Action: schedule a "cleanup" job for the parent torrent // do it in 2 batches with different selections log.Println("Filename mismatch", resp.Filename, filenameV2) } return ctx.Redirect(http.StatusFound, resp.Download) }) } } func xmlResponse(ctx aero.Context, resp dav.MultiStatus) error { output, err := xml.MarshalIndent(resp, "", " ") if err != nil { log.Printf("Cannot marshal xml: %v\n", err.Error()) return ctx.Error(http.StatusInternalServerError, "Cannot read directory") } ctx.SetStatus(http.StatusMultiStatus) ctx.Response().SetHeader("Content-Type", "text/xml; charset=\"utf-8\"") return ctx.String(fmt.Sprintf("\n%s\n", output)) }