diff --git a/internal/app.go b/internal/app.go index 496da60..cdf2daf 100644 --- a/internal/app.go +++ b/internal/app.go @@ -44,11 +44,27 @@ func MainApp(configPath string) { os.Exit(1) } - apiClient := http.NewHTTPClient(config.GetToken(), config.GetRetriesUntilFailed(), config.GetApiTimeoutSecs(), false, config, log.Named("httpclient")) + apiClient := http.NewHTTPClient( + config.GetToken(), + config.GetRetriesUntilFailed(), + config.GetApiTimeoutSecs(), + false, + config, + log.Named("api_client"), + ) - rd := realdebrid.NewRealDebrid(apiClient, log.Named("realdebrid")) + unrestrictClient := http.NewHTTPClient( + config.GetToken(), + config.GetRetriesUntilFailed(), + config.GetDownloadTimeoutSecs(), + false, + config, + log.Named("unrestrict_client"), + ) - premium.MonitorPremiumStatus(rd, zurglog) + api := realdebrid.NewRealDebrid(apiClient, unrestrictClient, log.Named("realdebrid")) + + premium.MonitorPremiumStatus(api, zurglog) workerPool, err := ants.NewPool(config.GetNumOfWorkers()) if err != nil { @@ -58,13 +74,13 @@ func MainApp(configPath string) { defer workerPool.Release() utils.EnsureDirExists("data") // Ensure the data directory exists - torrentMgr := torrent.NewTorrentManager(config, rd, workerPool, log.Named("manager")) + torrentMgr := torrent.NewTorrentManager(config, api, workerPool, log.Named("manager")) - downloadClient := http.NewHTTPClient(config.GetToken(), config.GetRetriesUntilFailed(), config.GetDownloadTimeoutSecs(), true, config, log.Named("dlclient")) + downloadClient := http.NewHTTPClient(config.GetToken(), config.GetRetriesUntilFailed(), config.GetDownloadTimeoutSecs(), true, config, log.Named("download_client")) downloader := universal.NewDownloader(downloadClient) router := chi.NewRouter() - handlers.AttachHandlers(router, downloader, torrentMgr, config, rd, workerPool, log.Named("router")) + handlers.AttachHandlers(router, downloader, torrentMgr, config, api, workerPool, log.Named("router")) // go func() { // if err := netHttp.ListenAndServe(":6060", nil); err != nil && err != netHttp.ErrServerClosed { diff --git a/internal/config/types.go b/internal/config/types.go index b1b0672..84bbe3b 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -178,7 +178,7 @@ func (z *ZurgConfig) EnableDownloadMount() bool { func (z *ZurgConfig) GetApiTimeoutSecs() int { if z.ApiTimeoutSecs == 0 { - return 15 + return 60 } return z.ApiTimeoutSecs } diff --git a/pkg/http/client.go b/pkg/http/client.go index 67a2abe..af6a9c3 100644 --- a/pkg/http/client.go +++ b/pkg/http/client.go @@ -249,6 +249,9 @@ func (r *HTTPClient) shouldRetry(resp *http.Response, reqHasRangeHeader bool, er } } if err != nil && strings.Contains(err.Error(), "timeout") { + if resp.Request.URL.Host == "api.real-debrid.com" && !strings.Contains(resp.Request.URL.Path, "unrestrict/") { + r.log.Warnf("Request timed out, adjust the timeout value in the config") + } return 1 } if resp != nil { diff --git a/pkg/realdebrid/api.go b/pkg/realdebrid/api.go index b13be9c..88ae8d3 100644 --- a/pkg/realdebrid/api.go +++ b/pkg/realdebrid/api.go @@ -14,14 +14,16 @@ import ( ) type RealDebrid struct { - log *logutil.Logger - client *zurghttp.HTTPClient + client *zurghttp.HTTPClient + unrestrictClient *zurghttp.HTTPClient + log *logutil.Logger } -func NewRealDebrid(client *zurghttp.HTTPClient, log *logutil.Logger) *RealDebrid { +func NewRealDebrid(client, unrestrictClient *zurghttp.HTTPClient, log *logutil.Logger) *RealDebrid { return &RealDebrid{ - log: log, - client: client, + client: client, + unrestrictClient: unrestrictClient, + log: log, } } @@ -36,7 +38,7 @@ func (rd *RealDebrid) UnrestrictCheck(link string) (*Download, error) { } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - resp, err := rd.client.Do(req) + resp, err := rd.unrestrictClient.Do(req) if err != nil { rd.log.Errorf("Error when executing the unrestrict check request: %v", err) return nil, err @@ -60,6 +62,52 @@ func (rd *RealDebrid) UnrestrictCheck(link string) (*Download, error) { return &response, nil } +func (rd *RealDebrid) UnrestrictLink(link string, checkFirstByte bool) (*Download, error) { + data := url.Values{} + data.Set("link", link) + + req, err := http.NewRequest("POST", "https://api.real-debrid.com/rest/1.0/unrestrict/link", bytes.NewBufferString(data.Encode())) + if err != nil { + rd.log.Errorf("Error when creating a unrestrict link request: %v", err) + return nil, err + } + + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + // at this point, any errors mean that the link has expired and we need to repair it + resp, err := rd.unrestrictClient.Do(req) + if err != nil { + // rd.log.Errorf("Error when executing the unrestrict link request: %v", err) + return nil, fmt.Errorf("unrestrict link request failed: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + rd.log.Errorf("Unrestrict link request returned status code %d for link %s", resp.StatusCode, link) + // return nil, fmt.Errorf("unrestrict link request returned status code %d", resp.StatusCode) + } + + body, err := io.ReadAll(resp.Body) + if err != nil { + // rd.log.Errorf("Error when reading the body of unrestrict link response: %v", err) + return nil, fmt.Errorf("unreadable body: %v", err) + } + + var response Download + err = json.Unmarshal(body, &response) + if err != nil { + // rd.log.Errorf("Error when decoding unrestrict link JSON: %v", err) + return nil, fmt.Errorf("undecodable response: %v", err) + } + + if checkFirstByte && !rd.client.CanFetchFirstByte(response.Download) { + return nil, fmt.Errorf("can't fetch first byte") + } + + // rd.log.Debugf("Unrestricted link %s into %s", link, response.Download) + return &response, nil +} + // GetTorrents returns all torrents, paginated // if customLimit is 0, the default limit of 1000 is used func (rd *RealDebrid) GetTorrents(customLimit int, active bool) ([]Torrent, int, error) { @@ -270,52 +318,6 @@ func (rd *RealDebrid) GetActiveTorrentCount() (*ActiveTorrentCountResponse, erro return &response, nil } -func (rd *RealDebrid) UnrestrictLink(link string, checkFirstByte bool) (*Download, error) { - data := url.Values{} - data.Set("link", link) - - req, err := http.NewRequest("POST", "https://api.real-debrid.com/rest/1.0/unrestrict/link", bytes.NewBufferString(data.Encode())) - if err != nil { - rd.log.Errorf("Error when creating a unrestrict link request: %v", err) - return nil, err - } - - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - // at this point, any errors mean that the link has expired and we need to repair it - resp, err := rd.client.Do(req) - if err != nil { - // rd.log.Errorf("Error when executing the unrestrict link request: %v", err) - return nil, fmt.Errorf("unrestrict link request failed: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - rd.log.Errorf("Unrestrict link request returned status code %d for link %s", resp.StatusCode, link) - // return nil, fmt.Errorf("unrestrict link request returned status code %d", resp.StatusCode) - } - - body, err := io.ReadAll(resp.Body) - if err != nil { - // rd.log.Errorf("Error when reading the body of unrestrict link response: %v", err) - return nil, fmt.Errorf("unreadable body: %v", err) - } - - var response Download - err = json.Unmarshal(body, &response) - if err != nil { - // rd.log.Errorf("Error when decoding unrestrict link JSON: %v", err) - return nil, fmt.Errorf("undecodable response: %v", err) - } - - if checkFirstByte && !rd.client.CanFetchFirstByte(response.Download) { - return nil, fmt.Errorf("can't fetch first byte") - } - - // rd.log.Debugf("Unrestricted link %s into %s", link, response.Download) - return &response, nil -} - // GetDownloads returns all torrents, paginated func (rd *RealDebrid) GetDownloads(page, offset int) ([]Download, int, error) { baseURL := "https://api.real-debrid.com/rest/1.0/downloads"