- Add __bad__ for bad torrents

- Add colors to logs info
- Make logs a bit more clearer
- Mark torrent has bad instead of deleting them
This commit is contained in:
Mukhtar Akere
2025-05-13 13:17:26 +01:00
parent 7c1bb52793
commit 36e681d0e6
8 changed files with 110 additions and 48 deletions
+8 -11
View File
@@ -41,6 +41,7 @@ type CachedTorrent struct {
*types.Torrent
AddedOn time.Time `json:"added_on"`
IsComplete bool `json:"is_complete"`
Bad bool `json:"bad"`
}
func (c CachedTorrent) copy() CachedTorrent {
@@ -285,7 +286,11 @@ func (c *Cache) load() (map[string]CachedTorrent, error) {
}
func (c *Cache) Sync() error {
defer c.logger.Info().Msg("WebDav server sync complete")
cfg := config.Get()
name := c.client.GetName()
addr := cfg.BindAddress + ":" + cfg.Port + cfg.URLBase + "webdav/" + name + "/"
defer c.logger.Info().Msgf("%s WebDav server running at %s", name, addr)
cachedTorrents, err := c.load()
if err != nil {
c.logger.Error().Err(err).Msg("Failed to load cache")
@@ -298,7 +303,7 @@ func (c *Cache) Sync() error {
totalTorrents := len(torrents)
c.logger.Info().Msgf("Got %d torrents from %s", totalTorrents, c.client.GetName())
c.logger.Info().Msgf("%d torrents found from %s", totalTorrents, c.client.GetName())
newTorrents := make([]*types.Torrent, 0)
idStore := make(map[string]struct{}, totalTorrents)
@@ -475,12 +480,6 @@ func (c *Cache) GetCustomFolders() []string {
return c.customFolders
}
func (c *Cache) GetDirectories() []string {
dirs := []string{"__all__", "torrents"}
dirs = append(dirs, c.customFolders...)
return dirs
}
func (c *Cache) Close() error {
return nil
}
@@ -711,9 +710,7 @@ func (c *Cache) deleteTorrent(id string, removeFromDebrid bool) bool {
t.Files = newFiles
newId = cmp.Or(newId, t.Id)
t.Id = newId
c.setTorrent(t, func(tor CachedTorrent) {
c.RefreshListings(false)
})
c.setTorrent(t, nil) // This gets called after calling deleteTorrent
}
}
return true
+24 -7
View File
@@ -3,7 +3,6 @@ package debrid
import (
"errors"
"fmt"
"github.com/sirrobot01/decypharr/internal/config"
"github.com/sirrobot01/decypharr/internal/request"
"github.com/sirrobot01/decypharr/internal/utils"
"github.com/sirrobot01/decypharr/pkg/debrid/types"
@@ -34,6 +33,25 @@ func (r *reInsertRequest) Wait() (*CachedTorrent, error) {
return r.result, r.err
}
func (c *Cache) markAsFailedToReinsert(torrentId string) {
currentCount := 0
if retryCount, ok := c.failedToReinsert.Load(torrentId); ok {
currentCount = retryCount.(int)
}
c.failedToReinsert.Store(torrentId, currentCount+1)
// Remove the torrent from the directory if it has failed to reinsert, max retries are hardcoded to 5
if currentCount > 3 {
// Mark torrent as failed
if torrent, ok := c.torrents.getByID(torrentId); ok {
torrent.Bad = true
c.SaveTorrent(torrent)
}
}
}
func (c *Cache) IsTorrentBroken(t *CachedTorrent, filenames []string) bool {
// Check torrent files
@@ -85,9 +103,8 @@ func (c *Cache) IsTorrentBroken(t *CachedTorrent, filenames []string) bool {
}
}
}
cfg := config.Get()
// Try to reinsert the torrent if it's broken
if cfg.Repair.ReInsert && isBroken && t.Torrent != nil {
if isBroken && t.Torrent != nil {
// Check if the torrent is already in progress
if _, err := c.reInsertTorrent(t); err != nil {
c.logger.Error().Err(err).Str("torrentId", t.Torrent.Id).Msg("Failed to reinsert torrent")
@@ -161,14 +178,14 @@ func (c *Cache) reInsertTorrent(ct *CachedTorrent) (*CachedTorrent, error) {
var err error
newTorrent, err = c.client.SubmitMagnet(newTorrent)
if err != nil {
c.failedToReinsert.Store(oldID, struct{}{})
c.markAsFailedToReinsert(oldID)
// Remove the old torrent from the cache and debrid service
return ct, fmt.Errorf("failed to submit magnet: %w", err)
}
// Check if the torrent was submitted
if newTorrent == nil || newTorrent.Id == "" {
c.failedToReinsert.Store(oldID, struct{}{})
c.markAsFailedToReinsert(oldID)
return ct, fmt.Errorf("failed to submit magnet: empty torrent")
}
newTorrent.DownloadUncached = false // Set to false, avoid re-downloading
@@ -178,7 +195,7 @@ func (c *Cache) reInsertTorrent(ct *CachedTorrent) (*CachedTorrent, error) {
// Delete the torrent if it was not downloaded
_ = c.client.DeleteTorrent(newTorrent.Id)
}
c.failedToReinsert.Store(oldID, struct{}{})
c.markAsFailedToReinsert(oldID)
return ct, err
}
@@ -189,7 +206,7 @@ func (c *Cache) reInsertTorrent(ct *CachedTorrent) (*CachedTorrent, error) {
}
for _, f := range newTorrent.Files {
if f.Link == "" {
c.failedToReinsert.Store(oldID, struct{}{})
c.markAsFailedToReinsert(oldID)
return ct, fmt.Errorf("failed to reinsert torrent: empty link")
}
}
+28 -1
View File
@@ -1,6 +1,7 @@
package debrid
import (
"fmt"
"os"
"regexp"
"sort"
@@ -51,9 +52,11 @@ type torrentCache struct {
}
type sortableFile struct {
id string
name string
modTime time.Time
size int64
bad bool
}
func newTorrentCache(dirFilters map[string][]directoryFilter) *torrentCache {
@@ -132,7 +135,7 @@ func (tc *torrentCache) refreshListing() {
tc.mu.Lock()
all := make([]sortableFile, 0, len(tc.byName))
for name, t := range tc.byName {
all = append(all, sortableFile{name, t.AddedOn, t.Size})
all = append(all, sortableFile{t.Id, name, t.AddedOn, t.Size, t.Bad})
}
tc.sortNeeded.Store(false)
tc.mu.Unlock()
@@ -156,6 +159,30 @@ func (tc *torrentCache) refreshListing() {
}()
wg.Done()
wg.Add(1)
// For __bad__
go func() {
listing := make([]os.FileInfo, 0)
for _, sf := range all {
if sf.bad {
listing = append(listing, &fileInfo{
name: fmt.Sprintf("%s(%s)", sf.name, sf.id),
size: sf.size,
mode: 0755 | os.ModeDir,
modTime: sf.modTime,
isDir: true,
})
}
}
tc.folderListingMu.Lock()
if len(listing) > 0 {
tc.folderListing["__bad__"] = listing
} else {
delete(tc.folderListing, "__bad__")
}
tc.folderListingMu.Unlock()
}()
now := time.Now()
wg.Add(len(tc.directoriesFilters)) // for each directory filter
for dir, filters := range tc.directoriesFilters {
+9 -16
View File
@@ -9,33 +9,26 @@ import (
func (c *Cache) StartSchedule() error {
// For now, we just want to refresh the listing and download links
ctx := context.Background()
downloadLinkJob, err := utils.ScheduleJob(ctx, c.downloadLinksRefreshInterval, nil, c.refreshDownloadLinks)
if err != nil {
if _, err := utils.ScheduleJob(ctx, c.downloadLinksRefreshInterval, nil, c.refreshDownloadLinks); err != nil {
c.logger.Error().Err(err).Msg("Failed to add download link refresh job")
}
if t, err := downloadLinkJob.NextRun(); err == nil {
c.logger.Trace().Msgf("Next download link refresh job: %s", t.Format("2006-01-02 15:04:05"))
} else {
c.logger.Debug().Msgf("Download link refresh job scheduled every %s", c.downloadLinksRefreshInterval)
}
torrentJob, err := utils.ScheduleJob(ctx, c.torrentRefreshInterval, nil, c.refreshTorrents)
if err != nil {
if _, err := utils.ScheduleJob(ctx, c.torrentRefreshInterval, nil, c.refreshTorrents); err != nil {
c.logger.Error().Err(err).Msg("Failed to add torrent refresh job")
}
if t, err := torrentJob.NextRun(); err == nil {
c.logger.Trace().Msgf("Next torrent refresh job: %s", t.Format("2006-01-02 15:04:05"))
} else {
c.logger.Debug().Msgf("Torrent refresh job scheduled every %s", c.torrentRefreshInterval)
}
// Schedule the reset invalid links job
// This job will run every 24 hours
// This job will run every at 00:00 CET
// and reset the invalid links in the cache
cet, _ := time.LoadLocation("CET")
resetLinksJob, err := utils.ScheduleJob(ctx, "00:00", cet, c.resetInvalidLinks)
if err != nil {
if _, err := utils.ScheduleJob(ctx, "00:00", cet, c.resetInvalidLinks); err != nil {
c.logger.Error().Err(err).Msg("Failed to add reset invalid links job")
}
if t, err := resetLinksJob.NextRun(); err == nil {
c.logger.Trace().Msgf("Next reset invalid download links job at: %s", t.Format("2006-01-02 15:04:05"))
}
return nil
}