Remove immediate bin
This commit is contained in:
@@ -87,8 +87,13 @@ func MainApp(configPath string) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
hosts := repo.GetOptimalHosts(config.GetNumberOfHosts(), config.ShouldForceIPv6())
|
hosts := repo.GetOptimalHosts(config.GetNumberOfHosts(), config.ShouldForceIPv6())
|
||||||
|
if len(hosts) == 0 {
|
||||||
|
zurglog.Fatal("No optimal hosts found. We cannot continue! (check if Real-Debrid is down or they have blocked your IP address)")
|
||||||
|
}
|
||||||
zurglog.Debugf("Optimal hosts (%d): %v", len(hosts), hosts)
|
zurglog.Debugf("Optimal hosts (%d): %v", len(hosts), hosts)
|
||||||
|
// help message
|
||||||
zurglog.Debug("To reset optimal hosts, run 'zurg network-test' (Using docker compose? 'docker compose exec zurg ./zurg network-test')")
|
zurglog.Debug("To reset optimal hosts, run 'zurg network-test' (Using docker compose? 'docker compose exec zurg ./zurg network-test')")
|
||||||
|
zurglog.Debug("To run network-test with a proxy, set the PROXY environment variable 'PROXY=http://xyz:123 zurg network-test'")
|
||||||
|
|
||||||
downloadClient := http.NewHTTPClient(
|
downloadClient := http.NewHTTPClient(
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ type RootResponse struct {
|
|||||||
PID int `json:"pid"` // Process ID
|
PID int `json:"pid"` // Process ID
|
||||||
Sponsor SponsorResponse `json:"sponsor_zurg"` // Sponsorship links
|
Sponsor SponsorResponse `json:"sponsor_zurg"` // Sponsorship links
|
||||||
Config config.ZurgConfig `json:"config"`
|
Config config.ZurgConfig `json:"config"`
|
||||||
ImmediateBin []string `json:"immediate_bin"`
|
|
||||||
OnceDoneBin []string `json:"once_done_bin"`
|
OnceDoneBin []string `json:"once_done_bin"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +86,8 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
|
|||||||
Github: "https://github.com/sponsors/debridmediamanager",
|
Github: "https://github.com/sponsors/debridmediamanager",
|
||||||
Paypal: "https://paypal.me/yowmamasita",
|
Paypal: "https://paypal.me/yowmamasita",
|
||||||
},
|
},
|
||||||
Config: zr.cfg.GetConfig(),
|
Config: zr.cfg.GetConfig(),
|
||||||
ImmediateBin: zr.torMgr.ImmediateBin.ToSlice(),
|
OnceDoneBin: zr.torMgr.OnceDoneBin.ToSlice(),
|
||||||
OnceDoneBin: zr.torMgr.OnceDoneBin.ToSlice(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out := `<table border="1px">
|
out := `<table border="1px">
|
||||||
@@ -272,11 +270,7 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
|
|||||||
<td>%v</td>
|
<td>%v</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Immediate Bin</td>
|
<td>IDs to be deleted once download completes</td>
|
||||||
<td colspan="2">%v</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Once Done Bin</td>
|
|
||||||
<td colspan="2">%v</td>
|
<td colspan="2">%v</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -367,7 +361,6 @@ func (zr *Handlers) handleHome(resp http.ResponseWriter, req *http.Request) {
|
|||||||
response.Config.ShouldServeFromRclone(),
|
response.Config.ShouldServeFromRclone(),
|
||||||
response.Config.ShouldForceIPv6(),
|
response.Config.ShouldForceIPv6(),
|
||||||
response.Config.GetOnLibraryUpdate(),
|
response.Config.GetOnLibraryUpdate(),
|
||||||
response.ImmediateBin,
|
|
||||||
response.OnceDoneBin,
|
response.OnceDoneBin,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -31,17 +31,14 @@ func (t *TorrentManager) initializeBins() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t.ImmediateBin = mapset.NewSet[string](data["trash_bin"]...)
|
|
||||||
t.OnceDoneBin = mapset.NewSet[string](data["repair_bin"]...)
|
t.OnceDoneBin = mapset.NewSet[string](data["repair_bin"]...)
|
||||||
|
|
||||||
t.repairLog.Debugf("Bin immediately: %v", t.ImmediateBin.ToSlice())
|
t.repairLog.Debugf("These IDs will be deleted after completion: %v", t.OnceDoneBin.ToSlice())
|
||||||
t.repairLog.Debugf("Bin once done: %v", t.OnceDoneBin.ToSlice())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TorrentManager) persistBins() {
|
func (t *TorrentManager) persistBins() {
|
||||||
data := map[string]interface{}{
|
data := map[string]interface{}{
|
||||||
"trash_bin": t.ImmediateBin.ToSlice(), // Assuming trashBin is a mapset.Set[string]
|
"repair_bin": t.OnceDoneBin.ToSlice(), // Assuming repairBin is a mapset.Set[string]
|
||||||
"repair_bin": t.OnceDoneBin.ToSlice(), // Assuming repairBin is a mapset.Set[string]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jsonData, err := json.Marshal(data)
|
jsonData, err := json.Marshal(data)
|
||||||
@@ -63,12 +60,6 @@ func (t *TorrentManager) persistBins() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TorrentManager) setToBinImmediately(torrentId string) {
|
|
||||||
t.repairLog.Debugf("id=%s set to delete immediately", torrentId)
|
|
||||||
t.ImmediateBin.Add(torrentId)
|
|
||||||
t.persistBins()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TorrentManager) setToBinOnceDone(torrentId string) {
|
func (t *TorrentManager) setToBinOnceDone(torrentId string) {
|
||||||
t.repairLog.Debugf("id=%s set to delete once it completes", torrentId)
|
t.repairLog.Debugf("id=%s set to delete once it completes", torrentId)
|
||||||
t.OnceDoneBin.Add(torrentId)
|
t.OnceDoneBin.Add(torrentId)
|
||||||
@@ -83,24 +74,19 @@ func (t *TorrentManager) setXToBinOnceYDone(deleteId, completeId string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *TorrentManager) cleanupBins(freshIDs mapset.Set[string]) {
|
func (t *TorrentManager) cleanupBins(freshIDs mapset.Set[string]) {
|
||||||
t.ImmediateBin.Clone().Each(func(entry string) bool {
|
|
||||||
if !freshIDs.Contains(entry) {
|
|
||||||
t.ImmediateBin.Remove(entry)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
t.OnceDoneBin.Clone().Each(func(entry string) bool {
|
t.OnceDoneBin.Clone().Each(func(entry string) bool {
|
||||||
// check if the entry is a special case
|
// check for: delete x once y is done cases
|
||||||
if strings.Contains(entry, "-") {
|
if strings.Contains(entry, "-") {
|
||||||
// format is: id1-id2 or id1-
|
// format is: id1-id2 or id1-
|
||||||
// if either id1 or id2 is not fresh, remove the entry
|
// if either id1 or id2 is not fresh, remove the entry
|
||||||
ids := strings.Split(entry, "-")
|
ids := strings.Split(entry, "-")
|
||||||
if !freshIDs.Contains(ids[0]) || (ids[1] != "" && !freshIDs.Contains(ids[1])) {
|
if !freshIDs.ContainsOne(ids[0]) || (ids[1] != "" && !freshIDs.ContainsOne(ids[1])) {
|
||||||
t.OnceDoneBin.Remove(entry)
|
t.OnceDoneBin.Remove(entry)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if !freshIDs.Contains(entry) {
|
// check for: delete once done cases
|
||||||
|
if !freshIDs.ContainsOne(entry) {
|
||||||
t.OnceDoneBin.Remove(entry)
|
t.OnceDoneBin.Remove(entry)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
@@ -108,41 +94,22 @@ func (t *TorrentManager) cleanupBins(freshIDs mapset.Set[string]) {
|
|||||||
t.persistBins()
|
t.persistBins()
|
||||||
}
|
}
|
||||||
|
|
||||||
// binImmediatelyErrorCheck checks if the torrent is in the ImmediateBin and deletes it if it is.
|
|
||||||
// returns true if the torrent was in the bin and was deleted, false otherwise
|
|
||||||
func (t *TorrentManager) binImmediately(torrentId string) bool {
|
|
||||||
if t.ImmediateBin.Contains(torrentId) {
|
|
||||||
if err := t.api.DeleteTorrent(torrentId); err != nil {
|
|
||||||
t.repairLog.Warnf("Failed to delete torrent %s: %v", torrentId, err)
|
|
||||||
}
|
|
||||||
t.ImmediateBin.Remove(torrentId)
|
|
||||||
t.repairLog.Debugf("Bin: immediate deletion of torrent %s", torrentId)
|
|
||||||
t.persistBins()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// binOnceDoneErrorCheck checks if the torrent is in error states and then checks if it should be deleted
|
// binOnceDoneErrorCheck checks if the torrent is in error states and then checks if it should be deleted
|
||||||
func (t *TorrentManager) binOnceDoneErrorCheck(torrentId, status string) bool {
|
func (t *TorrentManager) binOnceDoneErrorCheck(torrentId, status string) bool {
|
||||||
if status == "downloading" || status == "downloaded" || status == "uploading" || status == "queued" || status == "compressing" || status == "waiting_files_selection" {
|
if status == "downloading" || status == "downloaded" || status == "uploading" || status == "queued" || status == "compressing" || status == "waiting_files_selection" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
t.repairLog.Infof("Bin: error status=%s, checking if %s should be deleted", status, torrentId)
|
|
||||||
return t.binOnceDone(torrentId, true)
|
return t.binOnceDone(torrentId, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// binOnceDone checks if the torrent is in the OnceDoneBin and deletes it if it is.
|
// binOnceDone checks if the torrent is in the OnceDoneBin and deletes it if it is.
|
||||||
// returns true if the torrent was in the bin and was deleted, false otherwise
|
// returns true if the torrent was in the bin and was deleted, false otherwise
|
||||||
func (t *TorrentManager) binOnceDone(completedTorrentId string, errorCheck bool) bool {
|
func (t *TorrentManager) binOnceDone(completedTorrentId string, errorCheck bool) bool {
|
||||||
if t.OnceDoneBin.Contains(completedTorrentId) {
|
if t.OnceDoneBin.ContainsOne(completedTorrentId) {
|
||||||
if err := t.api.DeleteTorrent(completedTorrentId); err != nil {
|
t.DeleteByID(completedTorrentId)
|
||||||
t.repairLog.Warnf("Failed to delete torrent %s: %v", completedTorrentId, err)
|
|
||||||
}
|
|
||||||
t.deleteInfoFile(completedTorrentId)
|
|
||||||
t.OnceDoneBin.Remove(completedTorrentId)
|
t.OnceDoneBin.Remove(completedTorrentId)
|
||||||
if errorCheck {
|
if errorCheck {
|
||||||
t.repairLog.Errorf("Bin: error deletion of torrent %s", completedTorrentId)
|
t.repairLog.Infof("Bin: deleting torrent id=%s early because it has encountered an error", completedTorrentId)
|
||||||
} else {
|
} else {
|
||||||
t.repairLog.Debugf("Bin: done deletion of torrent %s", completedTorrentId)
|
t.repairLog.Debugf("Bin: done deletion of torrent %s", completedTorrentId)
|
||||||
}
|
}
|
||||||
@@ -152,7 +119,7 @@ func (t *TorrentManager) binOnceDone(completedTorrentId string, errorCheck bool)
|
|||||||
|
|
||||||
// special case: yyy-xxx means if yyy is done, delete xxx
|
// special case: yyy-xxx means if yyy is done, delete xxx
|
||||||
specialCase := fmt.Sprintf("%s-", completedTorrentId)
|
specialCase := fmt.Sprintf("%s-", completedTorrentId)
|
||||||
if !t.OnceDoneBin.Contains(specialCase) {
|
if !t.OnceDoneBin.ContainsOne(specialCase) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
t.deleteInfoFile(completedTorrentId)
|
t.deleteInfoFile(completedTorrentId)
|
||||||
@@ -160,16 +127,12 @@ func (t *TorrentManager) binOnceDone(completedTorrentId string, errorCheck bool)
|
|||||||
t.OnceDoneBin.Clone().Each(func(entry string) bool {
|
t.OnceDoneBin.Clone().Each(func(entry string) bool {
|
||||||
if strings.Contains(entry, specialCase) {
|
if strings.Contains(entry, specialCase) {
|
||||||
if errorCheck {
|
if errorCheck {
|
||||||
if err := t.api.DeleteTorrent(completedTorrentId); err != nil {
|
t.DeleteByID(completedTorrentId)
|
||||||
t.repairLog.Warnf("Failed to delete torrent %s: %v", completedTorrentId, err)
|
|
||||||
}
|
|
||||||
t.OnceDoneBin.Remove(entry)
|
t.OnceDoneBin.Remove(entry)
|
||||||
t.repairLog.Errorf("Bin: error deletion of torrent %s", completedTorrentId)
|
t.repairLog.Infof("Bin: deleting torrent id=%s early because it has encountered an error", completedTorrentId)
|
||||||
} else {
|
} else {
|
||||||
idToDelete := strings.Split(entry, "-")[1]
|
idToDelete := strings.Split(entry, "-")[1]
|
||||||
if err := t.api.DeleteTorrent(idToDelete); err != nil {
|
t.DeleteByID(idToDelete)
|
||||||
t.repairLog.Warnf("Failed to delete torrent %s: %v", idToDelete, err)
|
|
||||||
}
|
|
||||||
t.OnceDoneBin.Remove(entry)
|
t.OnceDoneBin.Remove(entry)
|
||||||
t.repairLog.Debugf("Bin: %s completed, done deletion of torrent %s", completedTorrentId, idToDelete)
|
t.repairLog.Debugf("Bin: %s completed, done deletion of torrent %s", completedTorrentId, idToDelete)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,7 @@ func (t *TorrentManager) Delete(accessKey string, deleteInRD bool) {
|
|||||||
if torrent, ok := allTorrents.Get(accessKey); ok {
|
if torrent, ok := allTorrents.Get(accessKey); ok {
|
||||||
if deleteInRD {
|
if deleteInRD {
|
||||||
torrent.DownloadedIDs.Clone().Each(func(torrentID string) bool {
|
torrent.DownloadedIDs.Clone().Each(func(torrentID string) bool {
|
||||||
t.log.Debugf("Deleting torrent %s (id=%s) in RD", accessKey, torrentID)
|
t.DeleteByID(torrentID)
|
||||||
t.api.DeleteTorrent(torrentID)
|
|
||||||
t.deleteInfoFile(torrentID)
|
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -38,3 +36,8 @@ func (t *TorrentManager) Delete(accessKey string, deleteInRD bool) {
|
|||||||
})
|
})
|
||||||
allTorrents.Remove(accessKey)
|
allTorrents.Remove(accessKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *TorrentManager) DeleteByID(torrentID string) {
|
||||||
|
t.api.DeleteTorrent(torrentID)
|
||||||
|
t.deleteInfoFile(torrentID)
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ type TorrentManager struct {
|
|||||||
repairRunning bool
|
repairRunning bool
|
||||||
repairRunningMu sync.Mutex
|
repairRunningMu sync.Mutex
|
||||||
|
|
||||||
ImmediateBin mapset.Set[string]
|
OnceDoneBin mapset.Set[string]
|
||||||
OnceDoneBin mapset.Set[string]
|
DeleteOnCompletionBin cmap.ConcurrentMap[string, string]
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTorrentManager creates a new torrent manager
|
// NewTorrentManager creates a new torrent manager
|
||||||
@@ -85,8 +85,8 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, w
|
|||||||
|
|
||||||
latestState: &LibraryState{log: log},
|
latestState: &LibraryState{log: log},
|
||||||
|
|
||||||
ImmediateBin: mapset.NewSet[string](),
|
OnceDoneBin: mapset.NewSet[string](),
|
||||||
OnceDoneBin: mapset.NewSet[string](),
|
DeleteOnCompletionBin: cmap.New[string](),
|
||||||
}
|
}
|
||||||
|
|
||||||
t.initializeBins()
|
t.initializeBins()
|
||||||
@@ -353,6 +353,7 @@ func (t *TorrentManager) mountNewDownloads() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartDownloadsJob: permanent job for remounting downloads
|
||||||
func (t *TorrentManager) StartDownloadsJob() {
|
func (t *TorrentManager) StartDownloadsJob() {
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
remountTicker := time.NewTicker(time.Duration(t.Config.GetDownloadsEveryMins()) * time.Minute)
|
remountTicker := time.NewTicker(time.Duration(t.Config.GetDownloadsEveryMins()) * time.Minute)
|
||||||
@@ -412,6 +413,7 @@ func copyFile(sourcePath, destPath string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartDumpJob: permanent job for dumping torrents
|
||||||
func (t *TorrentManager) StartDumpJob() {
|
func (t *TorrentManager) StartDumpJob() {
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
dumpTicker := time.NewTicker(time.Duration(t.Config.GetDumpTorrentsEveryMins()) * time.Minute)
|
dumpTicker := time.NewTicker(time.Duration(t.Config.GetDumpTorrentsEveryMins()) * time.Minute)
|
||||||
@@ -442,6 +444,7 @@ func (t *TorrentManager) analyzeAllTorrents() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StartMediaAnalysisJob: permanent job for analyzing media info (triggered by the user)
|
||||||
func (t *TorrentManager) StartMediaAnalysisJob() {
|
func (t *TorrentManager) StartMediaAnalysisJob() {
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
for range t.AnalyzeTrigger {
|
for range t.AnalyzeTrigger {
|
||||||
|
|||||||
@@ -37,8 +37,7 @@ func (t *TorrentManager) refreshTorrents(initialRun bool) {
|
|||||||
idx := i
|
idx := i
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
if t.binImmediately(instances[idx].ID) ||
|
if t.binOnceDoneErrorCheck(instances[idx].ID, instances[idx].Status) ||
|
||||||
t.binOnceDoneErrorCheck(instances[idx].ID, instances[idx].Status) ||
|
|
||||||
instances[idx].Progress != 100 {
|
instances[idx].Progress != 100 {
|
||||||
mergeChan <- nil
|
mergeChan <- nil
|
||||||
return
|
return
|
||||||
@@ -55,7 +54,7 @@ func (t *TorrentManager) refreshTorrents(initialRun bool) {
|
|||||||
allTorrents.Set(accessKey, torrent)
|
allTorrents.Set(accessKey, torrent)
|
||||||
t.writeTorrentToFile(torrent)
|
t.writeTorrentToFile(torrent)
|
||||||
t.assignDirectory(torrent, !initialRun)
|
t.assignDirectory(torrent, !initialRun)
|
||||||
} else if !mainTorrent.DownloadedIDs.Contains(tInfo.ID) {
|
} else if !mainTorrent.DownloadedIDs.ContainsOne(tInfo.ID) {
|
||||||
forMerging = torrent
|
forMerging = torrent
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,20 +97,21 @@ func (t *TorrentManager) refreshTorrents(initialRun bool) {
|
|||||||
t.log.Infof("Compiled into %d unique torrents", allTorrents.Count())
|
t.log.Infof("Compiled into %d unique torrents", allTorrents.Count())
|
||||||
|
|
||||||
// delete info files that are no longer present
|
// delete info files that are no longer present
|
||||||
|
// it also runs binOnceDone (needed for cleanup every refresh)
|
||||||
t.getInfoFiles().Each(func(path string) bool {
|
t.getInfoFiles().Each(func(path string) bool {
|
||||||
path = filepath.Base(path)
|
path = filepath.Base(path)
|
||||||
torrentID := strings.TrimSuffix(path, ".zurginfo")
|
torrentID := strings.TrimSuffix(path, ".zurginfo")
|
||||||
// if binOnceDone returns true, it means the info file is deleted
|
// if binOnceDone returns true, it means the info file is deleted
|
||||||
// if false, then we check if it's one of the torrents we just fetched
|
// if false, then we check if it's one of the torrents we just fetched
|
||||||
// if not (both are false), then we delete the info file
|
// if not (both are false), then we delete the info file
|
||||||
if !t.binOnceDone(torrentID, false) && !freshIDs.Contains(torrentID) {
|
if !t.binOnceDone(torrentID, false) && !freshIDs.ContainsOne(torrentID) {
|
||||||
t.deleteInfoFile(torrentID)
|
t.deleteInfoFile(torrentID)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// cleans up DownloadedIDs field of all torrents
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
// update DownloadedIDs field of torrents
|
|
||||||
allTorrents.IterCb(func(accessKey string, torrent *Torrent) {
|
allTorrents.IterCb(func(accessKey string, torrent *Torrent) {
|
||||||
deletedIDs := torrent.DownloadedIDs.Difference(freshIDs)
|
deletedIDs := torrent.DownloadedIDs.Difference(freshIDs)
|
||||||
if deletedIDs.Cardinality() > 0 {
|
if deletedIDs.Cardinality() > 0 {
|
||||||
@@ -167,7 +167,7 @@ func (t *TorrentManager) getMoreInfo(rdTorrent realdebrid.Torrent) *realdebrid.T
|
|||||||
|
|
||||||
func (t *TorrentManager) convertToTorrent(info *realdebrid.TorrentInfo) *Torrent {
|
func (t *TorrentManager) convertToTorrent(info *realdebrid.TorrentInfo) *Torrent {
|
||||||
torrent := t.readTorrentFromFile("data/" + info.Hash + ".zurgtorrent")
|
torrent := t.readTorrentFromFile("data/" + info.Hash + ".zurgtorrent")
|
||||||
if torrent != nil && torrent.DownloadedIDs.Contains(info.ID) {
|
if torrent != nil && torrent.DownloadedIDs.ContainsOne(info.ID) {
|
||||||
return torrent
|
return torrent
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,7 +189,7 @@ func (t *TorrentManager) convertToTorrent(info *realdebrid.TorrentInfo) *Torrent
|
|||||||
var selectedFiles []*File
|
var selectedFiles []*File
|
||||||
for _, file := range info.Files {
|
for _, file := range info.Files {
|
||||||
filename := filepath.Base(file.Path)
|
filename := filepath.Base(file.Path)
|
||||||
if allFilenames.Contains(filename) {
|
if allFilenames.ContainsOne(filename) {
|
||||||
dupeFilenames.Add(filename)
|
dupeFilenames.Add(filename)
|
||||||
} else {
|
} else {
|
||||||
allFilenames.Add(filename)
|
allFilenames.Add(filename)
|
||||||
@@ -228,7 +228,7 @@ func (t *TorrentManager) convertToTorrent(info *realdebrid.TorrentInfo) *Torrent
|
|||||||
for _, file := range selectedFiles {
|
for _, file := range selectedFiles {
|
||||||
baseFilename := t.GetPath(file)
|
baseFilename := t.GetPath(file)
|
||||||
// todo better handling of duplicate filenames
|
// todo better handling of duplicate filenames
|
||||||
if dupeFilenames.Contains(baseFilename) {
|
if dupeFilenames.ContainsOne(baseFilename) {
|
||||||
extension := filepath.Ext(baseFilename)
|
extension := filepath.Ext(baseFilename)
|
||||||
filenameNoExt := strings.TrimSuffix(baseFilename, extension)
|
filenameNoExt := strings.TrimSuffix(baseFilename, extension)
|
||||||
newName := fmt.Sprintf("%s (%d)%s", filenameNoExt, file.ID, extension)
|
newName := fmt.Sprintf("%s (%d)%s", filenameNoExt, file.ID, extension)
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func (t *TorrentManager) StartRepairJob() {
|
|||||||
t.RepairQueue = mapset.NewSet[*Torrent]()
|
t.RepairQueue = mapset.NewSet[*Torrent]()
|
||||||
t.RepairAllTrigger = make(chan struct{})
|
t.RepairAllTrigger = make(chan struct{})
|
||||||
|
|
||||||
|
// periodic repair worker
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
t.repairLog.Debug("Starting periodic repair job")
|
t.repairLog.Debug("Starting periodic repair job")
|
||||||
repairTicker := time.NewTicker(time.Duration(t.Config.GetRepairEveryMins()) * time.Minute)
|
repairTicker := time.NewTicker(time.Duration(t.Config.GetRepairEveryMins()) * time.Minute)
|
||||||
@@ -46,7 +47,7 @@ func (t *TorrentManager) StartRepairJob() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// there is 1 repair worker, with max 1 blocking task
|
// repair worker
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@@ -90,7 +91,6 @@ func (t *TorrentManager) invokeRepair(torrent *Torrent) {
|
|||||||
t.repairRunningMu.Unlock()
|
t.repairRunningMu.Unlock()
|
||||||
|
|
||||||
// Execute the repair job
|
// Execute the repair job
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
t.executeRepairJob(torrent)
|
t.executeRepairJob(torrent)
|
||||||
|
|
||||||
// After repair is done
|
// After repair is done
|
||||||
@@ -120,10 +120,11 @@ func (t *TorrentManager) executeRepairJob(torrent *Torrent) {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
haystack.IterCb(func(_ string, torrent *Torrent) {
|
haystack.IterCb(func(_ string, torrent *Torrent) {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
// temp worker for finding broken torrents
|
||||||
t.workerPool.Submit(func() {
|
t.workerPool.Submit(func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
canExtract := t.Config.GetRarAction() == "extract" && strings.Contains(torrent.UnrepairableReason, "rar")
|
canExtract := t.Config.GetRarAction() == "extract" && strings.Contains(torrent.UnrepairableReason, "rar")
|
||||||
if torrent.UnrepairableReason != "" || !canExtract {
|
if !canExtract || torrent.UnrepairableReason != "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// check 1: for broken files
|
// check 1: for broken files
|
||||||
@@ -234,12 +235,12 @@ func (t *TorrentManager) repair(torrent *Torrent, wg *sync.WaitGroup) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if it's still broken, let's delete the newly downloaded torrent
|
// if it's still broken, let's delete the newly downloaded torrent
|
||||||
t.setToBinImmediately(info.ID)
|
t.DeleteByID(info.ID)
|
||||||
err = fmt.Errorf("links are still broken")
|
err = fmt.Errorf("links are still broken")
|
||||||
|
|
||||||
} else if info != nil && info.Progress != 100 {
|
} else if info != nil && info.Progress != 100 {
|
||||||
// it's faster to download just the broken files, so let's delete the newly downloaded torrent
|
// it's faster to download just the broken files, so let's delete the newly downloaded torrent
|
||||||
t.setToBinImmediately(info.ID)
|
t.DeleteByID(info.ID)
|
||||||
err = fmt.Errorf("no longer cached")
|
err = fmt.Errorf("no longer cached")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +279,7 @@ func (t *TorrentManager) repair(torrent *Torrent, wg *sync.WaitGroup) {
|
|||||||
t.repairLog.Warnf("Cannot repair torrent %s by downloading broken files (error=%v) giving up", t.GetKey(torrent), err)
|
t.repairLog.Warnf("Cannot repair torrent %s by downloading broken files (error=%v) giving up", t.GetKey(torrent), err)
|
||||||
// delete the newly downloaded torrents because the operation failed
|
// delete the newly downloaded torrents because the operation failed
|
||||||
for _, newId := range newlyDownloadedIds {
|
for _, newId := range newlyDownloadedIds {
|
||||||
t.setToBinImmediately(newId)
|
t.DeleteByID(newId)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -414,10 +415,10 @@ func (t *TorrentManager) assignLinks(torrent *Torrent) bool {
|
|||||||
t.repairLog.Warnf("Torrent %s is rar'ed and we cannot repair it", t.GetKey(torrent))
|
t.repairLog.Warnf("Torrent %s is rar'ed and we cannot repair it", t.GetKey(torrent))
|
||||||
t.markAsUnfixable(torrent, "rar'ed by RD")
|
t.markAsUnfixable(torrent, "rar'ed by RD")
|
||||||
t.markAsUnplayable(torrent, "rar'ed by RD")
|
t.markAsUnplayable(torrent, "rar'ed by RD")
|
||||||
|
torrent.State.Event(context.Background(), "mark_as_repaired")
|
||||||
}
|
}
|
||||||
|
|
||||||
torrent.UnassignedLinks = mapset.NewSet[string]()
|
torrent.UnassignedLinks = mapset.NewSet[string]()
|
||||||
// torrent.State.Event(context.Background(), "mark_as_repaired")
|
|
||||||
t.writeTorrentToFile(torrent)
|
t.writeTorrentToFile(torrent)
|
||||||
|
|
||||||
return false // end repair
|
return false // end repair
|
||||||
@@ -478,20 +479,20 @@ func (t *TorrentManager) redownloadTorrent(torrent *Torrent, selection []string)
|
|||||||
for {
|
for {
|
||||||
retries++
|
retries++
|
||||||
if retries > 10 {
|
if retries > 10 {
|
||||||
t.setToBinImmediately(newTorrentID)
|
t.DeleteByID(newTorrentID)
|
||||||
return nil, fmt.Errorf("cannot start redownloading: too many retries")
|
return nil, fmt.Errorf("cannot start redownloading: too many retries")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = t.api.SelectTorrentFiles(newTorrentID, finalSelection)
|
err = t.api.SelectTorrentFiles(newTorrentID, finalSelection)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.setToBinImmediately(newTorrentID)
|
t.DeleteByID(newTorrentID)
|
||||||
return nil, fmt.Errorf("cannot start redownloading: %v", err)
|
return nil, fmt.Errorf("cannot start redownloading: %v", err)
|
||||||
}
|
}
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
info, err = t.api.GetTorrentInfo(newTorrentID)
|
info, err = t.api.GetTorrentInfo(newTorrentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.setToBinImmediately(newTorrentID)
|
t.DeleteByID(newTorrentID)
|
||||||
return nil, fmt.Errorf("cannot get info on redownloaded : %v", err)
|
return nil, fmt.Errorf("cannot get info on redownloaded : %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,13 +505,13 @@ func (t *TorrentManager) redownloadTorrent(torrent *Torrent, selection []string)
|
|||||||
|
|
||||||
// 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
|
||||||
if info.Status != "downloading" && info.Status != "downloaded" && info.Status != "uploading" && info.Status != "queued" && info.Status != "compressing" {
|
if info.Status != "downloading" && info.Status != "downloaded" && info.Status != "uploading" && info.Status != "queued" && info.Status != "compressing" {
|
||||||
t.setToBinImmediately(newTorrentID)
|
t.DeleteByID(newTorrentID)
|
||||||
return nil, fmt.Errorf("non-OK state: %s", info.Status)
|
return nil, fmt.Errorf("non-OK state: %s", info.Status)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if incorrect number of links
|
// check if incorrect number of links
|
||||||
if info.Progress == 100 && len(info.Links) != len(selection) {
|
if info.Progress == 100 && len(info.Links) != len(selection) {
|
||||||
t.setToBinImmediately(newTorrentID)
|
t.DeleteByID(newTorrentID)
|
||||||
return nil, fmt.Errorf("only got %d links but we need %d", len(info.Links), len(selection))
|
return nil, fmt.Errorf("only got %d links but we need %d", len(info.Links), len(selection))
|
||||||
} else if info.Progress != 100 {
|
} else if info.Progress != 100 {
|
||||||
t.repairLog.Infof("Downloading torrent %s (id=%s, progress=%d)", t.GetKey(torrent), info.ID, info.Progress)
|
t.repairLog.Infof("Downloading torrent %s (id=%s, progress=%d)", t.GetKey(torrent), info.ID, info.Progress)
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ func NewTorrentState(initial string) *fsm.FSM {
|
|||||||
{Name: "break_torrent", Src: []string{"ok_torrent"}, Dst: "broken_torrent"},
|
{Name: "break_torrent", Src: []string{"ok_torrent"}, Dst: "broken_torrent"},
|
||||||
// when repair has been started
|
// when repair has been started
|
||||||
{Name: "repair_torrent", Src: []string{"ok_torrent", "broken_torrent"}, Dst: "under_repair_torrent"},
|
{Name: "repair_torrent", Src: []string{"ok_torrent", "broken_torrent"}, Dst: "under_repair_torrent"},
|
||||||
|
// when converting to torrent
|
||||||
|
// when merging with another same hash torrent
|
||||||
|
// when a torrent is rar'ed and not extracting
|
||||||
{Name: "mark_as_repaired", Src: []string{"broken_torrent", "under_repair_torrent"}, Dst: "ok_torrent"},
|
{Name: "mark_as_repaired", Src: []string{"broken_torrent", "under_repair_torrent"}, Dst: "ok_torrent"},
|
||||||
},
|
},
|
||||||
fsm.Callbacks{},
|
fsm.Callbacks{},
|
||||||
|
|||||||
Reference in New Issue
Block a user