Implement Deluge support
This commit is contained in:
parent
9c993d7c04
commit
31d124dd82
21 changed files with 659 additions and 72 deletions
263
Cargo.lock
generated
263
Cargo.lock
generated
|
|
@ -312,6 +312,35 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie_store"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"document-features",
|
||||
"idna",
|
||||
"log",
|
||||
"publicsuffix",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
|
@ -449,6 +478,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dsl_auto_type"
|
||||
version = "0.1.3"
|
||||
|
|
@ -463,6 +501,18 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "educe"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8"
|
||||
dependencies = [
|
||||
"enum-ordinalize",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
|
|
@ -504,6 +554,26 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-ordinalize"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5"
|
||||
dependencies = [
|
||||
"enum-ordinalize-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-ordinalize-derive"
|
||||
version = "4.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.2"
|
||||
|
|
@ -530,7 +600,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -603,6 +673,21 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.31"
|
||||
|
|
@ -610,6 +695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -618,6 +704,34 @@ version = "0.3.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.31"
|
||||
|
|
@ -636,10 +750,16 @@ version = "0.3.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -859,10 +979,10 @@ dependencies = [
|
|||
"http 1.3.1",
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls 0.23.26",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
|
@ -1121,7 +1241,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
|||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1190,6 +1310,12 @@ version = "0.7.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
|
|
@ -1424,6 +1550,26 @@ version = "2.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
|
|
@ -1489,6 +1635,22 @@ dependencies = [
|
|||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl-types"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "publicsuffix"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.7"
|
||||
|
|
@ -1501,7 +1663,7 @@ dependencies = [
|
|||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"rustls 0.23.26",
|
||||
"socket2",
|
||||
"thiserror 2.0.12",
|
||||
"tokio",
|
||||
|
|
@ -1520,7 +1682,7 @@ dependencies = [
|
|||
"rand",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"rustls 0.23.26",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.12",
|
||||
|
|
@ -1540,7 +1702,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1600,8 +1762,10 @@ dependencies = [
|
|||
"diesel",
|
||||
"diesel_migrations",
|
||||
"directories",
|
||||
"env_logger",
|
||||
"figment",
|
||||
"figment_file_provider_adapter",
|
||||
"futures",
|
||||
"log",
|
||||
"magnet-url",
|
||||
"multimap",
|
||||
|
|
@ -1610,10 +1774,15 @@ dependencies = [
|
|||
"regex",
|
||||
"reqwest 0.12.15",
|
||||
"roux",
|
||||
"rustls 0.22.4",
|
||||
"rustls-native-certs",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tokio-rustls 0.25.0",
|
||||
"tokio-serde",
|
||||
"tokio-util",
|
||||
"transmission-rpc",
|
||||
"url",
|
||||
"urlencoding",
|
||||
|
|
@ -1707,6 +1876,8 @@ checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
|
|||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"cookie_store",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
|
|
@ -1727,7 +1898,7 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls 0.23.26",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
|
|
@ -1737,7 +1908,7 @@ dependencies = [
|
|||
"system-configuration 0.6.1",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tokio-rustls 0.26.2",
|
||||
"tokio-socks",
|
||||
"tower",
|
||||
"tower-service",
|
||||
|
|
@ -1797,7 +1968,21 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.22.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.8",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1809,11 +1994,24 @@ dependencies = [
|
|||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.103.1",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pemfile 2.2.0",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
|
|
@ -1841,6 +2039,17 @@ dependencies = [
|
|||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.1"
|
||||
|
|
@ -2107,7 +2316,7 @@ dependencies = [
|
|||
"getrandom 0.3.2",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2262,16 +2471,42 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||
dependencies = [
|
||||
"rustls 0.22.4",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls 0.23.26",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-serde"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caf600e7036b17782571dd44fa0a5cea3c82f60db5137f774a325a76a0d6852b"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"educe",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-socks"
|
||||
version = "0.5.2"
|
||||
|
|
@ -2639,7 +2874,7 @@ version = "0.1.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
11
Cargo.toml
11
Cargo.toml
|
|
@ -13,12 +13,15 @@ roux = "2.2.14"
|
|||
figment = { version = "0.10", features = ["toml", "json", "env"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tokio = { version = "1.44.2", features = ["rt", "rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.44.2", features = ["rt", "rt-multi-thread", "macros", "io-util", "net"] }
|
||||
regex = "1.11.1"
|
||||
figment_file_provider_adapter = "0.1.1"
|
||||
directories = "6.0.0"
|
||||
log = "0.4.27"
|
||||
color-eyre = "0.6.3"
|
||||
tokio-rustls = "0.25.0"
|
||||
rustls = "0.22.2"
|
||||
rustls-native-certs = "0.7.0"
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
multimap = "0.10.0"
|
||||
diesel = { version = "2.2.10", features = ["sqlite", "chrono"] }
|
||||
|
|
@ -30,7 +33,11 @@ clap-verbosity-flag = "3.0.2"
|
|||
pretty_env_logger = "0.5.0"
|
||||
async-trait = "0.1.88"
|
||||
console = "0.15.11"
|
||||
reqwest = "0.12.15"
|
||||
reqwest = { version = "0.12.15", features = ["cookies"] }
|
||||
magnet-url = "2.0.0"
|
||||
urlencoding = "2.1.3"
|
||||
ntfy = "0.7.0"
|
||||
tokio-util = "0.7.15"
|
||||
env_logger = "0.10.2"
|
||||
tokio-serde = { version = "0.9.0", features = ["json"] }
|
||||
futures = "0.3.31"
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
DROP TABLE deluge_processed;
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
CREATE TABLE deluge_processed
|
||||
(
|
||||
id INTEGER PRIMARY KEY NOT NULL,
|
||||
magnet_id INTEGER NOT NULL,
|
||||
processed_at DATETIME NOT NULL,
|
||||
FOREIGN KEY (magnet_id) REFERENCES magnets (id)
|
||||
);
|
||||
|
||||
CREATE INDEX deluge_processed_magnet_id ON deluge_processed (magnet_id);
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::actions::action::{Action, ProcessedMagnets};
|
||||
use crate::actions::bitmagnet::client::BitmagnetClient;
|
||||
use crate::actions::bitmagnet::config::BitmagnetConfig;
|
||||
use crate::db::{BitmagnetProcessedTable, Database};
|
||||
use crate::actions::bitmagnet::db::BitmagnetProcessedTable;
|
||||
use crate::db::Database;
|
||||
use color_eyre::eyre::Result;
|
||||
use log::{debug, warn};
|
||||
|
||||
|
|
|
|||
31
src/actions/bitmagnet/db.rs
Normal file
31
src/actions/bitmagnet/db.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use crate::db::ProcessedTable;
|
||||
use crate::models::NewBitmagnetProcessed;
|
||||
use crate::schema::bitmagnet_processed;
|
||||
use color_eyre::eyre::Context;
|
||||
use diesel::{QueryDsl, RunQueryDsl, SqliteConnection};
|
||||
|
||||
pub struct BitmagnetProcessedTable;
|
||||
|
||||
impl ProcessedTable for BitmagnetProcessedTable {
|
||||
fn get_processed_ids(conn: &mut SqliteConnection) -> color_eyre::Result<Vec<i32>> {
|
||||
bitmagnet_processed::table
|
||||
.select(bitmagnet_processed::magnet_id)
|
||||
.load(conn)
|
||||
.wrap_err("Failed to load processed magnet IDs for Bitmagnet")
|
||||
}
|
||||
|
||||
fn mark_processed(conn: &mut SqliteConnection, magnet_id: i32) -> color_eyre::Result<()> {
|
||||
let now = chrono::Utc::now().naive_utc();
|
||||
let new_processed = NewBitmagnetProcessed {
|
||||
magnet_id,
|
||||
processed_at: &now,
|
||||
};
|
||||
|
||||
diesel::insert_into(bitmagnet_processed::table)
|
||||
.values(&new_processed)
|
||||
.execute(conn)
|
||||
.wrap_err("Failed to mark magnet as processed by Bitmagnet")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
mod action;
|
||||
mod client;
|
||||
mod config;
|
||||
mod db;
|
||||
|
||||
pub use action::BitmagnetAction;
|
||||
pub use config::BitmagnetConfig;
|
||||
|
|
|
|||
65
src/actions/deluge/action.rs
Normal file
65
src/actions/deluge/action.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
use crate::actions::action::{Action, ProcessedMagnets};
|
||||
use crate::actions::deluge::client::DelugeClient;
|
||||
use crate::actions::deluge::config::DelugeConfig;
|
||||
use crate::actions::deluge::db::DelugeProcessedTable;
|
||||
use crate::db::Database;
|
||||
use color_eyre::eyre::Result;
|
||||
use log::{debug, warn};
|
||||
|
||||
/// Action for submitting magnet links to Deluge
|
||||
pub struct DelugeAction {
|
||||
client: DelugeClient,
|
||||
}
|
||||
|
||||
impl DelugeAction {
|
||||
pub fn new(config: &DelugeConfig) -> Result<Self> {
|
||||
let client = DelugeClient::new(config)?;
|
||||
|
||||
Ok(DelugeAction { client })
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Action for DelugeAction {
|
||||
/// Return the name of the action
|
||||
fn name() -> &'static str {
|
||||
"Deluge"
|
||||
}
|
||||
|
||||
fn get_name(&self) -> &'static str {
|
||||
Self::name()
|
||||
}
|
||||
|
||||
/// Process all unprocessed magnet links and return the list of processed magnets
|
||||
async fn process_unprocessed_magnets(&mut self, db: &mut Database) -> Result<ProcessedMagnets> {
|
||||
let unprocessed_magnets = db.get_unprocessed_magnets_for_table::<DelugeProcessedTable>()?;
|
||||
let mut processed_magnets = Vec::new();
|
||||
let mut failed_magnets = Vec::new();
|
||||
|
||||
self.client.login().await?;
|
||||
|
||||
for magnet in unprocessed_magnets {
|
||||
match self.client.submit_magnet(&magnet.link).await {
|
||||
Ok(_) => {
|
||||
debug!(
|
||||
"Successfully submitted magnet link to {}: {}",
|
||||
Self::name(),
|
||||
magnet.title
|
||||
);
|
||||
debug!("Magnet link: {}", magnet.link);
|
||||
db.mark_magnet_processed_for_table::<DelugeProcessedTable>(magnet.id)?;
|
||||
processed_magnets.push(magnet);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to submit magnet link to {}: {}", Self::name(), e);
|
||||
failed_magnets.push(magnet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ProcessedMagnets {
|
||||
success: processed_magnets,
|
||||
failed: failed_magnets,
|
||||
})
|
||||
}
|
||||
}
|
||||
159
src/actions/deluge/client.rs
Normal file
159
src/actions/deluge/client.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
use std::net::ToSocketAddrs;
|
||||
|
||||
use color_eyre::eyre::{eyre, Result, WrapErr};
|
||||
use futures::SinkExt;
|
||||
use futures::StreamExt;
|
||||
use log::{debug, info};
|
||||
use serde_json::{json, Value};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::codec::{Framed, LengthDelimitedCodec};
|
||||
|
||||
use crate::actions::deluge::config::DelugeConfig;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum DelugeError {
|
||||
AuthenticationFailed,
|
||||
Rpc(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for DelugeError {}
|
||||
|
||||
impl std::fmt::Display for DelugeError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DelugeError::AuthenticationFailed => write!(f, "Authentication failed"),
|
||||
DelugeError::Rpc(s) => write!(f, "RPC error: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DelugeClient {
|
||||
config: DelugeConfig,
|
||||
stream: Option<Framed<TcpStream, LengthDelimitedCodec>>,
|
||||
request_id: u64,
|
||||
}
|
||||
|
||||
impl DelugeClient {
|
||||
pub fn new(config: &DelugeConfig) -> Result<Self> {
|
||||
let config = config.clone();
|
||||
|
||||
Ok(DelugeClient {
|
||||
config,
|
||||
stream: None,
|
||||
request_id: 0,
|
||||
})
|
||||
}
|
||||
|
||||
async fn connect(&mut self) -> Result<()> {
|
||||
if self.stream.is_none() {
|
||||
let addr = format!("{}:{}", self.config.host, self.config.port)
|
||||
.to_socket_addrs()
|
||||
.wrap_err_with(|| format!("Failed to resolve host: {}", self.config.host))?
|
||||
.next()
|
||||
.ok_or_else(|| eyre!("Failed to get socket address"))?;
|
||||
let tcp_stream = TcpStream::connect(addr).await.wrap_err_with(|| {
|
||||
format!(
|
||||
"Failed to connect to Deluge daemon at {}:{}",
|
||||
self.config.host, self.config.port
|
||||
)
|
||||
})?;
|
||||
|
||||
let length_delimited = LengthDelimitedCodec::new();
|
||||
let framed = Framed::new(tcp_stream, length_delimited);
|
||||
|
||||
info!(
|
||||
"Connected to Deluge daemon at {}:{}",
|
||||
self.config.host, self.config.port
|
||||
);
|
||||
|
||||
self.stream = Some(framed);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send_request(&mut self, method: &str, params: Vec<Value>) -> Result<u64> {
|
||||
self.connect().await?;
|
||||
|
||||
self.request_id += 1;
|
||||
let request = json!({
|
||||
"method": method,
|
||||
"params": params,
|
||||
"id": self.request_id,
|
||||
});
|
||||
debug!(
|
||||
"Sending request: {}",
|
||||
serde_json::to_string(&request).unwrap()
|
||||
);
|
||||
|
||||
if let Some(stream) = &mut self.stream {
|
||||
let bytes = serde_json::to_vec(&request)?;
|
||||
stream.send(bytes.into()).await?;
|
||||
Ok(self.request_id)
|
||||
} else {
|
||||
Err(eyre!("Stream is not initialized"))
|
||||
}
|
||||
}
|
||||
|
||||
async fn receive_response(&mut self, expected_id: u64) -> Result<Value> {
|
||||
if let Some(stream) = &mut self.stream {
|
||||
while let Some(response) = stream.next().await {
|
||||
let bytes = response?;
|
||||
let response: Value = serde_json::from_slice(&bytes)?;
|
||||
debug!(
|
||||
"Received response: {}",
|
||||
serde_json::to_string(&response).unwrap()
|
||||
);
|
||||
if response.get("id") == Some(&json!(expected_id)) {
|
||||
if let Some(error) = response.get("error") {
|
||||
if !error.is_null() {
|
||||
let error_string = serde_json::to_string(error)
|
||||
.unwrap_or_else(|_| "Unknown error".to_string());
|
||||
return Err(DelugeError::Rpc(error_string).into());
|
||||
}
|
||||
}
|
||||
if let Some(result) = response.get("result") {
|
||||
return Ok(result.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(eyre!("Connection closed prematurely"))
|
||||
} else {
|
||||
Err(eyre!("Stream is not initialized"))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn login(&mut self) -> Result<()> {
|
||||
let auth_id = self
|
||||
.send_request("auth.login", vec![json!(self.config.password)])
|
||||
.await?;
|
||||
let response = self.receive_response(auth_id).await?;
|
||||
if response.as_bool().unwrap_or(false) {
|
||||
info!("Successfully authenticated with Deluge daemon.");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(DelugeError::AuthenticationFailed.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn submit_magnet(&mut self, magnet_link: &str) -> Result<String> {
|
||||
let add_torrent_id = self
|
||||
.send_request(
|
||||
"core.add_torrent_magnet",
|
||||
vec![
|
||||
json!(magnet_link),
|
||||
json!({
|
||||
"download_location": self.config.download_dir,
|
||||
"paused": false,
|
||||
}),
|
||||
],
|
||||
)
|
||||
.await?;
|
||||
let response = self.receive_response(add_torrent_id).await?;
|
||||
if let Some(torrent_hash) = response.as_str() {
|
||||
info!("Successfully added torrent with hash: {}", torrent_hash);
|
||||
Ok(torrent_hash.to_string())
|
||||
} else {
|
||||
Err(DelugeError::Rpc("Failed to add torrent".to_string()).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
19
src/actions/deluge/config.rs
Normal file
19
src/actions/deluge/config.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
use crate::app::Enableable;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Configuration for the Deluge action
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct DelugeConfig {
|
||||
pub enable: bool,
|
||||
pub host: String,
|
||||
pub login: String,
|
||||
pub password: String,
|
||||
pub port: u16,
|
||||
pub download_dir: String,
|
||||
}
|
||||
|
||||
impl Enableable for DelugeConfig {
|
||||
fn is_enabled(&self) -> bool {
|
||||
self.enable
|
||||
}
|
||||
}
|
||||
31
src/actions/deluge/db.rs
Normal file
31
src/actions/deluge/db.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use crate::db::ProcessedTable;
|
||||
use crate::models::NewDelugeProcessed;
|
||||
use crate::schema::deluge_processed;
|
||||
use color_eyre::eyre::Context;
|
||||
use diesel::{QueryDsl, RunQueryDsl, SqliteConnection};
|
||||
|
||||
pub struct DelugeProcessedTable;
|
||||
|
||||
impl ProcessedTable for DelugeProcessedTable {
|
||||
fn get_processed_ids(conn: &mut SqliteConnection) -> color_eyre::Result<Vec<i32>> {
|
||||
deluge_processed::table
|
||||
.select(deluge_processed::magnet_id)
|
||||
.load(conn)
|
||||
.wrap_err("Failed to load processed magnet IDs for Bitmagnet")
|
||||
}
|
||||
|
||||
fn mark_processed(conn: &mut SqliteConnection, magnet_id: i32) -> color_eyre::Result<()> {
|
||||
let now = chrono::Utc::now().naive_utc();
|
||||
let new_processed = NewDelugeProcessed {
|
||||
magnet_id,
|
||||
processed_at: &now,
|
||||
};
|
||||
|
||||
diesel::insert_into(deluge_processed::table)
|
||||
.values(&new_processed)
|
||||
.execute(conn)
|
||||
.wrap_err("Failed to mark magnet as processed by Bitmagnet")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
7
src/actions/deluge/mod.rs
Normal file
7
src/actions/deluge/mod.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
pub mod action;
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod db;
|
||||
|
||||
pub use action::DelugeAction;
|
||||
pub use config::DelugeConfig;
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
pub mod action;
|
||||
pub mod bitmagnet;
|
||||
pub mod deluge;
|
||||
pub mod factory;
|
||||
pub mod transmission;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::actions::action::{Action, ProcessedMagnets};
|
||||
use crate::actions::transmission::client::TransmissionClient;
|
||||
use crate::actions::transmission::config::TransmissionConfig;
|
||||
use crate::db::{Database, TransmissionProcessedTable};
|
||||
use crate::actions::transmission::db::TransmissionProcessedTable;
|
||||
use crate::db::Database;
|
||||
use color_eyre::eyre::Result;
|
||||
use log::{debug, warn};
|
||||
|
||||
|
|
|
|||
31
src/actions/transmission/db.rs
Normal file
31
src/actions/transmission/db.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use crate::db::ProcessedTable;
|
||||
use crate::models::NewTransmissionProcessed;
|
||||
use crate::schema::transmission_processed;
|
||||
use color_eyre::eyre::Context;
|
||||
use diesel::{QueryDsl, RunQueryDsl, SqliteConnection};
|
||||
|
||||
pub struct TransmissionProcessedTable;
|
||||
|
||||
impl ProcessedTable for TransmissionProcessedTable {
|
||||
fn get_processed_ids(conn: &mut SqliteConnection) -> color_eyre::Result<Vec<i32>> {
|
||||
transmission_processed::table
|
||||
.select(transmission_processed::magnet_id)
|
||||
.load(conn)
|
||||
.wrap_err("Failed to load processed magnet IDs for Transmission")
|
||||
}
|
||||
|
||||
fn mark_processed(conn: &mut SqliteConnection, magnet_id: i32) -> color_eyre::Result<()> {
|
||||
let now = chrono::Utc::now().naive_utc();
|
||||
let new_processed = NewTransmissionProcessed {
|
||||
magnet_id,
|
||||
processed_at: &now,
|
||||
};
|
||||
|
||||
diesel::insert_into(transmission_processed::table)
|
||||
.values(&new_processed)
|
||||
.execute(conn)
|
||||
.wrap_err("Failed to mark magnet as processed by Transmission")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod action;
|
||||
pub mod client;
|
||||
pub mod config;
|
||||
pub mod db;
|
||||
|
||||
pub use action::TransmissionAction;
|
||||
pub use config::TransmissionConfig;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::actions::action::{Action, ProcessedMagnets};
|
||||
use crate::actions::bitmagnet::BitmagnetAction;
|
||||
use crate::actions::deluge::DelugeAction;
|
||||
use crate::actions::factory::init_action;
|
||||
use crate::actions::transmission::TransmissionAction;
|
||||
use crate::config::Config;
|
||||
|
|
@ -47,6 +48,9 @@ impl App {
|
|||
if let Some(action) = init_action(&self.config.bitmagnet, BitmagnetAction::new)? {
|
||||
self.actions.push(action);
|
||||
}
|
||||
if let Some(action) = init_action(&self.config.deluge, DelugeAction::new)? {
|
||||
self.actions.push(action);
|
||||
}
|
||||
if let Some(action) = init_action(&self.config.transmission, TransmissionAction::new)? {
|
||||
self.actions.push(action);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::actions::bitmagnet::BitmagnetConfig;
|
||||
use crate::actions::deluge::DelugeConfig;
|
||||
use crate::actions::transmission::TransmissionConfig;
|
||||
use crate::args::Args;
|
||||
use crate::notifications::ntfy::NtfyConfig;
|
||||
|
|
@ -31,6 +32,9 @@ pub struct Config {
|
|||
#[serde(default)]
|
||||
pub bitmagnet: Option<BitmagnetConfig>,
|
||||
|
||||
#[serde(default)]
|
||||
pub deluge: Option<DelugeConfig>,
|
||||
|
||||
#[serde(default)]
|
||||
pub transmission: Option<TransmissionConfig>,
|
||||
|
||||
|
|
|
|||
55
src/db.rs
55
src/db.rs
|
|
@ -1,5 +1,5 @@
|
|||
use crate::models::{Magnet, NewBitmagnetProcessed, NewMagnet, NewTag, NewTransmissionProcessed};
|
||||
use crate::schema::{bitmagnet_processed, magnet_tags, magnets, tags, transmission_processed};
|
||||
use crate::models::{Magnet, NewMagnet, NewTag};
|
||||
use crate::schema::{magnet_tags, magnets, tags};
|
||||
use crate::PostInfo;
|
||||
use color_eyre::eyre::{eyre, Result, WrapErr};
|
||||
use diesel::prelude::*;
|
||||
|
|
@ -16,57 +16,6 @@ pub trait ProcessedTable {
|
|||
fn mark_processed(conn: &mut SqliteConnection, magnet_id: i32) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct BitmagnetProcessedTable;
|
||||
pub struct TransmissionProcessedTable;
|
||||
|
||||
impl ProcessedTable for BitmagnetProcessedTable {
|
||||
fn get_processed_ids(conn: &mut SqliteConnection) -> Result<Vec<i32>> {
|
||||
bitmagnet_processed::table
|
||||
.select(bitmagnet_processed::magnet_id)
|
||||
.load(conn)
|
||||
.wrap_err("Failed to load processed magnet IDs for Bitmagnet")
|
||||
}
|
||||
|
||||
fn mark_processed(conn: &mut SqliteConnection, magnet_id: i32) -> Result<()> {
|
||||
let now = chrono::Utc::now().naive_utc();
|
||||
let new_processed = NewBitmagnetProcessed {
|
||||
magnet_id,
|
||||
processed_at: &now,
|
||||
};
|
||||
|
||||
diesel::insert_into(bitmagnet_processed::table)
|
||||
.values(&new_processed)
|
||||
.execute(conn)
|
||||
.wrap_err("Failed to mark magnet as processed by Bitmagnet")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ProcessedTable for TransmissionProcessedTable {
|
||||
fn get_processed_ids(conn: &mut SqliteConnection) -> Result<Vec<i32>> {
|
||||
transmission_processed::table
|
||||
.select(transmission_processed::magnet_id)
|
||||
.load(conn)
|
||||
.wrap_err("Failed to load processed magnet IDs for Transmission")
|
||||
}
|
||||
|
||||
fn mark_processed(conn: &mut SqliteConnection, magnet_id: i32) -> Result<()> {
|
||||
let now = chrono::Utc::now().naive_utc();
|
||||
let new_processed = NewTransmissionProcessed {
|
||||
magnet_id,
|
||||
processed_at: &now,
|
||||
};
|
||||
|
||||
diesel::insert_into(transmission_processed::table)
|
||||
.values(&new_processed)
|
||||
.execute(conn)
|
||||
.wrap_err("Failed to mark magnet as processed by Transmission")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
/// Database for storing magnet links and associated information
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::schema::{bitmagnet_processed, magnet_tags, magnets, tags, transmission_processed};
|
||||
use crate::schema::{
|
||||
bitmagnet_processed, deluge_processed, magnet_tags, magnets, tags, transmission_processed,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::prelude::*;
|
||||
|
||||
|
|
@ -73,6 +75,24 @@ pub struct NewBitmagnetProcessed<'a> {
|
|||
pub processed_at: &'a NaiveDateTime,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[diesel(table_name = deluge_processed)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct DelugeProcessed {
|
||||
pub id: i32,
|
||||
pub magnet_id: i32,
|
||||
pub processed_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = deluge_processed)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct NewDelugeProcessed<'a> {
|
||||
pub magnet_id: i32,
|
||||
pub processed_at: &'a NaiveDateTime,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[diesel(table_name = transmission_processed)]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,14 @@ diesel::table! {
|
|||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
deluge_processed (id) {
|
||||
id -> Integer,
|
||||
magnet_id -> Integer,
|
||||
processed_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
magnet_tags (magnet_id, tag_id) {
|
||||
magnet_id -> Integer,
|
||||
|
|
@ -43,12 +51,14 @@ diesel::table! {
|
|||
}
|
||||
|
||||
diesel::joinable!(bitmagnet_processed -> magnets (magnet_id));
|
||||
diesel::joinable!(deluge_processed -> magnets (magnet_id));
|
||||
diesel::joinable!(magnet_tags -> magnets (magnet_id));
|
||||
diesel::joinable!(magnet_tags -> tags (tag_id));
|
||||
diesel::joinable!(transmission_processed -> magnets (magnet_id));
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
bitmagnet_processed,
|
||||
deluge_processed,
|
||||
magnet_tags,
|
||||
magnets,
|
||||
tags,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue