Skip to main content

autopulse_service/settings/triggers/
tdarr.rs

1use crate::settings::path_filter::PathFilter;
2use crate::settings::rewrite::Rewrite;
3use crate::settings::timer::Timer;
4use crate::settings::triggers::{TriggerConfig, TriggerRequest};
5use serde::{Deserialize, Serialize};
6
7#[derive(Serialize, Deserialize, Clone, Default)]
8pub struct Tdarr {
9    /// Rewrite path
10    pub rewrite: Option<Rewrite>,
11    /// Timer settings
12    pub timer: Option<Timer>,
13    /// Targets to ignore
14    #[serde(default)]
15    pub excludes: Vec<String>,
16    /// Path filter matched against the rewritten file path.
17    #[serde(default)]
18    pub filter: PathFilter,
19}
20
21impl TriggerConfig for Tdarr {
22    fn rewrite(&self) -> Option<&Rewrite> {
23        self.rewrite.as_ref()
24    }
25
26    fn timer(&self) -> Option<&Timer> {
27        self.timer.as_ref()
28    }
29
30    fn excludes(&self) -> &Vec<String> {
31        &self.excludes
32    }
33
34    fn filter(&self) -> &PathFilter {
35        &self.filter
36    }
37}
38
39/// JSON body: `path` (new file), `original_path` (replaced original, queued without
40/// an existence check unless equal to `path`), `dir`. Values are preserved exactly;
41/// blank or whitespace-only values are ignored. At least one path is required.
42#[derive(Deserialize, Clone)]
43pub struct TdarrRequest {
44    pub path: Option<String>,
45    pub original_path: Option<String>,
46    pub dir: Option<String>,
47}
48
49impl TriggerRequest for TdarrRequest {
50    fn from_json(json: serde_json::Value) -> anyhow::Result<Self> {
51        let request: Self = serde_json::from_value(json)?;
52        anyhow::ensure!(!request.paths().is_empty(), "Tdarr request has no paths");
53        Ok(request)
54    }
55
56    fn paths(&self) -> Vec<(String, bool)> {
57        // Tdarr renders missing template variables as "", so blank values count as absent.
58        let present =
59            |value: &Option<String>| value.clone().filter(|value| !value.trim().is_empty());
60        let path = present(&self.path);
61        let original_path =
62            present(&self.original_path).filter(|original| Some(original) != path.as_ref());
63        [
64            (path, true),
65            (original_path, false),
66            (present(&self.dir), true),
67        ]
68        .into_iter()
69        .filter_map(|(path, search)| Some((path?, search)))
70        .collect()
71    }
72}