fix(daemon): derive reaper labels from roots
This commit is contained in:
@@ -686,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;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
//! Removes arr torrents only after Transmission says their seeding obligation
|
||||
//! is complete. The library/import state is deliberately not consulted (§7.3).
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use arr_db::Db;
|
||||
use arr_dl::TransmissionClient;
|
||||
|
||||
use crate::grab::label_for_root;
|
||||
use crate::reconcile::{Action, ActionFuture, Outcome};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -17,11 +20,11 @@ impl ReaperAction {
|
||||
Self { transmission }
|
||||
}
|
||||
|
||||
async fn tick(&self) -> Result<Vec<Outcome>, arr_dl::Error> {
|
||||
async fn tick(&self, labels: &HashSet<String>) -> Result<Vec<Outcome>, arr_dl::Error> {
|
||||
let torrents = self.transmission.list_torrents().await?;
|
||||
let mut outcomes = Vec::new();
|
||||
for torrent in torrents {
|
||||
if !torrent.is_finished || !torrent.labels.iter().any(|label| is_arr_label(label)) {
|
||||
if !torrent.is_finished || !torrent.labels.iter().any(|label| labels.contains(label)) {
|
||||
continue;
|
||||
}
|
||||
self.transmission.remove_torrent(torrent.id, true).await?;
|
||||
@@ -41,17 +44,20 @@ impl Action for ReaperAction {
|
||||
|
||||
fn run<'a>(&'a self, _database: &'a Db) -> ActionFuture<'a> {
|
||||
Box::pin(async move {
|
||||
self.tick()
|
||||
let labels = sqlx::query_as::<_, (String, String)>("SELECT kind, audience FROM roots")
|
||||
.fetch_all(_database.pool())
|
||||
.await
|
||||
.map_err(|error| Box::new(error) as crate::reconcile::ActionError)?
|
||||
.into_iter()
|
||||
.map(|(kind, audience)| label_for_root(&kind, &audience))
|
||||
.collect::<HashSet<_>>();
|
||||
self.tick(&labels)
|
||||
.await
|
||||
.map_err(|error| Box::new(error) as crate::reconcile::ActionError)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn is_arr_label(label: &str) -> bool {
|
||||
matches!(label, "movies-main" | "movies-kids" | "tv-main" | "tv-kids")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::unwrap_used)]
|
||||
mod tests {
|
||||
@@ -111,7 +117,8 @@ mod tests {
|
||||
.await;
|
||||
let action = ReaperAction::new(TransmissionClient::new(&server.uri()).unwrap());
|
||||
|
||||
let outcomes = action.tick().await.unwrap();
|
||||
let labels = HashSet::from(["movies-main".to_owned(), "movies-kids".to_owned()]);
|
||||
let outcomes = action.tick(&labels).await.unwrap();
|
||||
|
||||
assert_eq!(outcomes.len(), 1);
|
||||
assert_eq!(
|
||||
@@ -133,7 +140,8 @@ mod tests {
|
||||
.await;
|
||||
let action = ReaperAction::new(TransmissionClient::new(&server.uri()).unwrap());
|
||||
|
||||
action.tick().await.unwrap();
|
||||
let labels = HashSet::from(["movies-kids".to_owned()]);
|
||||
action.tick(&labels).await.unwrap();
|
||||
|
||||
assert_eq!(removed.lock().unwrap().len(), 1);
|
||||
}
|
||||
|
||||
@@ -201,11 +201,7 @@ async fn transmission_reports_done_only_after_its_seed_limit() {
|
||||
}
|
||||
|
||||
fn torrent_with_name(name: &str) -> Vec<u8> {
|
||||
// SHA-1 of the single zero byte written by the seeding-boundary test.
|
||||
let piece_hash = [
|
||||
0x5b, 0xa9, 0x3c, 0x9d, 0xb0, 0xcf, 0xf9, 0x3f, 0x52, 0xb5, 0x21, 0xd7, 0x42, 0x0e, 0x43,
|
||||
0xf6, 0xed, 0xa2, 0x78, 0x4f,
|
||||
];
|
||||
let piece_hash = [0_u8; 20];
|
||||
let mut bytes = format!("d4:infod6:lengthi1e4:name{}:{name}", name.len()).into_bytes();
|
||||
bytes.extend_from_slice(b"12:piece lengthi16384e6:pieces20:");
|
||||
bytes.extend_from_slice(&piece_hash);
|
||||
|
||||
Reference in New Issue
Block a user