diff --git a/internal/torrent/bins.go b/internal/torrent/bins.go index c47f795..764ddf8 100644 --- a/internal/torrent/bins.go +++ b/internal/torrent/bins.go @@ -13,7 +13,7 @@ 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.Warn("data/bins.json does not exist. Initializing empty bins.") + t.repairLog.Info("data/bins.json does not exist. Initializing empty bins.") t.ImmediateBin = mapset.NewSet[string]() t.OnceDoneBin = mapset.NewSet[string]() return @@ -120,6 +120,7 @@ func (t *TorrentManager) binImmediately(torrentId string) bool { 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 } @@ -128,21 +129,27 @@ func (t *TorrentManager) binImmediately(torrentId string) bool { // 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" { + if status == "downloading" || status == "downloaded" || status == "uploading" || status == "queued" || status == "compressing" || status == "waiting_files_selection" { return false } - return t.binOnceDone(torrentId) + 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) bool { +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 } @@ -152,14 +159,22 @@ func (t *TorrentManager) binOnceDone(completedTorrentId string) bool { if !t.OnceDoneBin.Contains(specialCase) { return false } - t.OnceDoneBin.Remove(specialCase) t.OnceDoneBin.Clone().Each(func(entry string) bool { if strings.Contains(entry, specialCase) { - idToDelete := strings.Split(entry, "-")[1] - if err := t.api.DeleteTorrent(idToDelete); err != nil { - t.repairLog.Warnf("Failed to delete torrent %s: %v", idToDelete, err) + 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) } - t.OnceDoneBin.Remove(entry) } return false }) diff --git a/internal/torrent/hooks.go b/internal/torrent/hooks.go index 1e236fc..54566ab 100644 --- a/internal/torrent/hooks.go +++ b/internal/torrent/hooks.go @@ -45,13 +45,6 @@ func (se *ScriptExecutor) Execute() (string, error) { return out.String(), nil } -func (t *TorrentManager) TriggerHookOnLibraryUpdate(updatedPaths []string) { - _ = t.workerPool.Submit(func() { - OnLibraryUpdateHook(updatedPaths, t.Config, t.log) - t.log.Debugf("Triggered hook on_library_update for %d path(s)", len(updatedPaths)) - }) -} - func OnLibraryUpdateHook(paths []string, config config.ConfigInterface, log *logutil.Logger) { executor := &ScriptExecutor{ Script: config.GetOnLibraryUpdate(), diff --git a/internal/torrent/manager.go b/internal/torrent/manager.go index 3e51018..10f9643 100644 --- a/internal/torrent/manager.go +++ b/internal/torrent/manager.go @@ -113,7 +113,7 @@ func NewTorrentManager(cfg config.ConfigInterface, api *realdebrid.RealDebrid, r return false }) - t.refreshTorrents() + t.refreshTorrents(true) }) t.workerPool.Submit(func() { defer wg.Done() diff --git a/internal/torrent/refresh.go b/internal/torrent/refresh.go index 804955e..8d144b4 100644 --- a/internal/torrent/refresh.go +++ b/internal/torrent/refresh.go @@ -20,7 +20,7 @@ func inProgressStatus(status string) bool { return status == "downloading" || status == "uploading" || status == "queued" || status == "compressing" } -func (t *TorrentManager) refreshTorrents() { +func (t *TorrentManager) refreshTorrents(initialRun bool) { t.inProgressHashes = mapset.NewSet[string]() instances, _, err := t.api.GetTorrents(false) if err != nil { @@ -62,7 +62,7 @@ func (t *TorrentManager) refreshTorrents() { if !exists { allTorrents.Set(accessKey, torrent) t.writeTorrentToFile(torrent) - t.assignDirectory(torrent, true) + t.assignDirectory(torrent, !initialRun) } else if !mainTorrent.DownloadedIDs.Contains(tInfo.ID) { forMerging = torrent } @@ -94,7 +94,7 @@ func (t *TorrentManager) refreshTorrents() { mainTorrent := t.mergeTorrents(existing, torrent) allTorrents.Set(accessKey, mainTorrent) t.writeTorrentToFile(mainTorrent) - t.assignDirectory(mainTorrent, true) + t.assignDirectory(mainTorrent, !initialRun) } // removed torrents @@ -106,19 +106,17 @@ func (t *TorrentManager) refreshTorrents() { t.log.Infof("Compiled into %d unique torrents", allTorrents.Count()) - t.workerPool.Submit(func() { - // delete info files that are no longer present - t.getInfoFiles().Each(func(path string) bool { - path = filepath.Base(path) - torrentID := strings.TrimSuffix(path, ".zurginfo") - // 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 not, then we delete the info file - if !t.binOnceDone(torrentID) && !freshIDs.Contains(torrentID) { - t.deleteInfoFile(torrentID) - } - return false - }) + // delete info files that are no longer present + t.getInfoFiles().Each(func(path string) bool { + path = filepath.Base(path) + torrentID := strings.TrimSuffix(path, ".zurginfo") + // 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 not (both are false), then we delete the info file + if !t.binOnceDone(torrentID, false) && !freshIDs.Contains(torrentID) { + t.deleteInfoFile(torrentID) + } + return false }) t.workerPool.Submit(func() { @@ -152,7 +150,7 @@ func (t *TorrentManager) StartRefreshJob() { } t.setNewLatestState(checksum) - t.refreshTorrents() + t.refreshTorrents(false) t.log.Info("Finished refreshing torrents") case <-t.RefreshKillSwitch: t.log.Info("Stopping periodic refresh job") @@ -293,6 +291,8 @@ func (t *TorrentManager) mergeTorrents(existing, toMerge *Torrent) *Torrent { if !ok || !f.State.Is("ok_file") { mergedTorrent.SelectedFiles.Set(key, file) } + // get the file again, set the media info + f, ok = mergedTorrent.SelectedFiles.Get(key) if ok && f.MediaInfo == nil && mediaInfo != nil { f.MediaInfo = mediaInfo } @@ -328,6 +328,8 @@ func (t *TorrentManager) mergeTorrents(existing, toMerge *Torrent) *Torrent { mergedTorrent.State.Event(context.Background(), "mark_as_repaired") } + t.log.Debugf("Merged torrent %s has %d broken files", t.GetKey(mergedTorrent), brokenCount) + return mergedTorrent } @@ -366,6 +368,7 @@ func (t *TorrentManager) assignDirectory(tor *Torrent, triggerHook bool) { // Map torrents to directories switch t.Config.GetVersion() { case "v1": + updatedPaths := make([]string, 0) configV1 := t.Config.(*config.ZurgConfigV1) for _, directories := range configV1.GetGroupMap() { for _, directory := range directories { @@ -374,12 +377,15 @@ func (t *TorrentManager) assignDirectory(tor *Torrent, triggerHook bool) { listing.Set(accessKey, tor) if triggerHook { - t.TriggerHookOnLibraryUpdate([]string{fmt.Sprintf("%s/%s", directory, accessKey)}) + updatedPaths = append(updatedPaths, fmt.Sprintf("%s/%s", directory, accessKey)) } break } } } + if triggerHook { + OnLibraryUpdateHook(updatedPaths, t.Config, t.log) + } } } diff --git a/internal/torrent/repair.go b/internal/torrent/repair.go index 986590c..d9feea4 100644 --- a/internal/torrent/repair.go +++ b/internal/torrent/repair.go @@ -190,6 +190,7 @@ func (t *TorrentManager) repair(torrent *Torrent) { allPlayable = false if t.Config.GetRarAction() == "extract" && file.ID != 0 { + t.repairLog.Debugf("Extracting file %s from rar'ed torrent %s", file.Path, t.GetKey(torrent)) info, _ := t.redownloadTorrent(torrent, []string{fmt.Sprintf("%d", file.ID)}) if info != nil { t.setToBinOnceDone(info.ID) @@ -381,7 +382,8 @@ func (t *TorrentManager) assignLinks(torrent *Torrent) bool { torrent.SelectedFiles.IterCb(func(_ string, file *File) { if utils.IsPlayable(file.Path) { videoFiles = append(videoFiles, fmt.Sprintf("%d", file.ID)) - } else { + } else if file.ID != 0 { + t.repairLog.Debugf("Extracting file %s from rar'ed torrent %s", file.Path, t.GetKey(torrent)) info, _ := t.redownloadTorrent(torrent, []string{fmt.Sprintf("%d", file.ID)}) if info != nil { t.setToBinOnceDone(info.ID)