package universal import ( "io" "net/http" "path" "path/filepath" "strings" "github.com/debridmediamanager.com/zurg/internal/config" "github.com/debridmediamanager.com/zurg/internal/dav" intHttp "github.com/debridmediamanager.com/zurg/internal/http" "github.com/debridmediamanager.com/zurg/internal/torrent" zurghttp "github.com/debridmediamanager.com/zurg/pkg/http" "github.com/debridmediamanager.com/zurg/pkg/logutil" "github.com/hashicorp/golang-lru/v2/expirable" "go.uber.org/zap" ) // HandleGetRequest handles a GET request universally for both WebDAV and HTTP func HandleGetRequest(w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, cache *expirable.LRU[string, string]) { log := logutil.NewLogger().Named("uniget") requestPath := path.Clean(r.URL.Path) isDav := true if strings.Contains(requestPath, "/http") { requestPath = strings.Replace(requestPath, "/http", "/", 1) isDav = false } if requestPath == "/favicon.ico" { return } segments := strings.Split(requestPath, "/") // If there are less than 3 segments, return an error or adjust as needed if len(segments) <= 3 { if isDav { dav.HandlePropfindRequest(w, r, t, c) } else { intHttp.HandleDirectoryListing(w, r, t, c) } return } if data, exists := cache.Get(requestPath); exists { streamFileToResponse(data, w, r, t, c, log) return } baseDirectory := segments[len(segments)-3] accessKey := segments[len(segments)-2] filename := segments[len(segments)-1] torrent, _ := t.TorrentMap.Get(accessKey) if torrent == nil { log.Errorf("Cannot find torrent %s in the directory %s", accessKey, baseDirectory) http.Error(w, "File not found", http.StatusNotFound) return } file, _ := torrent.SelectedFiles.Get(filename) if file == nil { log.Errorf("Cannot find file from path %s", requestPath) http.Error(w, "File not found", http.StatusNotFound) return } if file.Link == "" { // This is a dead file, serve an alternate file log.Errorf("File %s is no longer available", filename) streamErrorVideo("https://www.youtube.com/watch?v=bGTqwt6vdcY", w, r, t, c, log) return } link := file.Link resp := t.UnrestrictUntilOk(link) if resp == nil { log.Errorf("The link cannot be unrestricted, file %s is no longer available", file.Path) // TODO: maybe repair the torrent? streamErrorVideo("https://www.youtube.com/watch?v=gea_FJrtFVA", w, r, t, c, log) return } else if resp.Filename != filename { actualExt := filepath.Ext(resp.Filename) expectedExt := filepath.Ext(filename) if actualExt != expectedExt && resp.Streamable != 1 { log.Errorf("File extension mismatch: %s and %s", filename, resp.Filename) streamErrorVideo("https://www.youtube.com/watch?v=t9VgOriBHwE", w, r, t, c, log) return } else { log.Errorf("Filename mismatch: %s and %s", filename, resp.Filename) } } cache.Add(requestPath, resp.Download) streamFileToResponse(resp.Download, w, r, t, c, log) } func streamFileToResponse(url string, w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, log *zap.SugaredLogger) { // Create a new request for the file download. req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { log.Errorf("Error creating new request: %v", err) streamErrorVideo("https://www.youtube.com/watch?v=H3NSrObyAxM", w, r, t, c, log) return } // copy range header if it exists if r.Header.Get("Range") != "" { req.Header.Add("Range", r.Header.Get("Range")) } // Create a custom HTTP client with a timeout. client := zurghttp.NewHTTPClient(c.GetToken(), 10) resp, err := client.Do(req) if err != nil { log.Errorf("Error downloading file %v", err) streamErrorVideo("https://www.youtube.com/watch?v=FSSd8cponAA", w, r, t, c, log) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusPartialContent { log.Errorf("Received a nonOK status code %d", resp.StatusCode) streamErrorVideo("https://www.youtube.com/watch?v=BcseUxviVqE", w, r, t, c, log) return } for k, vv := range resp.Header { for _, v := range vv { w.Header().Add(k, v) } } buf := make([]byte, c.GetNetworkBufferSize()) io.CopyBuffer(w, resp.Body, buf) } func streamErrorVideo(link string, w http.ResponseWriter, r *http.Request, t *torrent.TorrentManager, c config.ConfigInterface, log *zap.SugaredLogger) { resp := t.UnrestrictUntilOk(link) if resp == nil { http.Error(w, "REAL-DEBRID IS DOWN", http.StatusInternalServerError) return } streamFileToResponse(resp.Download, w, r, t, c, log) }