From e282cbb486ec56519015ba6753a4b31d87939f56 Mon Sep 17 00:00:00 2001 From: s0up4200 Date: Mon, 13 Jan 2025 10:25:15 +0100 Subject: [PATCH 1/3] feat(torrent): add helper to detect missing files --- README.md | 69 +++++++++++++++++++++++++++++------------------ config/torrent.go | 16 +++++++++++ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 0eaaed9..4ea6aa7 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,13 @@ [![Donate](https://img.shields.io/badge/Donate-gray.svg?style=flat-square)](#donate) # tqm + CLI tool to manage your torrent client queues. Primary focus is on removing torrents that meet specific criteria. This is a fork from [l3uddz](https://github.com/l3uddz/tqm). ## Example Configuration + ```yaml clients: deluge: @@ -105,6 +107,7 @@ filters: ``` ## Optional - Tracker Configuration + ```yaml trackers: bhd: @@ -113,45 +116,50 @@ trackers: api_user: your-api-user api_key: your-api-key ``` + Allows tqm to validate if a torrent was removed from the tracker using the tracker's own API. Currently implements: + - Beyond-HD - PTP ## Filtering Language Definition + The language definition used in the configuration filters is available [here](https://github.com/antonmedv/expr/blob/586b86b462d22497d442adbc924bfb701db3075d/docs/Language-Definition.md) ## Filterable Fields + The following torrent fields (along with their types) can be used in the configuration when filtering torrents: + ```go type Torrent struct { - Hash string - Name string - Path string - TotalBytes int64 - DownloadedBytes int64 - State string - Files []string - Tags []string - Downloaded bool - Seeding bool - Ratio float32 - AddedSeconds int64 - AddedHours float32 - AddedDays float32 - SeedingSeconds int64 - SeedingHours float32 - SeedingDays float32 - Label string - Seeds int64 - Peers int64 - - FreeSpaceGB func() float64 - FreeSpaceSet bool - - TrackerName string - TrackerStatus string + Hash string + Name string + Path string + TotalBytes int64 + DownloadedBytes int64 + State string + Files []string + Tags []string + Downloaded bool + Seeding bool + Ratio float32 + AddedSeconds int64 + AddedHours float32 + AddedDays float32 + SeedingSeconds int64 + SeedingHours float32 + SeedingDays float32 + Label string + Seeds int64 + Peers int64 + + FreeSpaceGB func() float64 + FreeSpaceSet bool + + TrackerName string + TrackerStatus string } ``` @@ -164,17 +172,22 @@ Fields of type `[]string` (lists) such as the `Tags` and `Files` fields support All of this and more can be noted in the [language definition](https://github.com/antonmedv/expr/blob/586b86b462d22497d442adbc924bfb701db3075d/docs/Language-Definition.md) mentioned above. ## Helper Filtering Options + The following helper functions are available for usage while filtering, usage examples are available in the example config above. + ```go IsUnregistered() bool // Evaluates to true if torrent is unregistered in the tracker HasAllTags(tags ...string) bool // True if torrent has ALL tags specified HasAnyTag(tags ...string) bool // True if torrent has at least one tag specified +HasMissingFiles() bool // True if any of the torrent's files are missing from disk Log(n float64) float64 // The natural logarithm function ``` ## BypassIgnoreIfUnregistered + If the top level config option `bypassIgnoreIfUnregistered` is set to `true`, unregistered torrents will not be ignored. This helps making the config less verbose, so this: + ```yaml filters: default: @@ -188,7 +201,9 @@ filters: # Filter based on qbittorrent tags (only qbit at the moment) - '"permaseed" in Tags && !IsUnregistered()' ``` + can turn into this: + ```yaml bypassIgnoreIfUnregistered: true @@ -208,10 +223,12 @@ filters: **Note:** If `TrackerStatus contains "Tracker is down"` then a torrent will not be considered unregistered anyways and will be ignored when tracker is down assuming the above filters. ## Supported Clients + - Deluge - qBittorrent ## Example Commands + 1. Clean - Retrieve torrent client queue and remove torrents matching its configured filters `tqm clean qbt --dry-run` diff --git a/config/torrent.go b/config/torrent.go index 899d128..01d71b0 100644 --- a/config/torrent.go +++ b/config/torrent.go @@ -2,6 +2,7 @@ package config import ( "math" + "os" "strings" "github.com/autobrr/tqm/sliceutils" @@ -137,6 +138,21 @@ func (t *Torrent) HasAnyTag(tags ...string) bool { return false } +func (t *Torrent) HasMissingFiles() bool { + if !t.Downloaded { + return false + } + + // check if files exist on disk + for _, f := range t.Files { + if _, err := os.Stat(f); os.IsNotExist(err) { + return true + } + } + + return false +} + func (t *Torrent) Log(n float64) float64 { return math.Log(n) } From 855bf0b8b04ab27ae755f42b70d37a329aaeb4ff Mon Sep 17 00:00:00 2001 From: s0up4200 Date: Mon, 13 Jan 2025 11:37:19 +0100 Subject: [PATCH 2/3] feat(torrent): improve HasMissingFiles error handling --- config/torrent.go | 19 ++++++++++++++++--- logger/log.go | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/config/torrent.go b/config/torrent.go index 01d71b0..b719522 100644 --- a/config/torrent.go +++ b/config/torrent.go @@ -5,6 +5,7 @@ import ( "os" "strings" + "github.com/autobrr/tqm/logger" "github.com/autobrr/tqm/sliceutils" "github.com/autobrr/tqm/tracker" ) @@ -143,10 +144,22 @@ func (t *Torrent) HasMissingFiles() bool { return false } - // check if files exist on disk + log := logger.GetLogger("torrent") + for _, f := range t.Files { - if _, err := os.Stat(f); os.IsNotExist(err) { - return true + if f == "" { + log.Tracef("Skipping empty path for torrent: %s", t.Name) + continue + } + + _, err := os.Stat(f) + if err != nil { + if os.IsNotExist(err) { + //log.Debugf("Missing file detected: %s for torrent: %s", f, t.Name) + return true + } + log.Warnf("Error checking file %s for torrent %s: %v", f, t.Name, err) + continue } } diff --git a/logger/log.go b/logger/log.go index 1b1e4fc..b884860 100644 --- a/logger/log.go +++ b/logger/log.go @@ -9,7 +9,7 @@ import ( ) var ( - prefixLen = 14 + prefixLen = 15 loggingFilePath string ) From 0b27a55a19597db6e23fc95b4bed02bd5e181da9 Mon Sep 17 00:00:00 2001 From: s0up4200 Date: Fri, 17 Jan 2025 14:06:04 +0100 Subject: [PATCH 3/3] refactor(torrent): simplify style in HasMissingFiles --- config/torrent.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/config/torrent.go b/config/torrent.go index c993419..51d7729 100644 --- a/config/torrent.go +++ b/config/torrent.go @@ -157,13 +157,11 @@ func (t *Torrent) HasMissingFiles() bool { continue } - _, err := os.Stat(f) - if err != nil { + if _, err := os.Stat(f); err != nil { if os.IsNotExist(err) { - //log.Debugf("Missing file detected: %s for torrent: %s", f, t.Name) return true } - log.Warnf("Error checking file %s for torrent %s: %v", f, t.Name, err) + log.Warnf("error checking file '%s' for torrent '%s': %v", f, t.Name, err) continue } }