From 742cb8ecd9646a81fd8a79715b41614a86867c67 Mon Sep 17 00:00:00 2001 From: s0up4200 Date: Wed, 15 Jan 2025 09:54:58 +0100 Subject: [PATCH 1/3] feat(tracker): add isTrackerDown helper --- README.md | 28 +++++++++++++++++--------- config/torrent.go | 48 ++++++++++++++++++++++++++++++++++++++++++-- tracker/bhd.go | 4 ++++ tracker/hdb.go | 4 ++++ tracker/interface.go | 1 + tracker/ops.go | 4 ++++ tracker/ptp.go | 4 ++++ tracker/red.go | 4 ++++ tracker/unit3d.go | 4 ++++ 9 files changed, 90 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 76c0277..d24e091 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ filters: default: ignore: # general - - TrackerStatus contains "Tracker is down" + - IsTrackerDown() - Downloaded == false && !IsUnregistered() - SeedingHours < 26 && !IsUnregistered() # permaseed / un-sorted (unless torrent has been deleted) @@ -54,7 +54,7 @@ filters: - '"permaseed" in Tags && !IsUnregistered()' remove: # general - - IsUnregistered() + - IsUnregistered() && !IsTrackerDown() # imported - Label in ["sonarr-imported", "radarr-imported", "lidarr-imported"] && (Ratio > 4.0 || SeedingDays >= 15.0) # ipt @@ -194,12 +194,24 @@ All of this and more can be noted in the [language definition](https://github.co 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 +IsUnregistered() bool // Evaluates to true if torrent is unregistered in the tracker +IsTrackerDown() bool // Evaluates to true if the tracker appears to be down/unreachable HasAllTags(tags ...string) bool // True if torrent has ALL tags specified -HasAnyTag(tags ...string) bool // True if torrent has at least one tag specified -Log(n float64) float64 // The natural logarithm function +HasAnyTag(tags ...string) bool // True if torrent has at least one tag specified +Log(n float64) float64 // The natural logarithm function ``` +### IsUnregistered and IsTrackerDown + +When using both `IsUnregistered()` and `IsTrackerDown()` in filters: + +- `IsUnregistered() && !IsTrackerDown()` - Only matches torrents that are confirmed unregistered while the tracker is responding +- This helps prevent false positives where torrents might appear unregistered simply because their tracker is down +- The functions are independent but related - a torrent can be: + - Unregistered with tracker up (IsUnregistered: true, IsTrackerDown: false) + - Status unknown with tracker down (IsUnregistered: false, IsTrackerDown: true) + - Registered with tracker up (IsUnregistered: false, IsTrackerDown: false) + ## BypassIgnoreIfUnregistered If the top level config option `bypassIgnoreIfUnregistered` is set to `true`, unregistered torrents will not be ignored. @@ -210,7 +222,7 @@ filters: default: ignore: # general - - TrackerStatus contains "Tracker is down" + - IsTrackerDown() - Downloaded == false && !IsUnregistered() - SeedingHours < 26 && !IsUnregistered() # permaseed / un-sorted (unless torrent has been deleted) @@ -228,7 +240,7 @@ filters: default: ignore: # general - - TrackerStatus contains "Tracker is down" + - IsTrackerDown() - Downloaded == false - SeedingHours < 26 # permaseed / un-sorted (unless torrent has been deleted) @@ -237,8 +249,6 @@ filters: - '"permaseed" in Tags ``` -**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 diff --git a/config/torrent.go b/config/torrent.go index dec39dd..8d3c6dc 100644 --- a/config/torrent.go +++ b/config/torrent.go @@ -39,12 +39,21 @@ var ( "tracker nicht registriert", "torrent not found", "trump", - //"truncated", // Tracker is down "unknown", "unregistered", "upgraded", "uploaded", } + + trackerDownStatuses = []string{ + "bad gateway", + "down", + "maintenance", + "tracker is down", + "tracker unavailable", + "truncated", + "unreachable", + } ) type Torrent struct { @@ -86,7 +95,7 @@ type Torrent struct { } func (t *Torrent) IsUnregistered() bool { - if t.TrackerStatus == "" || strings.Contains(t.TrackerStatus, "Tracker is down") { + if t.TrackerStatus == "" { return false } @@ -211,3 +220,38 @@ func (t *Torrent) RegexMatchAll(patternsStr string) bool { } return match } + +func (t *Torrent) IsTrackerDown() bool { + if t.TrackerStatus == "" { + return false + } + + status := strings.ToLower(t.TrackerStatus) + for _, v := range trackerDownStatuses { + if strings.Contains(status, v) { + return true + } + } + + // check tracker api (if available) + if tr := tracker.Get(t.TrackerName); tr != nil { + tt := &tracker.Torrent{ + Hash: t.Hash, + Name: t.Name, + TotalBytes: t.TotalBytes, + DownloadedBytes: t.DownloadedBytes, + State: t.State, + Downloaded: t.Downloaded, + Seeding: t.Seeding, + TrackerName: t.TrackerName, + TrackerStatus: t.State, + Comment: t.Comment, + } + + if err, down := tr.IsTrackerDown(tt); err == nil { + return down + } + } + + return false +} diff --git a/tracker/bhd.go b/tracker/bhd.go index 705676b..fc53253 100644 --- a/tracker/bhd.go +++ b/tracker/bhd.go @@ -92,3 +92,7 @@ func (c *BHD) IsUnregistered(torrent *Torrent) (error, bool) { return nil, b.TotalResults < 1 } + +func (c *BHD) IsTrackerDown(torrent *Torrent) (error, bool) { + return nil, false +} diff --git a/tracker/hdb.go b/tracker/hdb.go index 01c84ea..1056ee4 100644 --- a/tracker/hdb.go +++ b/tracker/hdb.go @@ -102,3 +102,7 @@ func (c *HDB) IsUnregistered(torrent *Torrent) (error, bool) { // if we get no results for a valid hash, the torrent is unregistered return nil, b.Status == 0 && len(b.Data) == 0 } + +func (c *HDB) IsTrackerDown(torrent *Torrent) (error, bool) { + return nil, false +} diff --git a/tracker/interface.go b/tracker/interface.go index eae805e..b2e9311 100644 --- a/tracker/interface.go +++ b/tracker/interface.go @@ -4,4 +4,5 @@ type Interface interface { Name() string Check(string) bool IsUnregistered(torrent *Torrent) (error, bool) + IsTrackerDown(torrent *Torrent) (error, bool) } diff --git a/tracker/ops.go b/tracker/ops.go index b527377..52ab792 100644 --- a/tracker/ops.go +++ b/tracker/ops.go @@ -89,3 +89,7 @@ func (c *OPS) IsUnregistered(torrent *Torrent) (error, bool) { return nil, b.Status == "failure" && b.Error == "bad parameters" } + +func (c *OPS) IsTrackerDown(torrent *Torrent) (error, bool) { + return nil, false +} diff --git a/tracker/ptp.go b/tracker/ptp.go index 3fe24b5..8a28e17 100644 --- a/tracker/ptp.go +++ b/tracker/ptp.go @@ -88,3 +88,7 @@ func (c *PTP) IsUnregistered(torrent *Torrent) (error, bool) { return nil, b.Result == "ERROR" && b.ResultDetails == "Unregistered Torrent" } + +func (c *PTP) IsTrackerDown(torrent *Torrent) (error, bool) { + return nil, false +} diff --git a/tracker/red.go b/tracker/red.go index 15dcabb..6707f35 100644 --- a/tracker/red.go +++ b/tracker/red.go @@ -91,3 +91,7 @@ func (c *RED) IsUnregistered(torrent *Torrent) (error, bool) { return nil, b.Status == "failure" && b.Error == "bad hash parameter" } + +func (c *RED) IsTrackerDown(torrent *Torrent) (error, bool) { + return nil, false +} diff --git a/tracker/unit3d.go b/tracker/unit3d.go index b097451..ae8d7b8 100644 --- a/tracker/unit3d.go +++ b/tracker/unit3d.go @@ -146,3 +146,7 @@ func (c *UNIT3D) IsUnregistered(torrent *Torrent) (error, bool) { torrent.Hash, b.Data.Attributes.InfoHash) return nil, true } + +func (c *UNIT3D) IsTrackerDown(torrent *Torrent) (error, bool) { + return nil, false +} From b578e1a9f50bcf5c2cb117663cdb9d61ccf5aa66 Mon Sep 17 00:00:00 2001 From: s0up4200 Date: Wed, 15 Jan 2025 10:25:54 +0100 Subject: [PATCH 2/3] refactor: add tracker down check to IsUnregistered --- README.md | 19 ++++++++++++++--- config/torrent.go | 54 +++++++++++++++++------------------------------ 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index d24e091..8b4dc09 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ filters: - '"permaseed" in Tags && !IsUnregistered()' remove: # general - - IsUnregistered() && !IsTrackerDown() + - IsUnregistered() # imported - Label in ["sonarr-imported", "radarr-imported", "lidarr-imported"] && (Ratio > 4.0 || SeedingDays >= 15.0) # ipt @@ -205,13 +205,26 @@ Log(n float64) float64 // The natural logarithm function When using both `IsUnregistered()` and `IsTrackerDown()` in filters: -- `IsUnregistered() && !IsTrackerDown()` - Only matches torrents that are confirmed unregistered while the tracker is responding -- This helps prevent false positives where torrents might appear unregistered simply because their tracker is down +- `IsUnregistered()` has built-in protection against tracker down states - it will return `false` if the tracker is down +- `IsTrackerDown()` checks if the tracker status indicates the tracker is unreachable/down - The functions are independent but related - a torrent can be: - Unregistered with tracker up (IsUnregistered: true, IsTrackerDown: false) - Status unknown with tracker down (IsUnregistered: false, IsTrackerDown: true) - Registered with tracker up (IsUnregistered: false, IsTrackerDown: false) +Note: While `IsUnregistered()` automatically handles tracker down states, you may still want to explicitly check for `IsTrackerDown()` in your ignore filters to prevent any actions when tracker status is uncertain. + +Example: + +```yaml +filters: + default: + ignore: + - IsTrackerDown() # Skip any actions when tracker is down + remove: + - IsUnregistered() # Safe to use alone due to built-in protection +``` + ## BypassIgnoreIfUnregistered If the top level config option `bypassIgnoreIfUnregistered` is set to `true`, unregistered torrents will not be ignored. diff --git a/config/torrent.go b/config/torrent.go index 8d3c6dc..cb9e346 100644 --- a/config/torrent.go +++ b/config/torrent.go @@ -94,7 +94,26 @@ type Torrent struct { regexPattern *regex.Pattern } +func (t *Torrent) IsTrackerDown() bool { + if t.TrackerStatus == "" { + return false + } + + status := strings.ToLower(t.TrackerStatus) + for _, v := range trackerDownStatuses { + if strings.Contains(status, v) { + return true + } + } + + return false +} + func (t *Torrent) IsUnregistered() bool { + if t.IsTrackerDown() { + return false + } + if t.TrackerStatus == "" { return false } @@ -220,38 +239,3 @@ func (t *Torrent) RegexMatchAll(patternsStr string) bool { } return match } - -func (t *Torrent) IsTrackerDown() bool { - if t.TrackerStatus == "" { - return false - } - - status := strings.ToLower(t.TrackerStatus) - for _, v := range trackerDownStatuses { - if strings.Contains(status, v) { - return true - } - } - - // check tracker api (if available) - if tr := tracker.Get(t.TrackerName); tr != nil { - tt := &tracker.Torrent{ - Hash: t.Hash, - Name: t.Name, - TotalBytes: t.TotalBytes, - DownloadedBytes: t.DownloadedBytes, - State: t.State, - Downloaded: t.Downloaded, - Seeding: t.Seeding, - TrackerName: t.TrackerName, - TrackerStatus: t.State, - Comment: t.Comment, - } - - if err, down := tr.IsTrackerDown(tt); err == nil { - return down - } - } - - return false -} From 9fe8bb9ce222dcebb255e4c1ec224ef3f73494a0 Mon Sep 17 00:00:00 2001 From: s0up4200 Date: Fri, 17 Jan 2025 10:05:22 +0100 Subject: [PATCH 3/3] feat: add more statuses to trackerDown --- config/torrent.go | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/config/torrent.go b/config/torrent.go index cb9e346..34b6878 100644 --- a/config/torrent.go +++ b/config/torrent.go @@ -46,13 +46,44 @@ var ( } trackerDownStatuses = []string{ - "bad gateway", + // libtorrent HTTP status messages + // https://github.com/arvidn/libtorrent/blob/RC_2_0/src/error_code.cpp#L320-L339 + // https://github.com/arvidn/libtorrent/blob/RC_1_2/src/error_code.cpp#L298-L317 + "continue", // 100 - server still processing + "multiple choices", // 300 - could indicate load balancer issues + "not modified", // 304 - could be caching issues + "bad request", // 400 + "unauthorized", // 401 + "forbidden", // 403 + "internal server error", // 500 + "not implemented", // 501 + "bad gateway", // 502 + "service unavailable", // 503 + "moved permanently", // 301 + "moved temporarily", // 302 + "(unknown http error)", + + // tracker/network errors "down", "maintenance", "tracker is down", "tracker unavailable", "truncated", "unreachable", + "not working", + "not responding", + "timeout", + "refused", + "no connection", + "cannot connect", + "connection failed", + "ssl error", + "no data", + "timed out", + "temporarily disabled", + "unresolvable", + "host not found", + "offline", } )