Fix repair fsm
This commit is contained in:
@@ -352,18 +352,15 @@ func (t *TorrentManager) mergeTorrents(existing, toMerge *Torrent) *Torrent {
|
||||
|
||||
brokenCount := 0
|
||||
okCount := 0
|
||||
wtfCount := 0
|
||||
mergedTorrent.SelectedFiles.IterCb(func(key string, file *File) {
|
||||
if file.State.Is("broken_file") {
|
||||
brokenCount++
|
||||
} else if file.State.Is("ok_file") {
|
||||
okCount++
|
||||
} else {
|
||||
wtfCount++
|
||||
}
|
||||
})
|
||||
|
||||
if brokenCount == 0 && okCount > 0 && mergedTorrent.State.Can("mark_as_repaired") {
|
||||
if brokenCount == 0 && okCount > 0 {
|
||||
if err := mergedTorrent.State.Event(context.Background(), "mark_as_repaired"); err != nil {
|
||||
t.log.Errorf("Cannot repair torrent %s: %v", t, t.GetKey(mergedTorrent), err)
|
||||
}
|
||||
|
||||
@@ -77,6 +77,16 @@ func (t *TorrentManager) invokeRepair(torrent *Torrent) {
|
||||
|
||||
// TriggerRepair allows an on-demand repair to be initiated.
|
||||
func (t *TorrentManager) TriggerRepair(torrent *Torrent) {
|
||||
if torrent != nil {
|
||||
if err := torrent.State.Event(context.Background(), "break_torrent"); err != nil {
|
||||
t.log.Errorf("Failed to mark torrent %s as broken: %v", t.GetKey(torrent), err)
|
||||
return
|
||||
}
|
||||
if !t.Config.EnableRepair() {
|
||||
t.log.Warnf("Repair is disabled, skipping repair for torrent %s", t.GetKey(torrent))
|
||||
return
|
||||
}
|
||||
}
|
||||
t.repairTrigger <- torrent
|
||||
}
|
||||
|
||||
@@ -155,17 +165,14 @@ func (t *TorrentManager) Repair(torrent *Torrent, wg *sync.WaitGroup) {
|
||||
})
|
||||
}
|
||||
|
||||
// repairman
|
||||
func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
torrentIDs := torrent.DownloadedIDs.ToSlice()
|
||||
t.log.Infof("Started repair process for torrent %s (ids=%v)", t.GetKey(torrent), torrentIDs)
|
||||
t.log.Infof("Started repair process for torrent %s (ids=%v)", t.GetKey(torrent), torrent.DownloadedIDs.ToSlice())
|
||||
|
||||
// handle torrents with incomplete links for selected files
|
||||
// torrent can be rar'ed by RD, so we need to check for that
|
||||
if torrent.UnassignedLinks.Cardinality() > 0 && !t.assignUnassignedLinks(torrent) {
|
||||
if torrent.UnassignedLinks.Cardinality() > 0 && !t.assignLinks(torrent) {
|
||||
return
|
||||
}
|
||||
|
||||
// get all broken files
|
||||
brokenFiles, allBroken := getBrokenFiles(torrent)
|
||||
|
||||
// first step: redownload the whole torrent
|
||||
@@ -186,6 +193,7 @@ func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
t.log.Infof("Successfully repaired torrent %s by redownloading all files", t.GetKey(torrent))
|
||||
return
|
||||
} else if info != nil && info.Progress != 100 {
|
||||
@@ -256,7 +264,7 @@ func (t *TorrentManager) repair(torrent *Torrent) {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TorrentManager) assignUnassignedLinks(torrent *Torrent) bool {
|
||||
func (t *TorrentManager) assignLinks(torrent *Torrent) bool {
|
||||
unassignedTotal := torrent.UnassignedLinks.Cardinality()
|
||||
t.log.Infof("Trying to assign %d links to the %d selected of incomplete torrent %s", unassignedTotal, torrent.SelectedFiles.Count(), t.GetKey(torrent))
|
||||
|
||||
@@ -386,26 +394,29 @@ func (t *TorrentManager) redownloadTorrent(torrent *Torrent, selection []string)
|
||||
} else if strings.Contains(err.Error(), "allowed") {
|
||||
t.markAsUnfixable(torrent, "torrent not allowed")
|
||||
}
|
||||
return nil, fmt.Errorf("cannot redownload torrent: %v", err)
|
||||
return nil, fmt.Errorf("cannot add magnet of torrent %s (hash=%s): %v", t.GetKey(torrent), torrent.Hash, err)
|
||||
}
|
||||
|
||||
newTorrentID := resp.ID
|
||||
|
||||
// sleep for 1 second to let RD process the magnet
|
||||
time.Sleep(1 * time.Second)
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
var info *realdebrid.TorrentInfo
|
||||
retries := 0
|
||||
for {
|
||||
// select files
|
||||
retries++
|
||||
if retries > 10 {
|
||||
t.setToBinImmediately(newTorrentID)
|
||||
return nil, fmt.Errorf("cannot start redownloading torrent %s (id=%s): too many retries", t.GetKey(torrent), newTorrentID)
|
||||
}
|
||||
|
||||
err = t.api.SelectTorrentFiles(newTorrentID, finalSelection)
|
||||
if err != nil {
|
||||
t.setToBinImmediately(newTorrentID)
|
||||
return nil, fmt.Errorf("cannot start redownloading torrent %s (id=%s): %v", t.GetKey(torrent), newTorrentID, err)
|
||||
}
|
||||
// sleep for 2 second to let RD process the magnet
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// see if the torrent is ready
|
||||
info, err = t.api.GetTorrentInfo(newTorrentID)
|
||||
if err != nil {
|
||||
t.setToBinImmediately(newTorrentID)
|
||||
@@ -415,9 +426,6 @@ func (t *TorrentManager) redownloadTorrent(torrent *Torrent, selection []string)
|
||||
if info.Status == "magnet_conversion" {
|
||||
time.Sleep(60 * time.Second)
|
||||
continue
|
||||
} else if info.Status == "waiting_files_selection" {
|
||||
time.Sleep(10 * time.Second)
|
||||
return nil, fmt.Errorf("torrent %s (id=%s) is stuck on waiting_files_selection", t.GetKey(torrent), newTorrentID)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -434,7 +442,7 @@ func (t *TorrentManager) redownloadTorrent(torrent *Torrent, selection []string)
|
||||
}
|
||||
|
||||
if !isOkStatus {
|
||||
t.setToBinImmediately(info.ID)
|
||||
t.setToBinImmediately(newTorrentID)
|
||||
return nil, fmt.Errorf("the redownloaded torrent %s is in a non-OK state: %s", t.GetKey(torrent), info.Status)
|
||||
}
|
||||
|
||||
@@ -540,23 +548,21 @@ func (t *TorrentManager) isStillBroken(info *realdebrid.TorrentInfo, brokenFiles
|
||||
State: NewFileState("broken_file"),
|
||||
})
|
||||
}
|
||||
if len(selectedFiles) == len(info.Links) {
|
||||
// all links are still intact! good!
|
||||
for i, file := range selectedFiles {
|
||||
file.Link = info.Links[i]
|
||||
if strings.HasPrefix(file.Link, "https://real-debrid.com/d/") {
|
||||
file.Link = file.Link[0:39]
|
||||
}
|
||||
if err := file.State.Event(context.Background(), "repair_file"); err != nil {
|
||||
t.log.Errorf("Failed to mark file %s as repaired: %v", file.Path, err)
|
||||
return true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if we can't assign links then it's still broken
|
||||
if len(selectedFiles) != len(info.Links) {
|
||||
return true
|
||||
}
|
||||
|
||||
for i, file := range selectedFiles {
|
||||
file.Link = info.Links[i]
|
||||
if strings.HasPrefix(file.Link, "https://real-debrid.com/d/") {
|
||||
file.Link = file.Link[0:39]
|
||||
}
|
||||
if err := file.State.Event(context.Background(), "repair_file"); err != nil {
|
||||
t.log.Errorf("Failed to mark file %s as repaired: %v", file.Path, err)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if len(brokenFiles) == 0 {
|
||||
// just check for the last file
|
||||
brokenFiles = append(brokenFiles, selectedFiles[len(selectedFiles)-1])
|
||||
@@ -565,11 +571,8 @@ func (t *TorrentManager) isStillBroken(info *realdebrid.TorrentInfo, brokenFiles
|
||||
// check if the broken files can now be unrestricted
|
||||
for _, oldFile := range brokenFiles {
|
||||
for idx, newFile := range selectedFiles {
|
||||
if oldFile.Bytes == newFile.Bytes {
|
||||
unrestrict := t.UnrestrictFileUntilOk(selectedFiles[idx])
|
||||
if unrestrict == nil || oldFile.Bytes != unrestrict.Filesize {
|
||||
return true
|
||||
}
|
||||
if oldFile.ID == newFile.ID && t.UnrestrictFileUntilOk(selectedFiles[idx]) == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,17 +63,13 @@ func (dl *Downloader) DownloadFile(
|
||||
|
||||
unrestrict := torMgr.UnrestrictFileUntilOk(file)
|
||||
if unrestrict == nil {
|
||||
log.Warnf("File %s cannot be unrestricted (link=%s)", fileName, file.Link)
|
||||
if err := file.State.Event(context.Background(), "break_file"); err != nil {
|
||||
log.Errorf("File %s is stale: %v", fileName, err)
|
||||
http.Error(resp, "File is stale, please try again", http.StatusLocked)
|
||||
return
|
||||
}
|
||||
if cfg.EnableRepair() {
|
||||
log.Warnf("File %s cannot be unrestricted (link=%s) (repairing...)", fileName, file.Link)
|
||||
torMgr.TriggerRepair(torrent)
|
||||
} else {
|
||||
log.Warnf("Repair is disabled, skipping repair for unavailable file %s (link=%s)", fileName, file.Link)
|
||||
}
|
||||
torMgr.TriggerRepair(torrent)
|
||||
http.Error(resp, "File is not available", http.StatusNotFound)
|
||||
return
|
||||
} else {
|
||||
@@ -152,20 +148,14 @@ func (dl *Downloader) streamFileToResponse(
|
||||
// Perform the request
|
||||
downloadResp, err := dl.client.Do(dlReq)
|
||||
if err != nil {
|
||||
if file != nil && unrestrict.Streamable == 1 {
|
||||
log.Warnf("Cannot download file %s: %v", unrestrict.Download, err)
|
||||
if file != nil {
|
||||
if err := file.State.Event(context.Background(), "break_file"); err != nil {
|
||||
log.Errorf("File %s is stale: %v", file.Path, err)
|
||||
http.Error(resp, "File is stale, please try again", http.StatusLocked)
|
||||
return
|
||||
}
|
||||
if cfg.EnableRepair() && torrent != nil {
|
||||
log.Warnf("Cannot download file %s: %v (repairing...)", unrestrict.Download, err)
|
||||
torMgr.TriggerRepair(torrent)
|
||||
} else {
|
||||
log.Warnf("Repair is disabled, skipping repair for unavailable file %s (link=%s)", file.Path, file.Link)
|
||||
}
|
||||
} else {
|
||||
log.Warnf("Cannot download file %s: %v", unrestrict.Download, err)
|
||||
torMgr.TriggerRepair(torrent)
|
||||
}
|
||||
http.Error(resp, "File is not available", http.StatusNotFound)
|
||||
return
|
||||
@@ -174,20 +164,14 @@ func (dl *Downloader) streamFileToResponse(
|
||||
|
||||
// Check if the download was not successful
|
||||
if downloadResp.StatusCode/100 != 2 {
|
||||
if file != nil && unrestrict.Streamable == 1 {
|
||||
log.Warnf("Received a %s status code for file %s", downloadResp.Status, unrestrict.Filename)
|
||||
if file != nil {
|
||||
if err := file.State.Event(context.Background(), "break_file"); err != nil {
|
||||
log.Errorf("File %s is stale: %v", file.Path, err)
|
||||
http.Error(resp, "File is stale, please try again", http.StatusLocked)
|
||||
return
|
||||
}
|
||||
if cfg.EnableRepair() && torrent != nil {
|
||||
log.Warnf("Received a %s status code for file %s (repairing...)", downloadResp.Status, file.Path)
|
||||
torMgr.TriggerRepair(torrent)
|
||||
} else {
|
||||
log.Warnf("Repair is disabled, skipping repair for unavailable file %s (link=%s)", file.Path, file.Link)
|
||||
}
|
||||
} else {
|
||||
log.Warnf("Received a %s status code for file %s", downloadResp.Status, unrestrict.Download)
|
||||
torMgr.TriggerRepair(torrent)
|
||||
}
|
||||
http.Error(resp, "File is not available", http.StatusNotFound)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user