Per-tracker seeding rules and torrent reaper (#88)
This commit was merged in pull request #88.
This commit is contained in:
@@ -50,6 +50,26 @@ pub struct SeedingLimits {
|
||||
pub idle_minutes: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SeedingRules {
|
||||
default: SeedingLimits,
|
||||
trackers: HashMap<i64, SeedingLimits>,
|
||||
}
|
||||
|
||||
impl SeedingRules {
|
||||
#[must_use]
|
||||
pub fn new(default: SeedingLimits, trackers: HashMap<i64, SeedingLimits>) -> Self {
|
||||
Self { default, trackers }
|
||||
}
|
||||
|
||||
fn for_indexer(&self, indexer_id: i64) -> SeedingLimits {
|
||||
self.trackers
|
||||
.get(&indexer_id)
|
||||
.copied()
|
||||
.unwrap_or(self.default)
|
||||
}
|
||||
}
|
||||
|
||||
/// A failure during one grab tick.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum GrabError {
|
||||
@@ -84,7 +104,7 @@ pub struct GrabAction {
|
||||
prowlarr: ProwlarrClient,
|
||||
transmission: TransmissionClient,
|
||||
download_dir: PathBuf,
|
||||
seeding: SeedingLimits,
|
||||
seeding: SeedingRules,
|
||||
indexers: tokio::sync::RwLock<IndexerCache>,
|
||||
/// [`INDEXER_DISCOVERY_TIMEOUT`], overridden by tests that cannot wait
|
||||
/// out the real one. Mirrors `ReconcileLoop`'s action timeout override.
|
||||
@@ -97,7 +117,7 @@ impl GrabAction {
|
||||
prowlarr: ProwlarrClient,
|
||||
transmission: TransmissionClient,
|
||||
download_dir: PathBuf,
|
||||
seeding: SeedingLimits,
|
||||
seeding: SeedingRules,
|
||||
) -> Self {
|
||||
Self {
|
||||
prowlarr,
|
||||
@@ -342,14 +362,15 @@ impl GrabAction {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let seeding = self.seeding.for_indexer(winner.indexer_id);
|
||||
let added = self
|
||||
.transmission
|
||||
.add_torrent(AddTorrent {
|
||||
source: torrent_source(&winner.download_url),
|
||||
label: label(&loaded),
|
||||
download_dir: self.download_dir.clone(),
|
||||
seed_ratio_limit: self.seeding.ratio,
|
||||
seed_idle_limit_minutes: self.seeding.idle_minutes,
|
||||
seed_ratio_limit: seeding.ratio,
|
||||
seed_idle_limit_minutes: seeding.idle_minutes,
|
||||
})
|
||||
.await?;
|
||||
let infohash = added.hash.to_ascii_lowercase();
|
||||
@@ -665,12 +686,12 @@ async fn record_search(database: &Db, movie_id: i64) -> Result<(), GrabError> {
|
||||
/// `movies-main`, `tv-kids` (§7.1). Distinct from Radarr's own labels, so
|
||||
/// both stacks can run against one Transmission.
|
||||
fn label(loaded: &MoviePolicy) -> String {
|
||||
let kind = if loaded.root_kind == "movie" {
|
||||
"movies"
|
||||
} else {
|
||||
&loaded.root_kind
|
||||
};
|
||||
format!("{kind}-{}", loaded.root_audience)
|
||||
label_for_root(&loaded.root_kind, &loaded.root_audience)
|
||||
}
|
||||
|
||||
pub(crate) fn label_for_root(kind: &str, audience: &str) -> String {
|
||||
let kind = if kind == "movie" { "movies" } else { kind };
|
||||
format!("{kind}-{audience}")
|
||||
}
|
||||
|
||||
/// Indexers hand out magnets and `.torrent` links interchangeably;
|
||||
@@ -901,13 +922,32 @@ mod tests {
|
||||
ProwlarrClient::new(prowlarr.uri(), "key").unwrap(),
|
||||
TransmissionClient::new(&transmission.uri()).unwrap(),
|
||||
PathBuf::from("/mnt/media/transmission/complete"),
|
||||
SeedingLimits {
|
||||
ratio: 1.5,
|
||||
idle_minutes: 60,
|
||||
},
|
||||
SeedingRules::new(
|
||||
SeedingLimits {
|
||||
ratio: 1.5,
|
||||
idle_minutes: 60,
|
||||
},
|
||||
HashMap::new(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seeding_rules_select_by_prowlarr_indexer_id() {
|
||||
let default = SeedingLimits {
|
||||
ratio: 1.0,
|
||||
idle_minutes: 60,
|
||||
};
|
||||
let tracker = SeedingLimits {
|
||||
ratio: 2.5,
|
||||
idle_minutes: 120,
|
||||
};
|
||||
let rules = SeedingRules::new(default, HashMap::from([(7, tracker)]));
|
||||
|
||||
assert_eq!(rules.for_indexer(7), tracker);
|
||||
assert_eq!(rules.for_indexer(8), default);
|
||||
}
|
||||
|
||||
async fn grabs(database: &Db) -> Vec<(i64, String, String)> {
|
||||
sqlx::query_as::<_, (i64, String, String)>(
|
||||
"SELECT target_id, infohash, state FROM grabs ORDER BY id",
|
||||
|
||||
Reference in New Issue
Block a user