Refinements
This commit is contained in:
124
internal/dav/aero.go
Normal file
124
internal/dav/aero.go
Normal file
@@ -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("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n%s\n", output))
|
||||||
|
}
|
||||||
@@ -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 there are less than 3 segments, return an error or adjust as needed
|
||||||
if len(segments) < 3 {
|
if len(segments) < 3 {
|
||||||
log.Println("Invalid url", requestPath)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.Torrent
|
|||||||
resp := realdebrid.RetryUntilOk(unrestrictFn)
|
resp := realdebrid.RetryUntilOk(unrestrictFn)
|
||||||
if resp == nil {
|
if resp == nil {
|
||||||
// TODO: Readd the file
|
// 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
|
// if it's the only file, tough luck
|
||||||
log.Println("Cannot unrestrict link")
|
log.Println("Cannot unrestrict link")
|
||||||
http.Error(w, "Cannot find file", http.StatusNotFound)
|
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
|
// [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
|
// Pokemon.S22E24.The.Secret.Princess.DUBBED.1080p.WEBRip.AAC.2.0.x264-SRS.mkv
|
||||||
// Action: schedule a "cleanup" job for the parent torrent
|
// 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)
|
log.Println("Filename mismatch", resp.Filename, filenameV2)
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, resp.Download, http.StatusFound)
|
http.Redirect(w, r, resp.Download, http.StatusFound)
|
||||||
|
|||||||
@@ -49,8 +49,10 @@ func createSingleTorrentResponse(basePath string, torrents []torrent.Torrent, t
|
|||||||
for _, torrent := range torrents {
|
for _, torrent := range torrents {
|
||||||
for _, file := range torrent.SelectedFiles {
|
for _, file := range torrent.SelectedFiles {
|
||||||
if file.Link == "" {
|
if file.Link == "" {
|
||||||
// TODO: trigger a re-add for the file
|
|
||||||
log.Println("File has no link, skipping", file.Path)
|
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)
|
t.RefreshInfo(torrent.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,11 @@
|
|||||||
package dav
|
package dav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/xml"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/aerogo/aero"
|
|
||||||
"github.com/debridmediamanager.com/zurg/internal/config"
|
"github.com/debridmediamanager.com/zurg/internal/config"
|
||||||
"github.com/debridmediamanager.com/zurg/internal/torrent"
|
"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
|
// 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("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n%s\n", output))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ func (t *TorrentManager) getAll() []Torrent {
|
|||||||
|
|
||||||
var torrentsV2 []Torrent
|
var torrentsV2 []Torrent
|
||||||
for _, torrent := range torrents {
|
for _, torrent := range torrents {
|
||||||
|
torrent.Name = strings.TrimSuffix(torrent.Name, "/")
|
||||||
torrentV2 := Torrent{
|
torrentV2 := Torrent{
|
||||||
Torrent: torrent,
|
Torrent: torrent,
|
||||||
SelectedFiles: nil,
|
SelectedFiles: nil,
|
||||||
@@ -184,11 +185,10 @@ func (t *TorrentManager) getInfo(torrentID string) *Torrent {
|
|||||||
}
|
}
|
||||||
if len(selectedFiles) != len(info.Links) {
|
if len(selectedFiles) != len(info.Links) {
|
||||||
// TODO: This means some files have expired
|
// 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)
|
log.Println("Some links has expired for", info.Name)
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Filename string
|
Response *realdebrid.UnrestrictResponse
|
||||||
Link string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resultsChan := make(chan Result, len(info.Links))
|
resultsChan := make(chan Result, len(info.Links))
|
||||||
@@ -209,7 +209,7 @@ func (t *TorrentManager) getInfo(torrentID string) *Torrent {
|
|||||||
}
|
}
|
||||||
resp := realdebrid.RetryUntilOk(unrestrictFn)
|
resp := realdebrid.RetryUntilOk(unrestrictFn)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
resultsChan <- Result{Filename: resp.Filename, Link: resp.Link}
|
resultsChan <- Result{Response: resp}
|
||||||
}
|
}
|
||||||
}(link)
|
}(link)
|
||||||
}
|
}
|
||||||
@@ -221,11 +221,23 @@ func (t *TorrentManager) getInfo(torrentID string) *Torrent {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
for result := range resultsChan {
|
for result := range resultsChan {
|
||||||
|
found := false
|
||||||
for i := range selectedFiles {
|
for i := range selectedFiles {
|
||||||
if strings.HasSuffix(selectedFiles[i].Path, result.Filename) {
|
if strings.HasSuffix(selectedFiles[i].Path, result.Response.Filename) {
|
||||||
selectedFiles[i].Link = result.Link
|
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 {
|
} else {
|
||||||
for i, link := range info.Links {
|
for i, link := range info.Links {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ func File(path string, fileSize int64, added string, link string) Response {
|
|||||||
Propstat: PropStat{
|
Propstat: PropStat{
|
||||||
Prop: Prop{
|
Prop: Prop{
|
||||||
ContentLength: fileSize,
|
ContentLength: fileSize,
|
||||||
IsHidden: 0,
|
|
||||||
CreationDate: added,
|
CreationDate: added,
|
||||||
LastModified: added,
|
LastModified: added,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ type Torrent struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
ID int `json:"id"`
|
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Bytes int64 `json:"bytes"`
|
Bytes int64 `json:"bytes"`
|
||||||
Selected int `json:"selected"`
|
Selected int `json:"selected"`
|
||||||
|
|||||||
Reference in New Issue
Block a user