From a65019e202821672ca6608cb6308bf476db57e46 Mon Sep 17 00:00:00 2001 From: Ben Sarmiento Date: Thu, 19 Oct 2023 23:00:35 +0200 Subject: [PATCH] Refinements --- internal/dav/aero.go | 124 ++++++++++++++++++++++++++++++++++++ internal/dav/getfile.go | 6 +- internal/dav/response.go | 4 +- internal/dav/router.go | 115 --------------------------------- internal/torrent/manager.go | 24 +++++-- pkg/dav/response.go | 1 - pkg/realdebrid/types.go | 1 - 7 files changed, 149 insertions(+), 126 deletions(-) create mode 100644 internal/dav/aero.go diff --git a/internal/dav/aero.go b/internal/dav/aero.go new file mode 100644 index 0000000..6304578 --- /dev/null +++ b/internal/dav/aero.go @@ -0,0 +1,124 @@ +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") + 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") + 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)) +} diff --git a/internal/dav/getfile.go b/internal/dav/getfile.go index 6c7f2e5..9e395e0 100644 --- a/internal/dav/getfile.go +++ b/internal/dav/getfile.go @@ -22,7 +22,8 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent // If there are less than 3 segments, return an error or adjust as needed if len(segments) < 3 { log.Println("Invalid url", requestPath) - http.Error(w, "Cannot find file", http.StatusNotFound) + // http.Error(w, "Cannot find file", http.StatusNotFound) + handleSingleTorrent(requestPath, w, r, t) return } @@ -52,7 +53,7 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent resp := realdebrid.RetryUntilOk(unrestrictFn) if resp == nil { // TODO: Readd the file - // when unrestricting fails, it means the file is not available anymore + // when unrestricting fails, it means the file is not available anymore, but still in their database // if it's the only file, tough luck log.Println("Cannot unrestrict link") http.Error(w, "Cannot find file", http.StatusNotFound) @@ -63,6 +64,7 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent // [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 + // If the file extension changed, that means it's a different file log.Println("Filename mismatch", resp.Filename, filenameV2) } http.Redirect(w, r, resp.Download, http.StatusFound) diff --git a/internal/dav/response.go b/internal/dav/response.go index 86b82f2..83089b1 100644 --- a/internal/dav/response.go +++ b/internal/dav/response.go @@ -49,8 +49,10 @@ func createSingleTorrentResponse(basePath string, torrents []torrent.Torrent, t for _, torrent := range torrents { for _, file := range torrent.SelectedFiles { if file.Link == "" { - // TODO: trigger a re-add for the file log.Println("File has no link, skipping", file.Path) + // TODO: trigger a re-add for the file + // It is selected but no link is available + // I think this is handled on the manager side so we just need to refresh t.RefreshInfo(torrent.ID) continue } diff --git a/internal/dav/router.go b/internal/dav/router.go index b1ea876..12aed98 100644 --- a/internal/dav/router.go +++ b/internal/dav/router.go @@ -1,19 +1,11 @@ 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" ) // Router creates a WebDAV router @@ -35,110 +27,3 @@ func Router(mux *http.ServeMux, c config.ConfigInterface, t *torrent.TorrentMana } }) } - -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") - 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") - 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)) -} diff --git a/internal/torrent/manager.go b/internal/torrent/manager.go index 4c7db46..0f22dff 100644 --- a/internal/torrent/manager.go +++ b/internal/torrent/manager.go @@ -94,6 +94,7 @@ func (t *TorrentManager) getAll() []Torrent { var torrentsV2 []Torrent for _, torrent := range torrents { + torrent.Name = strings.TrimSuffix(torrent.Name, "/") torrentV2 := Torrent{ Torrent: torrent, SelectedFiles: nil, @@ -184,11 +185,10 @@ func (t *TorrentManager) getInfo(torrentID string) *Torrent { } if len(selectedFiles) != len(info.Links) { // TODO: This means some files have expired - // we need to re-add the torrent + // we need to 'fix' this torrent then, at least the missing selected files log.Println("Some links has expired for", info.Name) type Result struct { - Filename string - Link string + Response *realdebrid.UnrestrictResponse } resultsChan := make(chan Result, len(info.Links)) @@ -209,7 +209,7 @@ func (t *TorrentManager) getInfo(torrentID string) *Torrent { } resp := realdebrid.RetryUntilOk(unrestrictFn) if resp != nil { - resultsChan <- Result{Filename: resp.Filename, Link: resp.Link} + resultsChan <- Result{Response: resp} } }(link) } @@ -221,11 +221,23 @@ func (t *TorrentManager) getInfo(torrentID string) *Torrent { }() for result := range resultsChan { + found := false for i := range selectedFiles { - if strings.HasSuffix(selectedFiles[i].Path, result.Filename) { - selectedFiles[i].Link = result.Link + if strings.HasSuffix(selectedFiles[i].Path, result.Response.Filename) { + selectedFiles[i].Link = result.Response.Link + found = true } } + if !found { + selectedFiles = append(selectedFiles, File{ + File: realdebrid.File{ + Path: result.Response.Filename, + Bytes: result.Response.Filesize, + Selected: 1, + }, + Link: result.Response.Link, + }) + } } } else { for i, link := range info.Links { diff --git a/pkg/dav/response.go b/pkg/dav/response.go index 58a5891..70c4b21 100644 --- a/pkg/dav/response.go +++ b/pkg/dav/response.go @@ -18,7 +18,6 @@ func File(path string, fileSize int64, added string, link string) Response { Propstat: PropStat{ Prop: Prop{ ContentLength: fileSize, - IsHidden: 0, CreationDate: added, LastModified: added, }, diff --git a/pkg/realdebrid/types.go b/pkg/realdebrid/types.go index 94c2228..d5bf443 100644 --- a/pkg/realdebrid/types.go +++ b/pkg/realdebrid/types.go @@ -25,7 +25,6 @@ type Torrent struct { } type File struct { - ID int `json:"id"` Path string `json:"path"` Bytes int64 `json:"bytes"` Selected int `json:"selected"`