186 lines
5.9 KiB
Go
186 lines
5.9 KiB
Go
package torrent
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
mapset "github.com/deckarep/golang-set/v2"
|
|
)
|
|
|
|
const BINS_FILE = "data/bins.json"
|
|
|
|
// initializeBins reads from bins.json and assigns values to t.trashBin and t.repairBin
|
|
func (t *TorrentManager) initializeBins() {
|
|
if _, err := os.Stat(BINS_FILE); os.IsNotExist(err) {
|
|
t.repairLog.Info("data/bins.json does not exist. Initializing empty bins.")
|
|
t.ImmediateBin = mapset.NewSet[string]()
|
|
t.OnceDoneBin = mapset.NewSet[string]()
|
|
return
|
|
}
|
|
|
|
fileData, err := os.ReadFile(BINS_FILE)
|
|
if err != nil {
|
|
t.repairLog.Errorf("Failed to read bins.json file: %v", err)
|
|
t.ImmediateBin = mapset.NewSet[string]()
|
|
t.OnceDoneBin = mapset.NewSet[string]()
|
|
return
|
|
}
|
|
|
|
data := map[string][]string{}
|
|
|
|
err = json.Unmarshal(fileData, &data)
|
|
if err != nil {
|
|
t.repairLog.Errorf("Failed to unmarshal bin data: %v", err)
|
|
return
|
|
}
|
|
|
|
t.ImmediateBin = mapset.NewSet[string](data["trash_bin"]...)
|
|
t.OnceDoneBin = mapset.NewSet[string](data["repair_bin"]...)
|
|
|
|
t.repairLog.Debugf("Bin immediately: %v", t.ImmediateBin.ToSlice())
|
|
t.repairLog.Debugf("Bin once done: %v", t.OnceDoneBin.ToSlice())
|
|
}
|
|
|
|
func (t *TorrentManager) persistBins() {
|
|
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]
|
|
}
|
|
|
|
jsonData, err := json.Marshal(data)
|
|
if err != nil {
|
|
t.repairLog.Errorf("Failed to marshal bin data: %v", err)
|
|
return
|
|
}
|
|
|
|
file, err := os.Create(BINS_FILE)
|
|
if err != nil {
|
|
t.repairLog.Errorf("Failed to create bins.json file: %v", err)
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
_, err = file.Write(jsonData)
|
|
if err != nil {
|
|
t.repairLog.Errorf("Failed to write to bins.json file: %v", err)
|
|
}
|
|
}
|
|
|
|
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) {
|
|
t.repairLog.Debugf("id=%s set to delete once it completes", torrentId)
|
|
t.OnceDoneBin.Add(torrentId)
|
|
t.persistBins()
|
|
}
|
|
|
|
func (t *TorrentManager) setXToBinOnceYDone(deleteId, completeId string) {
|
|
t.repairLog.Debugf("id=%s set to delete once id=%s completes", deleteId, completeId)
|
|
t.OnceDoneBin.Add(fmt.Sprintf("%s-", completeId))
|
|
t.OnceDoneBin.Add(fmt.Sprintf("%s-%s", completeId, deleteId))
|
|
t.persistBins()
|
|
}
|
|
|
|
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 {
|
|
// check if the entry is a special case
|
|
if strings.Contains(entry, "-") {
|
|
// format is: id1-id2 or id1-
|
|
// if either id1 or id2 is not fresh, remove the entry
|
|
ids := strings.Split(entry, "-")
|
|
if !freshIDs.Contains(ids[0]) || (ids[1] != "" && !freshIDs.Contains(ids[1])) {
|
|
t.OnceDoneBin.Remove(entry)
|
|
}
|
|
return false
|
|
}
|
|
if !freshIDs.Contains(entry) {
|
|
t.OnceDoneBin.Remove(entry)
|
|
}
|
|
return false
|
|
})
|
|
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
|
|
func (t *TorrentManager) binOnceDoneErrorCheck(torrentId, status string) bool {
|
|
if status == "downloading" || status == "downloaded" || status == "uploading" || status == "queued" || status == "compressing" || status == "waiting_files_selection" {
|
|
return false
|
|
}
|
|
t.repairLog.Errorf("Bin: error status=%s, checking if %s should be deleted", status, torrentId)
|
|
return t.binOnceDone(torrentId, true)
|
|
}
|
|
|
|
// 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
|
|
func (t *TorrentManager) binOnceDone(completedTorrentId string, errorCheck bool) bool {
|
|
if t.OnceDoneBin.Contains(completedTorrentId) {
|
|
if err := t.api.DeleteTorrent(completedTorrentId); err != nil {
|
|
t.repairLog.Warnf("Failed to delete torrent %s: %v", completedTorrentId, err)
|
|
}
|
|
t.deleteInfoFile(completedTorrentId)
|
|
t.OnceDoneBin.Remove(completedTorrentId)
|
|
if errorCheck {
|
|
t.repairLog.Errorf("Bin: error deletion of torrent %s", completedTorrentId)
|
|
} else {
|
|
t.repairLog.Debugf("Bin: done deletion of torrent %s", completedTorrentId)
|
|
}
|
|
t.persistBins()
|
|
return true
|
|
}
|
|
|
|
// special case: yyy-xxx means if yyy is done, delete xxx
|
|
specialCase := fmt.Sprintf("%s-", completedTorrentId)
|
|
if !t.OnceDoneBin.Contains(specialCase) {
|
|
return false
|
|
}
|
|
t.deleteInfoFile(completedTorrentId)
|
|
t.OnceDoneBin.Remove(specialCase)
|
|
t.OnceDoneBin.Clone().Each(func(entry string) bool {
|
|
if strings.Contains(entry, specialCase) {
|
|
if errorCheck {
|
|
if err := t.api.DeleteTorrent(completedTorrentId); err != nil {
|
|
t.repairLog.Warnf("Failed to delete torrent %s: %v", completedTorrentId, err)
|
|
}
|
|
t.OnceDoneBin.Remove(entry)
|
|
t.repairLog.Errorf("Bin: error deletion of torrent %s", completedTorrentId)
|
|
} else {
|
|
idToDelete := strings.Split(entry, "-")[1]
|
|
if err := t.api.DeleteTorrent(idToDelete); err != nil {
|
|
t.repairLog.Warnf("Failed to delete torrent %s: %v", idToDelete, err)
|
|
}
|
|
t.OnceDoneBin.Remove(entry)
|
|
t.repairLog.Debugf("Bin: %s completed, done deletion of torrent %s", completedTorrentId, idToDelete)
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
t.persistBins()
|
|
return true
|
|
}
|