Repair rework
This commit is contained in:
@@ -128,7 +128,7 @@ func (t *TorrentManager) assignedDirectoryCb(tor *Torrent, cb func(string)) {
|
|||||||
tor.SelectedFiles.IterCb(func(key string, file *File) {
|
tor.SelectedFiles.IterCb(func(key string, file *File) {
|
||||||
filenames = append(filenames, filepath.Base(file.Path))
|
filenames = append(filenames, filepath.Base(file.Path))
|
||||||
fileSizes = append(fileSizes, file.Bytes)
|
fileSizes = append(fileSizes, file.Bytes)
|
||||||
if unplayable && utils.IsStreamable(file.Path) {
|
if !tor.Unfixable && unplayable && utils.IsStreamable(file.Path) {
|
||||||
unplayable = false
|
unplayable = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -154,22 +154,7 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *Torrent {
|
|||||||
Link: "", // no link yet
|
Link: "", // no link yet
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(selectedFiles) > len(info.Links) && info.Progress == 100 {
|
if len(selectedFiles) == len(info.Links) {
|
||||||
if len(info.Links) == 1 {
|
|
||||||
// this usually is a rar file issue
|
|
||||||
if t.Config.ShouldDeleteRarFiles() {
|
|
||||||
t.log.Warnf("Torrent %s id=%s is a rar file, it cannot be repaired. Deleting...", info.Name, info.ID)
|
|
||||||
t.Api.DeleteTorrent(info.ID)
|
|
||||||
} else {
|
|
||||||
t.log.Warnf("Torrent %s id=%s is a rar file, it cannot be repaired as it's a known Real-Debrid limitation. zurg recommends you delete this torrent or add auto_delete_rar_torrents: true in your config.yml", info.Name, info.ID)
|
|
||||||
torrent.Unfixable = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
t.log.Warnf("Torrent id=%s is partly expired. It has %d selected files but only %d links", info.ID, len(selectedFiles), len(info.Links))
|
|
||||||
torrent.ForRepair = true
|
|
||||||
}
|
|
||||||
} else if len(selectedFiles) == len(info.Links) {
|
|
||||||
// all links are still intact! good!
|
// all links are still intact! good!
|
||||||
for i, file := range selectedFiles {
|
for i, file := range selectedFiles {
|
||||||
file.Link = info.Links[i]
|
file.Link = info.Links[i]
|
||||||
@@ -209,6 +194,7 @@ func (t *TorrentManager) mergeToMain(existing, toMerge *Torrent) Torrent {
|
|||||||
mainTorrent.Hash = existing.Hash
|
mainTorrent.Hash = existing.Hash
|
||||||
mainTorrent.DownloadedIDs = mapset.NewSet[string]()
|
mainTorrent.DownloadedIDs = mapset.NewSet[string]()
|
||||||
mainTorrent.InProgressIDs = mapset.NewSet[string]()
|
mainTorrent.InProgressIDs = mapset.NewSet[string]()
|
||||||
|
mainTorrent.Unfixable = existing.Unfixable || toMerge.Unfixable
|
||||||
|
|
||||||
// this function triggers only when we have a new DownloadedID
|
// this function triggers only when we have a new DownloadedID
|
||||||
toMerge.DownloadedIDs.Difference(existing.DownloadedIDs).Each(func(id string) bool {
|
toMerge.DownloadedIDs.Difference(existing.DownloadedIDs).Each(func(id string) bool {
|
||||||
|
|||||||
@@ -20,10 +20,16 @@ func (t *TorrentManager) repairAll() {
|
|||||||
allTorrents, _ := t.DirectoryMap.Get(INT_ALL)
|
allTorrents, _ := t.DirectoryMap.Get(INT_ALL)
|
||||||
var toRepair []*Torrent
|
var toRepair []*Torrent
|
||||||
allTorrents.IterCb(func(_ string, torrent *Torrent) {
|
allTorrents.IterCb(func(_ string, torrent *Torrent) {
|
||||||
if torrent.AnyInProgress() && torrent.ForRepair {
|
if torrent.AnyInProgress() || torrent.Unfixable {
|
||||||
t.log.Warnf("Skipping %s for repairs because it is still in progress", torrent.AccessKey)
|
|
||||||
return
|
return
|
||||||
} else if torrent.ForRepair && !torrent.Unfixable {
|
}
|
||||||
|
hasBrokenFiles := false
|
||||||
|
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
|
||||||
|
if file.Link == "repair" || file.Link == "" {
|
||||||
|
hasBrokenFiles = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if hasBrokenFiles {
|
||||||
toRepair = append(toRepair, torrent)
|
toRepair = append(toRepair, torrent)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -68,54 +74,60 @@ func (t *TorrentManager) repair(torrent *Torrent) {
|
|||||||
t.log.Infof("Successfully downloaded torrent %s to repair it", torrent.AccessKey)
|
t.log.Infof("Successfully downloaded torrent %s to repair it", torrent.AccessKey)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
t.log.Warnf("Failed to repair by reinserting torrent %s", torrent.AccessKey)
|
t.log.Warnf("Failed to repair by reinserting torrent %s, will only redownload broken files...", torrent.AccessKey)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.log.Warnf("Torrent %s is not older than %d hours to be repaired by reinsertion", torrent.AccessKey, EXPIRED_LINK_TOLERANCE_HOURS)
|
t.log.Warnf("Torrent %s is not older than %d hours to be repaired by reinsertion, will only redownload broken files...", torrent.AccessKey, EXPIRED_LINK_TOLERANCE_HOURS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleep for 30 seconds to let the torrent accumulate more broken files if scanning
|
// sleep for 30 seconds to let the torrent accumulate more broken files if scanning
|
||||||
time.Sleep(30 * time.Second)
|
time.Sleep(30 * time.Second)
|
||||||
// second solution: add only the missing files
|
// second solution: add only the broken files
|
||||||
var missingFiles []File
|
var brokenFiles []File
|
||||||
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
|
torrent.SelectedFiles.IterCb(func(_ string, file *File) {
|
||||||
if file.Link == "repair" || file.Link == "" {
|
if file.Link == "repair" || file.Link == "" {
|
||||||
missingFiles = append(missingFiles, *file)
|
brokenFiles = append(brokenFiles, *file)
|
||||||
file.Link = "repairing"
|
file.Link = "repairing"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
t.log.Debugf("During repair, zurg found %d broken files for torrent %s", len(missingFiles), torrent.AccessKey)
|
t.log.Debugf("During repair, zurg found %d broken files for torrent %s", len(brokenFiles), torrent.AccessKey)
|
||||||
|
|
||||||
if len(missingFiles) == 1 && torrent.SelectedFiles.Count() == 1 {
|
// todo: to verify removed logic when there's only 1 selected file selected and it's broken
|
||||||
t.log.Warnf("Torrent %s is unrepairable, the lone file is expired but still cached in Real-Debrid", torrent.AccessKey)
|
|
||||||
return
|
// todo: handle rar'ed torrents
|
||||||
} else if len(missingFiles) == 1 && torrent.SelectedFiles.Count() > 1 {
|
|
||||||
|
if len(brokenFiles) == 1 && torrent.SelectedFiles.Count() > 1 {
|
||||||
// if we download a single file, it will be named differently
|
// if we download a single file, it will be named differently
|
||||||
// so we need to download 1 extra file to preserve the name
|
// so we need to download 1 extra file to preserve the name
|
||||||
// this is only relevant if we enable retain_rd_torrent_name
|
// this is only relevant if we enable retain_rd_torrent_name
|
||||||
// add the first file link encountered with a prefix of http
|
// add the first file link encountered with a prefix of http
|
||||||
t.log.Debugf("Torrent %s has only 1 missing file, adding 1 extra file to preserve the name", torrent.AccessKey)
|
t.log.Debugf("Torrent %s has only 1 broken file, adding 1 extra file to preserve the name", torrent.AccessKey)
|
||||||
for _, file := range torrent.SelectedFiles.Items() {
|
for _, file := range torrent.SelectedFiles.Items() {
|
||||||
if strings.HasPrefix(file.Link, "http") {
|
if strings.HasPrefix(file.Link, "http") {
|
||||||
missingFiles = append(missingFiles, *file)
|
brokenFiles = append(brokenFiles, *file)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(missingFiles) > 0 {
|
if len(brokenFiles) > 0 {
|
||||||
t.log.Infof("Redownloading the %d missing files for torrent %s", len(missingFiles), torrent.AccessKey)
|
t.log.Infof("Redownloading the %d broken files for torrent %s", len(brokenFiles), torrent.AccessKey)
|
||||||
missingFileIDs := strings.Join(getFileIDs(missingFiles), ",")
|
brokenFileIDs := strings.Join(getFileIDs(brokenFiles), ",")
|
||||||
t.reinsertTorrent(torrent, missingFileIDs)
|
if t.reinsertTorrent(torrent, brokenFileIDs) {
|
||||||
|
t.log.Infof("Successfully downloaded torrent %s to repair it", torrent.AccessKey)
|
||||||
|
} else {
|
||||||
|
t.log.Warnf("Failed to repair torrent %s", torrent.AccessKey)
|
||||||
|
torrent.Unfixable = true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
t.log.Warnf("Torrent %s has no missing files to repair", torrent.AccessKey)
|
t.log.Warnf("Torrent %s has no broken files to repair", torrent.AccessKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string) bool {
|
func (t *TorrentManager) reinsertTorrent(torrent *Torrent, brokenFiles string) bool {
|
||||||
oldTorrentIDs := make([]string, 0)
|
oldTorrentIDs := make([]string, 0)
|
||||||
// missing files means missing links
|
// broken files means broken links
|
||||||
// if missingFiles is not provided
|
// if brokenFiles is not provided
|
||||||
if missingFiles == "" {
|
if brokenFiles == "" {
|
||||||
// only replace the torrent if we are reinserting all files
|
// only replace the torrent if we are reinserting all files
|
||||||
oldTorrentIDs = torrent.DownloadedIDs.ToSlice()
|
oldTorrentIDs = torrent.DownloadedIDs.ToSlice()
|
||||||
tmpSelection := ""
|
tmpSelection := ""
|
||||||
@@ -125,7 +137,7 @@ func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string)
|
|||||||
if tmpSelection == "" {
|
if tmpSelection == "" {
|
||||||
return true // nothing to repair
|
return true // nothing to repair
|
||||||
} else {
|
} else {
|
||||||
missingFiles = tmpSelection[:len(tmpSelection)-1]
|
brokenFiles = tmpSelection[:len(tmpSelection)-1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +151,7 @@ func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string)
|
|||||||
|
|
||||||
// select files
|
// select files
|
||||||
newTorrentID := resp.ID
|
newTorrentID := resp.ID
|
||||||
err = t.Api.SelectTorrentFiles(newTorrentID, missingFiles)
|
err = t.Api.SelectTorrentFiles(newTorrentID, brokenFiles)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.log.Warnf("Cannot start redownloading: %v", err)
|
t.log.Warnf("Cannot start redownloading: %v", err)
|
||||||
t.Api.DeleteTorrent(newTorrentID)
|
t.Api.DeleteTorrent(newTorrentID)
|
||||||
@@ -169,9 +181,9 @@ func (t *TorrentManager) reinsertTorrent(torrent *Torrent, missingFiles string)
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
missingCount := len(strings.Split(missingFiles, ","))
|
brokenCount := len(strings.Split(brokenFiles, ","))
|
||||||
if len(info.Links) != missingCount {
|
if len(info.Links) != brokenCount {
|
||||||
t.log.Infof("It did not fix the issue for id=%s, only got %d files but we need %d, undoing", info.ID, len(info.Links), missingCount)
|
t.log.Infof("It did not fix the issue for id=%s, only got %d files but we need %d, undoing", info.ID, len(info.Links), brokenCount)
|
||||||
t.Api.DeleteTorrent(newTorrentID)
|
t.Api.DeleteTorrent(newTorrentID)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ type Torrent struct {
|
|||||||
Hash string `json:"Hash"`
|
Hash string `json:"Hash"`
|
||||||
SelectedFiles cmap.ConcurrentMap[string, *File] `json:"-"`
|
SelectedFiles cmap.ConcurrentMap[string, *File] `json:"-"`
|
||||||
LatestAdded string `json:"LatestAdded"`
|
LatestAdded string `json:"LatestAdded"`
|
||||||
ForRepair bool `json:"ForRepair"`
|
|
||||||
Unfixable bool `json:"Unfixable"`
|
Unfixable bool `json:"Unfixable"`
|
||||||
DownloadedIDs mapset.Set[string] `json:"DownloadedIDs"`
|
DownloadedIDs mapset.Set[string] `json:"DownloadedIDs"`
|
||||||
InProgressIDs mapset.Set[string] `json:"InProgressIDs"`
|
InProgressIDs mapset.Set[string] `json:"InProgressIDs"`
|
||||||
|
|||||||
@@ -54,10 +54,16 @@ func getContentMimeType(filePath string) string {
|
|||||||
return "video/mp4"
|
return "video/mp4"
|
||||||
case ".avi":
|
case ".avi":
|
||||||
return "video/x-msvideo"
|
return "video/x-msvideo"
|
||||||
|
case ".wmv":
|
||||||
|
return "video/x-ms-wmv"
|
||||||
|
case ".m4v":
|
||||||
|
return "video/x-m4v"
|
||||||
case ".mp3":
|
case ".mp3":
|
||||||
return "audio/mpeg"
|
return "audio/mpeg"
|
||||||
case ".rar":
|
case ".rar":
|
||||||
return "application/x-rar-compressed"
|
return "application/x-rar-compressed"
|
||||||
|
case ".zip":
|
||||||
|
return "application/zip"
|
||||||
default:
|
default:
|
||||||
return "application/octet-stream"
|
return "application/octet-stream"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user