diff --git a/internal/torrent/fixer.go b/internal/torrent/fixer.go index b793834..9bc586f 100644 --- a/internal/torrent/fixer.go +++ b/internal/torrent/fixer.go @@ -15,9 +15,9 @@ import ( // id_trigger: this means a specific torrent id's completion // commands: delete | repair -func (t *TorrentManager) fixerAddCommand(trigger, command string) { - t.log.Debugf("Adding fixer command: %s %s", trigger, command) - t.fixers.Set(trigger, command) +func (t *TorrentManager) registerFixer(torrentId, command string) { + t.log.Debugf("Adding fixer command: %s %s", torrentId, command) + t.fixers.Set(torrentId, command) t.writeFixersToFile() } @@ -27,59 +27,37 @@ func (t *TorrentManager) processFixers(instances []realdebrid.Torrent) { var toRedownload []*Torrent allTorrents, _ := t.DirectoryMap.Get(INT_ALL) for _, instance := range instances { - id := instance.ID - if !t.fixers.Has(id) { + if !t.fixers.Has(instance.ID) { continue } - command, _ := t.fixers.Pop(id) // delete the fixer if it's done + + oldTorrentId := instance.ID + command, _ := t.fixers.Pop(oldTorrentId) // delete the fixer if it's done switch command { + case "replaced": // id is old torrent id - t.log.Debugf("Deleting old id=%s because it's redundant to fixed %s ", id, instance.Name) - toDelete = append(toDelete, id) + t.log.Debugf("Deleting old id=%s because it's redundant to fixed torrent %s ", oldTorrentId, instance.Name) + toDelete = append(toDelete, oldTorrentId) continue case "download_failed": // id is failed fixer id - t.log.Debugf("Deleting failed fixer id=%s of torrent %s", id, instance.Name) - toDelete = append(toDelete, id) + t.log.Debugf("Deleting failed fixer id=%s of torrent %s", oldTorrentId, instance.Name) + toDelete = append(toDelete, oldTorrentId) continue case "repaired": // this torrent contains broken files if instance.Progress != 100 { - t.fixers.Set(id, command) // requeue the fixer + t.fixers.Set(oldTorrentId, command) // requeue the fixer, it's not done yet continue } - torrent := t.getMoreInfo(instance) - t.log.Debugf("Repairing torrent %s again now that fixer id=%s is done", t.GetKey(torrent), id) - repairMe, _ := allTorrents.Get(t.GetKey(torrent)) + fixedTorrent := t.getMoreInfo(instance) + t.log.Debugf("Repairing torrent %s again now that fixer id=%s is done", t.GetKey(fixedTorrent), oldTorrentId) + repairMe, _ := allTorrents.Get(t.GetKey(fixedTorrent)) toRedownload = append(toRedownload, repairMe) - toDelete = append(toDelete, id) + toDelete = append(toDelete, oldTorrentId) continue } - - // a new case: repaired_with: - // if strings.HasPrefix(command, "repaired_with:") { - // if instance.Progress != 100 { - // t.fixers.Set(id, command) // requeue the fixer - // continue - // } - // otherId := strings.TrimPrefix(command, "repaired_with:") - // for _, instance2 := range instances { - // if instance2.ID == otherId { - // if instance2.Progress != 100 { - // t.fixers.Set(id, command) // requeue the fixer - // break - // } - // torrent := t.getMoreInfo(instance2) - // t.log.Debugf("Repairing torrent %s again now that fixers ids=%s and %s are done", t.GetKey(torrent), id, otherId) - // repairMe, _ := allTorrents.Get(t.GetKey(torrent)) - // toRedownload = append(toRedownload, repairMe) - // toDelete = append(toDelete, id, otherId) - // break - // } - // } - // continue - // } } infoCache, _ := t.DirectoryMap.Get(INT_INFO_CACHE) diff --git a/internal/torrent/refresh.go b/internal/torrent/refresh.go index 34d23d4..15235f3 100644 --- a/internal/torrent/refresh.go +++ b/internal/torrent/refresh.go @@ -20,6 +20,7 @@ func (t *TorrentManager) refreshTorrents(isInitialRun bool) []string { t.log.Warnf("Cannot get torrents: %v", err) return nil } + t.log.Infof("Fetched %d torrents", len(instances)) infoChan := make(chan *Torrent, len(instances)) var wg sync.WaitGroup diff --git a/internal/torrent/repair.go b/internal/torrent/repair.go index dcd4136..92c1c87 100644 --- a/internal/torrent/repair.go +++ b/internal/torrent/repair.go @@ -211,16 +211,21 @@ func (t *TorrentManager) repair(torrent *Torrent) { oldTorrentIDs := torrent.DownloadedIDs.Union(torrent.InProgressIDs).ToSlice() + newlyDownloadedIds := make([]string, 0) group := make([]*File, 0) for _, file := range brokenFiles { group = append(group, file) - if len(group) == 100 { + if len(group) >= 200 { brokenFileIDs := getFileIDs(group) - _, err := t.redownloadTorrent(torrent, brokenFileIDs) + redownloadedInfo, err := t.redownloadTorrent(torrent, brokenFileIDs) if err != nil { t.log.Warnf("Cannot repair torrent %s by downloading broken files (error=%s) giving up", t.GetKey(torrent), err.Error()) + for _, newId := range newlyDownloadedIds { + t.registerFixer(newId, "download_failed") + } return } + newlyDownloadedIds = append(newlyDownloadedIds, redownloadedInfo.ID) group = make([]*File, 0) } } @@ -230,12 +235,15 @@ func (t *TorrentManager) repair(torrent *Torrent) { _, err := t.redownloadTorrent(torrent, brokenFileIDs) if err != nil { t.log.Warnf("Cannot repair torrent %s by downloading broken files (error=%s) giving up", t.GetKey(torrent), err.Error()) + for _, newId := range newlyDownloadedIds { + t.registerFixer(newId, "download_failed") + } return } } for _, oldId := range oldTorrentIDs { - t.fixerAddCommand(oldId, "replaced") + t.registerFixer(oldId, "replaced") } } @@ -369,51 +377,59 @@ func (t *TorrentManager) redownloadTorrent(torrent *Torrent, selection string) ( newTorrentID := resp.ID // sleep for 1 second to let RD process the magnet - time.Sleep(1 * time.Second) + time.Sleep(10 * time.Second) - // select files - err = t.Api.SelectTorrentFiles(newTorrentID, selection) - if err != nil { - t.fixerAddCommand(newTorrentID, "download_failed") - return nil, fmt.Errorf("cannot start redownloading: %v", err) - } + var info *realdebrid.TorrentInfo + for { + // select files + err = t.Api.SelectTorrentFiles(newTorrentID, selection) + if err != nil { + t.registerFixer(newTorrentID, "download_failed") + return nil, fmt.Errorf("cannot start redownloading: %v", err) + } + // sleep for 5 second to let RD process the magnet + time.Sleep(10 * time.Second) - // sleep for 1 second to let RD process the magnet - time.Sleep(1 * time.Second) + // see if the torrent is ready + info, err = t.Api.GetTorrentInfo(newTorrentID) + if err != nil { + t.registerFixer(newTorrentID, "download_failed") + return nil, fmt.Errorf("cannot get info on redownloaded torrent %s (id=%s) : %v", t.GetKey(torrent), newTorrentID, err) + } - // see if the torrent is ready - info, err := t.Api.GetTorrentInfo(newTorrentID) - if err != nil { - t.fixerAddCommand(newTorrentID, "download_failed") - return nil, fmt.Errorf("cannot get info on redownloaded torrent %s (id=%s) : %v", t.GetKey(torrent), newTorrentID, err) + if info.Status != "magnet_conversion" && info.Status != "waiting_files_selection" { + break + } } // documented status: magnet_error, magnet_conversion, waiting_files_selection, queued, downloading, downloaded, error, virus, compressing, uploading, dead - okStatuses := []string{"downloading", "downloaded", "uploading"} - // not compressing because we need playable files isOkStatus := false + okStatuses := []string{"downloading", "downloaded", "uploading", "queued", "compressing"} + // not compressing because we need playable files for _, status := range okStatuses { if info.Status == status { isOkStatus = true break } } + if !isOkStatus { - t.fixerAddCommand(info.ID, "download_failed") - return nil, fmt.Errorf("the redownloaded torrent %s (id=%s) is in a non-OK state: %s", t.GetKey(torrent), info.ID, info.Status) + t.registerFixer(info.ID, "download_failed") + return nil, fmt.Errorf("the redownloaded torrent %s is in a non-OK state: %s", t.GetKey(torrent), info.Status) } // check if incorrect number of links selectionCount := len(strings.Split(selection, ",")) if info.Progress == 100 && len(info.Links) != selectionCount { - t.fixerAddCommand(newTorrentID, "download_failed") - return nil, fmt.Errorf("it did not fix the issue for %s (id=%s), only got %d files but we need %d, undoing", t.GetKey(torrent), info.ID, len(info.Links), selectionCount) + t.registerFixer(newTorrentID, "download_failed") + return nil, fmt.Errorf("torrent %s only got %d links but we need %d", t.GetKey(torrent), len(info.Links), selectionCount) } t.log.Infof("Redownloading torrent %s successful (id=%s, progress=%d)", t.GetKey(torrent), info.ID, info.Progress) for _, id := range oldTorrentIDs { - t.fixerAddCommand(id, "replaced") + t.registerFixer(id, "replaced") } + return info, nil } diff --git a/internal/universal/check.go b/internal/universal/check.go index b3b38cd..c0fe3ce 100644 --- a/internal/universal/check.go +++ b/internal/universal/check.go @@ -29,7 +29,7 @@ func CheckFile(directory, torrentName, fileName string, w http.ResponseWriter, r file, ok := torrent.SelectedFiles.Get(fileName) if !ok || file.IsDeleted { log.Warnf("Cannot find file %s from path %s", fileName, req.URL.Path) - http.Error(w, "Cannot find file", http.StatusNotFound) + http.Error(w, "File not found", http.StatusNotFound) return } contentType := getContentMimeType(fileName) diff --git a/internal/universal/downloader.go b/internal/universal/downloader.go index c2e1bcd..51b5da0 100644 --- a/internal/universal/downloader.go +++ b/internal/universal/downloader.go @@ -55,13 +55,8 @@ func (dl *Downloader) DownloadFile( return } - // log.Debugf("Opening file %s from torrent %s (%s)", fileName, torMgr.GetKey(torrent), file.Link) if file.IsBroken { - if cfg.EnableRepair() { - http.Error(resp, "File is temporarily unavailable", http.StatusNotFound) - } else { - http.Error(resp, "File is not available", http.StatusNotFound) - } + http.Error(resp, "File is not available", http.StatusNotFound) return } @@ -85,7 +80,7 @@ func (dl *Downloader) DownloadFile( if actualExt != expectedExt && unrestrict.Streamable != 1 { log.Warnf("File was changed and is not streamable: %s and %s (link=%s)", fileName, unrestrict.Filename, unrestrict.Link) } else { - log.Warnf("File mismatch: %s and %s", fileName, unrestrict.Filename) + log.Warnf("Filename mismatch: %s and %s", fileName, unrestrict.Filename) } } if cfg.ShouldServeFromRclone() { @@ -133,29 +128,23 @@ func (dl *Downloader) streamFileToResponse( cfg config.ConfigInterface, log *logutil.Logger, ) { - // Create a new request for the file download. + // Create a new request for the file download dlReq, err := http.NewRequest(http.MethodGet, unrestrict.Download, nil) if err != nil { if file != nil { log.Errorf("Error creating new request for file %s: %v", file.Path, err) } - http.Error(resp, "File is not available", http.StatusInternalServerError) + http.Error(resp, "File is not available", http.StatusNotFound) return } - // copy range header if it exists - // rangeLog := "" + // Add the range header if it exists if req.Header.Get("Range") != "" { dlReq.Header.Add("Range", req.Header.Get("Range")) - // rangeLog = " (range: " + req.Header.Get("Range") + ")" + log.Debugf("Range request for file %s: %s", unrestrict.Download, req.Header.Get("Range")) } - // if torrent != nil { - // log.Debugf("Downloading unrestricted link %s from torrent %s (%s)%s", unrestrict.Download, torMgr.GetKey(torrent), unrestrict.Link, rangeLog) - // } else { - // log.Debugf("Downloading unrestricted link %s (%s)%s", unrestrict.Download, unrestrict.Link, rangeLog) - // } - + // Perform the request downloadResp, err := dl.client.Do(dlReq) if err != nil { if file != nil && unrestrict.Streamable == 1 { @@ -174,6 +163,7 @@ func (dl *Downloader) streamFileToResponse( } defer downloadResp.Body.Close() + // Check if the download was not successful if downloadResp.StatusCode/100 != 2 { if file != nil && unrestrict.Streamable == 1 { file.IsBroken = true @@ -190,14 +180,13 @@ func (dl *Downloader) streamFileToResponse( return } + // Copy the headers from the download response to the response for k, vv := range downloadResp.Header { for _, v := range vv { resp.Header().Add(k, v) } } - // log.Debugf("Serving file %s%s", unrestrict.Download, rangeLog) - buf := make([]byte, cfg.GetNetworkBufferSize()) io.CopyBuffer(resp, downloadResp.Body, buf) } diff --git a/pkg/realdebrid/api.go b/pkg/realdebrid/api.go index fce011d..50f10bd 100644 --- a/pkg/realdebrid/api.go +++ b/pkg/realdebrid/api.go @@ -192,7 +192,9 @@ func (rd *RealDebrid) GetTorrents(onlyOne bool) ([]Torrent, int, error) { allTorrents = append(allTorrents, res.torrents...) } - if len(allTorrents) >= totalCount { + rd.log.Debugf("Got %d/%d torrents", len(allTorrents), totalCount) + + if len(allTorrents) >= totalCount || page >= maxPages { break }