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