diff --git a/.air.toml b/.air.toml index 0ff91a4..d07453e 100644 --- a/.air.toml +++ b/.air.toml @@ -14,7 +14,7 @@ tmp_dir = "tmp" follow_symlink = false full_bin = "" include_dir = [] - include_ext = ["go", "tpl", "tmpl", "html"] + include_ext = ["go", "tpl", "tmpl", "html", ".json"] include_file = [] kill_delay = "0s" log = "build-errors.log" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 59eb50d..8ba5de9 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -40,8 +40,8 @@ jobs: push: true tags: cy01/blackhole:beta - - name: Build and push for main branch with version - if: github.ref == 'refs/heads/main' && steps.get_version.outputs.VERSION != '' + - name: Build and push for main branch + if: github.ref == 'refs/heads/main' uses: docker/build-push-action@v5 with: context: . @@ -49,13 +49,4 @@ jobs: push: true tags: | cy01/blackhole:latest - cy01/blackhole:${{ steps.get_version.outputs.VERSION }} - - - name: Build and push for main branch without version - if: github.ref == 'refs/heads/main' && steps.get_version.outputs.VERSION == '' - uses: docker/build-push-action@v5 - with: - context: . - platforms: linux/amd64,linux/arm64,linux/arm/v7 - push: true - tags: cy01/blackhole:latest \ No newline at end of file + cy01/blackhole:${{ steps.get_version.outputs.VERSION }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ba2e18..5754c17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,4 +91,9 @@ - Refraction of the code - -Fix Torbox bug - Update CI/CD -- Update Readme \ No newline at end of file +- Update Readme + +#### 0.3.1 + +- Add DebridLink Support +- Refactor error handling \ No newline at end of file diff --git a/README.md b/README.md index af96fee..aaf6224 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,16 @@ This is a Golang implementation go Torrent QbitTorrent with a **Real Debrid & To - Proxy support for the Arrs - Real Debrid Support - Torbox Support +- Debrid Link Support - Multi-Debrid Providers support +- UI for adding torrents directly to *arrs The proxy is useful in filtering out un-cached Real Debrid torrents ### Supported Debrid Providers - Real Debrid - Torbox +- Debrid Link ### Changelog @@ -66,7 +69,7 @@ Download the binary from the releases page and run it with the config file. "name": "torbox", "host": "https://api.torbox.app/v1", "api_key": "torbox_api_key", - "folder": "data/realdebrid/torrents/", + "folder": "data/torbox/torrents/", "rate_limit": "250/minute", "download_uncached": false, "check_cached": true @@ -79,6 +82,15 @@ Download the binary from the releases page and run it with the config file. "rate_limit": "250/minute", "download_uncached": false, "check_cached": false + }, + { + "name": "debridlink", + "host": "https://debrid-link.com/api/v2", + "api_key": "debridlink_key", + "folder": "data/debridlink/torrents/", + "rate_limit": "250/minute", + "download_uncached": false, + "check_cached": false } ], "proxy": { @@ -163,6 +175,12 @@ Setting Up Qbittorrent in Arr - Test - Save +### UI for adding torrents + +![UI](./doc/ui.png) + +The UI is a simple web interface that allows you to add torrents directly to the Arrs(Sonarr, Radarr, etc) + ### TODO - [ ] A proper name!!!! - [ ] Debrid diff --git a/common/request.go b/common/request.go index dce171c..a3b8cf9 100644 --- a/common/request.go +++ b/common/request.go @@ -75,7 +75,9 @@ func (c *RLHTTPClient) MakeRequest(req *http.Request) ([]byte, error) { b, _ := io.ReadAll(res.Body) statusOk := strconv.Itoa(res.StatusCode)[0] == '2' if !statusOk { - return nil, fmt.Errorf("unexpected status code: %d", res.StatusCode) + // Add status code error to the body + b = append(b, []byte(fmt.Sprintf("\nstatus code: %d", res.StatusCode))...) + return nil, fmt.Errorf(string(b)) } defer func(Body io.ReadCloser) { err := Body.Close() diff --git a/doc/ui.png b/doc/ui.png new file mode 100644 index 0000000..85c722b Binary files /dev/null and b/doc/ui.png differ diff --git a/pkg/debrid/debrid.go b/pkg/debrid/debrid.go index 3af5176..2672a13 100644 --- a/pkg/debrid/debrid.go +++ b/pkg/debrid/debrid.go @@ -147,6 +147,8 @@ func ProcessTorrent(d *DebridService, magnet *common.Magnet, a *arr.Arr, isSymli Size: magnet.Size, } + errs := make([]error, 0) + for index, db := range d.debrids { log.Println("Processing debrid: ", db.GetName()) logger := db.GetLogger() @@ -162,18 +164,21 @@ func ProcessTorrent(d *DebridService, magnet *common.Magnet, a *arr.Arr, isSymli } dbt, err := db.SubmitMagnet(debridTorrent) - if err != nil || dbt.Id == "" { - if dbt != nil { - dbt.Delete() - } - logger.Printf("Error submitting magnet: %s", err) + if dbt != nil { + dbt.Debrid = db + dbt.Arr = a + } + if err != nil || dbt == nil || dbt.Id == "" { + errs = append(errs, err) continue } logger.Printf("Torrent: %s submitted to %s", dbt.Name, db.GetName()) d.lastUsed = index - dbt.Debrid = db - dbt.Arr = a return db.CheckStatus(dbt, isSymlink) } - return nil, fmt.Errorf("failed to process torrent") + err := fmt.Errorf("failed to process torrent") + for _, e := range errs { + err = fmt.Errorf("%w\n%w", err, e) + } + return nil, err } diff --git a/pkg/debrid/debrid_link.go b/pkg/debrid/debrid_link.go index 6092d27..843873a 100644 --- a/pkg/debrid/debrid_link.go +++ b/pkg/debrid/debrid_link.go @@ -89,7 +89,7 @@ func (r *DebridLink) IsAvailable(infohashes []string) map[string]bool { func (r *DebridLink) GetTorrent(id string) (*Torrent, error) { torrent := &Torrent{} - url := fmt.Sprintf("%s/seedbox/list/?ids=%s", r.Host, id) + url := fmt.Sprintf("%s/seedbox/list?ids=%s", r.Host, id) req, _ := http.NewRequest(http.MethodGet, url, nil) resp, err := r.client.MakeRequest(req) if err != nil { @@ -113,6 +113,9 @@ func (r *DebridLink) GetTorrent(id string) (*Torrent, error) { } data := dt[0] status := "downloading" + if data.Status == 100 { + status = "downloaded" + } name := common.RemoveInvalidChars(data.Name) torrent.Id = data.ID torrent.Name = name @@ -185,8 +188,8 @@ func (r *DebridLink) SubmitMagnet(torrent *Torrent) (*Torrent, error) { func (r *DebridLink) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, error) { for { - torrent, err := r.GetTorrent(torrent.Id) - + t, err := r.GetTorrent(torrent.Id) + torrent = t if err != nil || torrent == nil { return torrent, err } @@ -248,6 +251,7 @@ func NewDebridLink(dc common.DebridConfig, cache *common.Cache) *DebridLink { rl := common.ParseRateLimit(dc.RateLimit) headers := map[string]string{ "Authorization": fmt.Sprintf("Bearer %s", dc.APIKey), + "Content-Type": "application/json", } client := common.NewRLHTTPClient(rl, headers) logger := common.NewLogger(dc.Name, os.Stdout) diff --git a/pkg/debrid/realdebrid.go b/pkg/debrid/realdebrid.go index 706ac39..0dab3e4 100644 --- a/pkg/debrid/realdebrid.go +++ b/pkg/debrid/realdebrid.go @@ -180,12 +180,11 @@ func (r *RealDebrid) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, er torrent.Status = status torrent.Debrid = r if status == "error" || status == "dead" || status == "magnet_error" { - return torrent, fmt.Errorf("torrent: %s has error", torrent.Name) + return torrent, fmt.Errorf("torrent: %s has error: %s", torrent.Name, status) } else if status == "waiting_files_selection" { files := GetTorrentFiles(data) torrent.Files = files if len(files) == 0 { - go torrent.Delete() return torrent, fmt.Errorf("no video files found") } filesId := make([]string, 0) @@ -214,7 +213,6 @@ func (r *RealDebrid) CheckStatus(torrent *Torrent, isSymlink bool) (*Torrent, er break } else if status == "downloading" { if !r.DownloadUncached { - go torrent.Delete() return torrent, fmt.Errorf("torrent: %s not cached", torrent.Name) } // Break out of the loop if the torrent is downloading. diff --git a/pkg/debrid/torrent.go b/pkg/debrid/torrent.go index 89315b7..bd28b6f 100644 --- a/pkg/debrid/torrent.go +++ b/pkg/debrid/torrent.go @@ -74,7 +74,11 @@ func (t *Torrent) GetMountFolder(rClonePath string) string { } func (t *Torrent) Delete() { + if t.Debrid == nil { + return + } t.Debrid.DeleteTorrent(t) + } type TorrentFile struct { diff --git a/pkg/qbit/server/torrent_handlers.go b/pkg/qbit/server/torrent_handlers.go index b724d44..5f831e1 100644 --- a/pkg/qbit/server/torrent_handlers.go +++ b/pkg/qbit/server/torrent_handlers.go @@ -50,7 +50,6 @@ func (s *Server) handleTorrentsAdd(w http.ResponseWriter, r *http.Request) { } ctx = context.WithValue(ctx, "isSymlink", isSymlink) - for _, url := range urlList { if err := s.qbit.AddMagnet(ctx, url, category); err != nil { s.logger.Printf("Error adding magnet: %v\n", err) @@ -59,13 +58,8 @@ func (s *Server) handleTorrentsAdd(w http.ResponseWriter, r *http.Request) { } } - if contentType == "multipart/form-data" { + if contentType == "multipart/form-data" && len(r.MultipartForm.File["torrents"]) > 0 { files := r.MultipartForm.File["torrents"] - if len(files) == 0 { - s.logger.Printf("No files provided\n") - http.Error(w, "No files provided", http.StatusBadRequest) - return - } for _, fileHeader := range files { if err := s.qbit.AddTorrent(ctx, fileHeader, category); err != nil { s.logger.Printf("Error adding torrent: %v\n", err) diff --git a/pkg/qbit/shared/torrent.go b/pkg/qbit/shared/torrent.go index 5e6497b..7ec61bc 100644 --- a/pkg/qbit/shared/torrent.go +++ b/pkg/qbit/shared/torrent.go @@ -21,13 +21,11 @@ import ( func (q *QBit) AddMagnet(ctx context.Context, url, category string) error { magnet, err := common.GetMagnetFromUrl(url) if err != nil { - q.logger.Printf("Error parsing magnet link: %v\n", err) - return err + return fmt.Errorf("error parsing magnet link: %w", err) } err = q.Process(ctx, magnet, category) if err != nil { - q.logger.Println("Failed to process magnet:", err) - return err + return fmt.Errorf("failed to process torrent: %w", err) } return nil } @@ -38,13 +36,11 @@ func (q *QBit) AddTorrent(ctx context.Context, fileHeader *multipart.FileHeader, var reader io.Reader = file magnet, err := common.GetMagnetFromFile(reader, fileHeader.Filename) if err != nil { - q.logger.Printf("Error reading file: %s", fileHeader.Filename) - return err + return fmt.Errorf("error reading file: %s \n %w", fileHeader.Filename, err) } err = q.Process(ctx, magnet, category) if err != nil { - q.logger.Println("Failed to process torrent:", err) - return err + return fmt.Errorf("failed to process torrent: %w", err) } return nil } @@ -58,6 +54,9 @@ func (q *QBit) Process(ctx context.Context, magnet *common.Magnet, category stri isSymlink := ctx.Value("isSymlink").(bool) debridTorrent, err := debrid.ProcessTorrent(q.Debrid, magnet, a, isSymlink) if err != nil || debridTorrent == nil { + if debridTorrent != nil { + go debridTorrent.Delete() + } if err == nil { err = fmt.Errorf("failed to process torrent") }