Create simple torznab api module. Start asyncifying the indexer requests
This commit is contained in:
parent
2a225ac2db
commit
6741d35783
|
@ -17,6 +17,15 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi_term"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argmap"
|
name = "argmap"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
@ -46,7 +55,7 @@ dependencies = [
|
||||||
"derive_builder",
|
"derive_builder",
|
||||||
"diligent-date-parser",
|
"diligent-date-parser",
|
||||||
"never",
|
"never",
|
||||||
"quick-xml",
|
"quick-xml 0.22.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -182,13 +191,22 @@ name = "cross-seed"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"argmap",
|
"argmap",
|
||||||
|
"bytes 1.1.0",
|
||||||
"figment",
|
"figment",
|
||||||
|
"futures 0.3.21",
|
||||||
"lava_torrent",
|
"lava_torrent",
|
||||||
"magnet-url",
|
"magnet-url",
|
||||||
|
"quick-xml 0.23.0",
|
||||||
|
"reqwest",
|
||||||
|
"rss",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_with",
|
||||||
"tokio 1.19.2",
|
"tokio 1.19.2",
|
||||||
"toml",
|
"toml",
|
||||||
"torznab",
|
"torznab",
|
||||||
|
"tracing",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"urlencoding 2.1.0",
|
||||||
"wild",
|
"wild",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -262,8 +280,18 @@ version = "0.12.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
|
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.12.4",
|
||||||
"darling_macro",
|
"darling_macro 0.12.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core 0.13.4",
|
||||||
|
"darling_macro 0.13.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -280,13 +308,38 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling_macro"
|
name = "darling_macro"
|
||||||
version = "0.12.4"
|
version = "0.12.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
|
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core 0.12.4",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.13.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core 0.13.4",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
@ -306,7 +359,7 @@ version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
|
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling 0.12.4",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -418,6 +471,21 @@ version = "0.1.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
|
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
|
@ -425,6 +493,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -433,6 +502,34 @@ version = "0.3.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.21"
|
version = "0.3.21"
|
||||||
|
@ -451,10 +548,16 @@ version = "0.3.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1014,6 +1117,16 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9279fbdacaad3baf559d8cabe0acc3d06e30ea14931af31af79578ac0946decc"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.18"
|
version = "1.0.18"
|
||||||
|
@ -1119,7 +1232,7 @@ dependencies = [
|
||||||
"atom_syndication",
|
"atom_syndication",
|
||||||
"derive_builder",
|
"derive_builder",
|
||||||
"never",
|
"never",
|
||||||
"quick-xml",
|
"quick-xml 0.22.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1232,6 +1345,28 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_with_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_with_macros"
|
||||||
|
version = "1.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
|
||||||
|
dependencies = [
|
||||||
|
"darling 0.13.4",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
@ -1243,6 +1378,15 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sharded-slab"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -1350,6 +1494,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thread_local"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.6.0"
|
version = "1.6.0"
|
||||||
|
@ -1372,7 +1525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
|
checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.12",
|
"bytes 0.4.12",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"mio 0.6.23",
|
"mio 0.6.23",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"tokio-codec",
|
"tokio-codec",
|
||||||
|
@ -1416,7 +1569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"
|
checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.12",
|
"bytes 0.4.12",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"tokio-io",
|
"tokio-io",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1426,7 +1579,7 @@ version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
|
checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"tokio-executor",
|
"tokio-executor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1437,7 +1590,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
|
checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1446,7 +1599,7 @@ version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4"
|
checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"tokio-io",
|
"tokio-io",
|
||||||
"tokio-threadpool",
|
"tokio-threadpool",
|
||||||
]
|
]
|
||||||
|
@ -1458,7 +1611,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
|
checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.12",
|
"bytes 0.4.12",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1480,7 +1633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
|
checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"mio 0.6.23",
|
"mio 0.6.23",
|
||||||
|
@ -1510,7 +1663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
|
checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1520,7 +1673,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
|
checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.12",
|
"bytes 0.4.12",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"iovec",
|
"iovec",
|
||||||
"mio 0.6.23",
|
"mio 0.6.23",
|
||||||
"tokio-io",
|
"tokio-io",
|
||||||
|
@ -1536,7 +1689,7 @@ dependencies = [
|
||||||
"crossbeam-deque",
|
"crossbeam-deque",
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
|
@ -1551,7 +1704,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
|
checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio-executor",
|
"tokio-executor",
|
||||||
]
|
]
|
||||||
|
@ -1563,7 +1716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82"
|
checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.12",
|
"bytes 0.4.12",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"log",
|
"log",
|
||||||
"mio 0.6.23",
|
"mio 0.6.23",
|
||||||
"tokio-codec",
|
"tokio-codec",
|
||||||
|
@ -1578,7 +1731,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0"
|
checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes 0.4.12",
|
"bytes 0.4.12",
|
||||||
"futures",
|
"futures 0.1.31",
|
||||||
"iovec",
|
"iovec",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1649,7 +1802,7 @@ dependencies = [
|
||||||
"torrent-common",
|
"torrent-common",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-futures",
|
"tracing-futures",
|
||||||
"urlencoding",
|
"urlencoding 1.3.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1688,6 +1841,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
|
checksum = "7709595b8878a4965ce5e87ebf880a7d39c9afc6837721b21a5a816a8117d921"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"valuable",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1701,6 +1855,31 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-log"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"tracing-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tracing-subscriber"
|
||||||
|
version = "0.3.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4bc28f93baff38037f64e6f43d34cfa1605f27a49c34e8a04c5e78b0babf2596"
|
||||||
|
dependencies = [
|
||||||
|
"ansi_term",
|
||||||
|
"sharded-slab",
|
||||||
|
"smallvec 1.8.0",
|
||||||
|
"thread_local",
|
||||||
|
"tracing-core",
|
||||||
|
"tracing-log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -1767,6 +1946,18 @@ version = "1.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a1f0175e03a0973cf4afd476bef05c26e228520400eb1fd473ad417b1c00ffb"
|
checksum = "5a1f0175e03a0973cf4afd476bef05c26e228520400eb1fd473ad417b1c00ffb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "urlencoding"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "valuable"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -7,11 +7,23 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tokio = { version = "1.19.2", features = ["full"] }
|
tokio = { version = "1.19.2", features = ["full"] }
|
||||||
|
tracing = "0.1.35"
|
||||||
|
tracing-subscriber = "0.3.11"
|
||||||
|
futures = "0.3.21"
|
||||||
toml = "0.5.9"
|
toml = "0.5.9"
|
||||||
lava_torrent = "0.7.0" # https://docs.rs/lava_torrent/0.7.0/lava_torrent/
|
lava_torrent = "0.7.0" # https://docs.rs/lava_torrent/0.7.0/lava_torrent/
|
||||||
torznab = "0.7.2" # https://docs.rs/torznab/0.7.2/torznab/
|
torznab = "0.7.2" # https://docs.rs/torznab/0.7.2/torznab/
|
||||||
magnet-url = "2.0.0"
|
magnet-url = "2.0.0"
|
||||||
|
serde_with = "1.14.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
figment = { version = "0.10", features = ["toml", "env"] }
|
figment = { version = "0.10", features = ["toml", "env"] }
|
||||||
wild = "2.0.4"
|
wild = "2.0.4"
|
||||||
argmap = "1.1.2"
|
argmap = "1.1.2"
|
||||||
|
|
||||||
|
reqwest = {version = "0.11", default_features = false, features = ["gzip", "json", "rustls-tls"]}
|
||||||
|
urlencoding = "2.1.0"
|
||||||
|
|
||||||
|
# Torznab stuff
|
||||||
|
rss = "2.0.1"
|
||||||
|
bytes = "1.1.0"
|
||||||
|
quick-xml = {version = "0.23.0", features = ["serialize"]}
|
|
@ -5,6 +5,8 @@ use std::collections::HashMap;
|
||||||
use figment::{Figment, providers::{Format, Toml, Env}};
|
use figment::{Figment, providers::{Format, Toml, Env}};
|
||||||
use figment::value::Value as FigmentValue;
|
use figment::value::Value as FigmentValue;
|
||||||
|
|
||||||
|
use crate::torznab::TorznabClient;
|
||||||
|
|
||||||
use super::CliProvider;
|
use super::CliProvider;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Deserialize, Serialize)]
|
||||||
|
@ -26,13 +28,26 @@ pub struct Config {
|
||||||
pub indexers: Vec<Indexer>,
|
pub indexers: Vec<Indexer>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
pub struct Indexer {
|
pub struct Indexer {
|
||||||
#[serde(skip_deserializing)]
|
#[serde(skip_deserializing)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub enabled: Option<bool>,
|
pub enabled: Option<bool>,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub api_key: String,
|
pub api_key: String,
|
||||||
|
|
||||||
|
#[serde(skip)]
|
||||||
|
pub client: Option<TorznabClient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Indexer {
|
||||||
|
pub async fn create_client(&mut self) -> Result<&TorznabClient, crate::torznab::ClientError> {
|
||||||
|
if self.client.is_none() {
|
||||||
|
self.client = Some(TorznabClient::new(self.name.clone(), &self.url, &self.api_key).await?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.client.as_ref().unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow dead code for functions. We should probably remove this later on.
|
// Allow dead code for functions. We should probably remove this later on.
|
||||||
|
|
98
src/main.rs
98
src/main.rs
|
@ -1,13 +1,20 @@
|
||||||
mod config;
|
mod config;
|
||||||
|
mod torznab;
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
|
use tracing::{info, Level, debug};
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
use lava_torrent::torrent::v1::Torrent;
|
use lava_torrent::torrent::v1::Torrent;
|
||||||
|
|
||||||
use torznab::Client as TorznabClient;
|
use crate::torznab::{GenericSearchParameters, SearchFunction};
|
||||||
|
use crate::torznab::search_parameters::{GenericSearchParametersBuilder, MovieSearchParametersBuilder};
|
||||||
|
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
fn read_torrents(path: &Path) -> Result<Vec<PathBuf>, Box<dyn Error>> {
|
fn read_torrents(path: &Path) -> Result<Vec<PathBuf>, Box<dyn Error>> {
|
||||||
let mut torrents = Vec::new();
|
let mut torrents = Vec::new();
|
||||||
|
@ -28,48 +35,67 @@ fn read_torrents(path: &Path) -> Result<Vec<PathBuf>, Box<dyn Error>> {
|
||||||
return Ok(torrents);
|
return Ok(torrents);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let subscriber = tracing_subscriber::fmt()
|
||||||
|
.with_max_level(Level::INFO)
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
tracing::subscriber::set_global_default(subscriber).expect("Failed to set global default log subscriber");
|
||||||
|
|
||||||
// Get config and debug the torrents
|
// Get config and debug the torrents
|
||||||
let config = Config::new();//.expect("Failed to get config");
|
let config = Config::new();
|
||||||
println!("Searching torrents in: {}", config.torrents_path_str());
|
info!("Searching torrents in: {}", config.torrents_path_str());
|
||||||
|
|
||||||
println!("Searching {} trackers: ", config.indexers.len());
|
let mut indexers = config.indexers.clone();
|
||||||
for indexer in config.indexers.iter() {
|
|
||||||
println!(" {}: {}", indexer.name, indexer.url);
|
// Create torznab clients for each indexer.
|
||||||
|
for indexer in indexers.iter_mut() {
|
||||||
|
indexer.create_client().await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let torrents = read_torrents(config.torrents_path()).unwrap();
|
// Log the trackers
|
||||||
|
info!("Searching {} trackers: ", indexers.len());
|
||||||
|
for indexer in indexers.iter() {
|
||||||
|
info!(" {}: {}", indexer.name, indexer.url);
|
||||||
|
debug!(" Can Search: {:?}", indexer.client.as_ref().unwrap().capabilities.searching_capabilities);
|
||||||
|
}
|
||||||
|
|
||||||
for torrent_path in torrents.iter() {
|
let torrent_files = read_torrents(config.torrents_path()).unwrap();
|
||||||
let torrent = Torrent::read_from_file(torrent_path).unwrap();
|
info!("Found {} torrents", torrent_files.len());
|
||||||
println!("{}:", torrent.name);
|
|
||||||
|
|
||||||
/* for indexer in config.indexers.iter() {
|
//panic!("rhfhujergfre");
|
||||||
if indexer.enabled {
|
|
||||||
let client = TorznabClient::new(indexer.url.clone());
|
|
||||||
let results = client.search(&torrent).unwrap();
|
|
||||||
println!("{}", results);
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
//TorznabClient
|
|
||||||
|
|
||||||
/*if let Some(announce) = torrent.announce {
|
// Convert the indexers to be async friendly.
|
||||||
println!(" Announce: {}", announce);
|
let mut indexers = indexers.iter()
|
||||||
}
|
.map(|indexer| Arc::new(RwLock::new(indexer.clone())))
|
||||||
if let Some(announce_list) = torrent.announce_list {
|
.collect::<Vec<_>>();
|
||||||
println!(" Announce list:");
|
|
||||||
for announce in announce_list {
|
|
||||||
for ann in announce {
|
|
||||||
println!(" {}", ann);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!(" Files:");
|
|
||||||
|
|
||||||
if let Some(files) = torrent.files {
|
let mut indexer_handles = vec![];
|
||||||
for file in files.iter() {
|
|
||||||
println!(" {}", file.path.to_str().unwrap());
|
for torrent_path in torrent_files.iter() {
|
||||||
}
|
let torrent = Arc::new(Torrent::read_from_file(torrent_path).unwrap());
|
||||||
} */
|
info!("{}:", torrent.name);
|
||||||
|
|
||||||
|
for indexer in indexers.iter() {
|
||||||
|
let mut indexer = Arc::clone(indexer);
|
||||||
|
let torrent = Arc::clone(&torrent);
|
||||||
|
indexer_handles.push(tokio::spawn(async move {
|
||||||
|
let lock = indexer.read().await;
|
||||||
|
match &lock.client {
|
||||||
|
Some(client) => {
|
||||||
|
let generic = GenericSearchParametersBuilder::new()
|
||||||
|
.query(torrent.name.clone())
|
||||||
|
.build();
|
||||||
|
client.search(SearchFunction::Search, generic).await.unwrap();
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
panic!("idfk");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
futures::future::join_all(indexer_handles).await;
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum SupportedParam {
|
||||||
|
Query,
|
||||||
|
Season,
|
||||||
|
Episode,
|
||||||
|
IMDB,
|
||||||
|
TMDB,
|
||||||
|
TVDB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for SupportedParam {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
match s.as_str() {
|
||||||
|
"q" => SupportedParam::Query,
|
||||||
|
"season" => SupportedParam::Season,
|
||||||
|
"ep" => SupportedParam::Episode,
|
||||||
|
"imdbid" => SupportedParam::IMDB,
|
||||||
|
"tmdbid" => SupportedParam::TMDB,
|
||||||
|
"tvdbid" => SupportedParam::TVDB,
|
||||||
|
_ => panic!("Unsupported param: {}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Deserialize)]
|
||||||
|
pub enum SearchCapability {
|
||||||
|
#[serde(rename = "search")]
|
||||||
|
Search,
|
||||||
|
|
||||||
|
#[serde(rename = "tv-search")]
|
||||||
|
TV,
|
||||||
|
|
||||||
|
#[serde(rename = "movie-search")]
|
||||||
|
Movie,
|
||||||
|
|
||||||
|
#[serde(rename = "music-search")]
|
||||||
|
Music,
|
||||||
|
|
||||||
|
#[serde(rename = "audio-search")]
|
||||||
|
Audio,
|
||||||
|
|
||||||
|
#[serde(rename = "book-search")]
|
||||||
|
Book
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for SearchCapability {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
match s.as_str() {
|
||||||
|
"search" => SearchCapability::Search,
|
||||||
|
"tv-search" => SearchCapability::TV,
|
||||||
|
"movie-search" => SearchCapability::Movie,
|
||||||
|
"music-search" => SearchCapability::Music,
|
||||||
|
"audio-search" => SearchCapability::Audio,
|
||||||
|
"book" => SearchCapability::Book,
|
||||||
|
_ => panic!("Unsupported param: {}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct SearchingCapabilities {
|
||||||
|
supported_functions: HashMap<SearchCapability, Vec<SupportedParam>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchingCapabilities {
|
||||||
|
pub fn new(supported_functions: HashMap<SearchCapability, Vec<SupportedParam>>) -> SearchingCapabilities {
|
||||||
|
SearchingCapabilities {
|
||||||
|
supported_functions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the search type is supported.
|
||||||
|
pub fn does_support_search(&self, search_capability: SearchCapability) -> bool {
|
||||||
|
self.supported_functions.contains_key(&search_capability)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if a search type supports a specific parameter.
|
||||||
|
pub fn does_search_support_param(&self, capability: SearchCapability, param: SupportedParam) -> bool {
|
||||||
|
match self.supported_functions.get(&capability) {
|
||||||
|
Some(params) => params.contains(¶m),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SearchingCapabilities {
|
||||||
|
fn default() -> Self {
|
||||||
|
SearchingCapabilities {
|
||||||
|
supported_functions: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SearchingCapabilities {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let raw: HashMap<SearchCapability, HashMap<String, String>> = Deserialize::deserialize(deserializer).unwrap();
|
||||||
|
let mut functions: HashMap<SearchCapability, Vec<SupportedParam>> = HashMap::new();
|
||||||
|
|
||||||
|
for (key, value) in raw.iter() {
|
||||||
|
let mut supported_params = Vec::new();
|
||||||
|
|
||||||
|
let available_str: String = value.get("available").map(String::to_owned).unwrap_or_default();
|
||||||
|
let available = available_str == "yes";
|
||||||
|
|
||||||
|
if available {
|
||||||
|
if let Some(params) = value.get("supportedParams") {
|
||||||
|
for param in params.split(',') {
|
||||||
|
supported_params.push(param.to_string().into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
functions.insert(key.clone(), supported_params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SearchingCapabilities {
|
||||||
|
supported_functions: functions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Category {
|
||||||
|
#[serde(with = "serde_with::rust::display_fromstr")]
|
||||||
|
id: u32,
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
#[serde(rename = "subcat")]
|
||||||
|
sub_categories: Option<Vec<Category>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||||
|
pub struct Categories {
|
||||||
|
#[serde(rename = "category")]
|
||||||
|
pub categories: Vec<Category>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Categories {
|
||||||
|
pub fn new(categories: Vec<Category>) -> Self {
|
||||||
|
Categories { categories }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Categories {
|
||||||
|
fn default() -> Self {
|
||||||
|
Categories {
|
||||||
|
categories: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
|
||||||
|
pub struct Capabilities {
|
||||||
|
pub categories: Categories,
|
||||||
|
|
||||||
|
#[serde(rename = "searching")]
|
||||||
|
pub searching_capabilities: SearchingCapabilities,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Capabilities {
|
||||||
|
fn default() -> Self {
|
||||||
|
Capabilities {
|
||||||
|
categories: Categories::default(),
|
||||||
|
searching_capabilities: SearchingCapabilities::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
use super::{Capabilities, TorznabFunction, SearchFunction, GenericSearchParameters, TorrentResult, ClientError};
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
|
use bytes::Buf;
|
||||||
|
|
||||||
|
use rss::Channel;
|
||||||
|
use tracing::{span, event, debug, info, Level};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TorznabClient {
|
||||||
|
http: reqwest::Client,
|
||||||
|
pub name: String,
|
||||||
|
pub base_url: String,
|
||||||
|
api_key: String,
|
||||||
|
pub capabilities: Capabilities,
|
||||||
|
pub client_span: tracing::Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TorznabClient {
|
||||||
|
fn client_span(name: &String) -> tracing::Span {
|
||||||
|
span!(Level::INFO, "torznab_client", indexer = %name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a new client without getting the capabilities
|
||||||
|
pub fn new_no_capabilities(name: String, base_url: &str, api_key: &str) -> Self {
|
||||||
|
TorznabClient {
|
||||||
|
name: name.clone(),
|
||||||
|
http: reqwest::Client::new(),
|
||||||
|
base_url: base_url.to_string(),
|
||||||
|
api_key: api_key.to_string(),
|
||||||
|
capabilities: Capabilities::default(),
|
||||||
|
client_span: Self::client_span(&name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a new client and get the capabilities.
|
||||||
|
pub async fn new(name: String, base_url: &str, api_key: &str) -> Result<Self, reqwest::Error> {
|
||||||
|
let mut client = TorznabClient {
|
||||||
|
name: name.clone(),
|
||||||
|
http: reqwest::Client::new(),
|
||||||
|
base_url: base_url.to_string(),
|
||||||
|
api_key: api_key.to_string(),
|
||||||
|
capabilities: Capabilities::default(),
|
||||||
|
client_span: Self::client_span(&name),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get capabilities and store them in the client before returning
|
||||||
|
client.store_capabilities().await?;
|
||||||
|
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a request to the indexer using the query parameters.
|
||||||
|
async fn request(&self, param_str: String) -> Result<Bytes, reqwest::Error> {
|
||||||
|
let span = span!(parent: &self.client_span, Level::INFO, "client request");
|
||||||
|
let _enter = span.enter();
|
||||||
|
|
||||||
|
// Construct the url
|
||||||
|
let url = format!("{}?apikey={}{}", self.base_url, self.api_key, param_str);
|
||||||
|
debug!("Url: {}", url);
|
||||||
|
|
||||||
|
self.http.get(url).send().await?.error_for_status()?.bytes().await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request the capabilities of the indexer and return them.
|
||||||
|
pub async fn request_capabilities(&self) -> Result<Capabilities, reqwest::Error> {
|
||||||
|
let params = TorznabFunction::Capabilities.to_params();
|
||||||
|
|
||||||
|
let res = self.request(params).await?;
|
||||||
|
let str_res = String::from_utf8(res.as_ref().to_vec()).unwrap(); // TODO Handle
|
||||||
|
|
||||||
|
let cap: Capabilities = quick_xml::de::from_str(&str_res).unwrap();
|
||||||
|
|
||||||
|
Ok(cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Request and store the capabilities of the indexer in the struct.
|
||||||
|
pub async fn store_capabilities(&mut self) -> Result<&Capabilities, reqwest::Error> {
|
||||||
|
self.capabilities = self.request_capabilities().await?;
|
||||||
|
Ok(&self.capabilities)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search for torrents.
|
||||||
|
pub async fn search(&self, func: SearchFunction, generic_params: GenericSearchParameters) -> Result<(), ClientError> {
|
||||||
|
let param_str = format!("{}{}", func.to_params(), generic_params.to_params());
|
||||||
|
|
||||||
|
let bytes = self.request(param_str).await?;
|
||||||
|
let reader = bytes.reader();
|
||||||
|
|
||||||
|
let channel = Channel::read_from(reader).unwrap(); // TODO: handle
|
||||||
|
let items = channel.into_items();
|
||||||
|
|
||||||
|
let torrents: Vec<TorrentResult> = items.iter()
|
||||||
|
.map(TorrentResult::from_item)
|
||||||
|
.collect::<Result<Vec<TorrentResult>, super::ResultError>>()?;
|
||||||
|
|
||||||
|
debug!("Found results: {:?}", torrents);
|
||||||
|
|
||||||
|
//Torrent::from
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ClientError {
|
||||||
|
HttpError(reqwest::Error),
|
||||||
|
SearchResultError(super::ResultError)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::Error> for ClientError {
|
||||||
|
fn from(e: reqwest::Error) -> Self {
|
||||||
|
ClientError::HttpError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<super::ResultError> for ClientError {
|
||||||
|
fn from(e: super::ResultError) -> Self {
|
||||||
|
ClientError::SearchResultError(e)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
use super::search_parameters::*;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum SearchFunction {
|
||||||
|
/// Free text search query.
|
||||||
|
Search,
|
||||||
|
/// Search query with tv specific query params and filtering.
|
||||||
|
TVSearch(TVSearchParameters),
|
||||||
|
/// Search query with movie specific query params and filtering.
|
||||||
|
MovieSearch(MovieSearchParameters),
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
/// Search query with music specific query params and filtering.
|
||||||
|
MusicSearch,
|
||||||
|
/// Search query with book specific query params and filtering.
|
||||||
|
BookSearch,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SearchFunction {
|
||||||
|
pub fn to_function_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
SearchFunction::Search => "search",
|
||||||
|
SearchFunction::TVSearch(_) => "tvsearch",
|
||||||
|
SearchFunction::MovieSearch(_) => "movie",
|
||||||
|
SearchFunction::MusicSearch => "music",
|
||||||
|
SearchFunction::BookSearch => "book",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_params(&self) -> String {
|
||||||
|
let mut params = String::new();
|
||||||
|
|
||||||
|
params.push_str(&format!("&t={}", self.to_function_str()));
|
||||||
|
|
||||||
|
// Concatenate the params of the search function.
|
||||||
|
match self {
|
||||||
|
SearchFunction::Search => {},//params.push_str(&s),
|
||||||
|
SearchFunction::TVSearch(p) => params.push_str(&p.to_params()),
|
||||||
|
SearchFunction::MovieSearch(p) => params.push_str(&p.to_params()),
|
||||||
|
_ => panic!("Not implemented!"), // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TorznabFunction {
|
||||||
|
/// Returns the capabilities of the api.
|
||||||
|
Capabilities,
|
||||||
|
|
||||||
|
SearchFunction(GenericSearchParameters, SearchFunction),
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
/// (newznab) Returns all details about a particular item.
|
||||||
|
Details,
|
||||||
|
/// (newznab) Returns an nfo for a particular item.
|
||||||
|
GetNFO,
|
||||||
|
/// (newznab) Returns nzb for the specified item.
|
||||||
|
GetNZB,
|
||||||
|
/// (newznab) Adds item to the users cart.
|
||||||
|
CardAdd,
|
||||||
|
/// (newznab) Removes item from the users cart.
|
||||||
|
CardDel,
|
||||||
|
/// (newznab) Returns all comments known about an item.
|
||||||
|
Comments,
|
||||||
|
/// (newznab) Adds a comment to an item.
|
||||||
|
CommentsAdd,
|
||||||
|
/// (newznab) Register a new user account.
|
||||||
|
Register,
|
||||||
|
/// (newznab) Retrieves information about an user account.
|
||||||
|
User
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TorznabFunction {
|
||||||
|
pub fn to_function_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
TorznabFunction::Capabilities => "caps",
|
||||||
|
TorznabFunction::SearchFunction(_, func) => func.to_function_str(),
|
||||||
|
_ => panic!("Not implemented! ({:?})", self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_params(&self) -> String {
|
||||||
|
let mut params = String::new();
|
||||||
|
|
||||||
|
// Concatenate the params of the search function.
|
||||||
|
match self {
|
||||||
|
TorznabFunction::SearchFunction(p, func) => {
|
||||||
|
params.push_str(&p.to_params());
|
||||||
|
params.push_str(&func.to_params());
|
||||||
|
},
|
||||||
|
_ => params.push_str(&format!("&t={}", self.to_function_str())),
|
||||||
|
}
|
||||||
|
|
||||||
|
params
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
pub mod search_parameters;
|
||||||
|
pub use search_parameters::GenericSearchParameters;
|
||||||
|
|
||||||
|
pub mod functions;
|
||||||
|
pub use functions::*;
|
||||||
|
|
||||||
|
pub mod capabilities;
|
||||||
|
pub use capabilities::*;
|
||||||
|
|
||||||
|
pub mod error;
|
||||||
|
pub use error::*;
|
||||||
|
|
||||||
|
pub mod client;
|
||||||
|
pub use client::*;
|
||||||
|
|
||||||
|
pub mod torrent_result;
|
||||||
|
pub use torrent_result::*;
|
|
@ -0,0 +1,250 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GenericSearchParameters {
|
||||||
|
/// The string search query.
|
||||||
|
pub query: Option<String>,
|
||||||
|
/// Categories to search in
|
||||||
|
pub categories: Vec<i32>,
|
||||||
|
/// Extended attribute names that should be included in results.
|
||||||
|
pub attributes: Vec<String>,
|
||||||
|
/// Specifies that all extended attributes should be included in the results. Overrules attrs.
|
||||||
|
pub extended: Option<bool>,
|
||||||
|
/// Number of items to skip in the result.
|
||||||
|
pub offset: Option<i32>,
|
||||||
|
/// Number of results to return. Limited by the limits element in the Capabilities.
|
||||||
|
pub limit: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericSearchParameters {
|
||||||
|
/// Convert the search parameters to a query string.
|
||||||
|
/// This will be prefixed with "&"
|
||||||
|
pub fn to_params(&self) -> String {
|
||||||
|
let mut params = String::new();
|
||||||
|
|
||||||
|
if let Some(query) = &self.query {
|
||||||
|
let encoded = urlencoding::encode(query);
|
||||||
|
params.push_str(&format!("&q={}", encoded));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.categories.len() > 0 {
|
||||||
|
params.push_str(&format!("&cat={}",
|
||||||
|
self.categories.iter()
|
||||||
|
.map(|i| i.to_string())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(",")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.attributes.len() > 0 {
|
||||||
|
params.push_str(&format!("&attrs={}", self.attributes.join(",")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(extended) = &self.extended {
|
||||||
|
// Convert the boolean to an integer.
|
||||||
|
let i = if *extended { 1 } else { 0 };
|
||||||
|
|
||||||
|
params.push_str(&format!("&extended={}", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(offset) = &self.offset {
|
||||||
|
params.push_str(&format!("&offset={}", offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(limit) = &self.limit {
|
||||||
|
params.push_str(&format!("&limit={}", limit));
|
||||||
|
}
|
||||||
|
|
||||||
|
params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GenericSearchParametersBuilder {
|
||||||
|
params: GenericSearchParameters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericSearchParametersBuilder {
|
||||||
|
pub fn new() -> GenericSearchParametersBuilder {
|
||||||
|
GenericSearchParametersBuilder {
|
||||||
|
params: GenericSearchParameters {
|
||||||
|
query: None,
|
||||||
|
categories: Vec::new(),
|
||||||
|
attributes: Vec::new(),
|
||||||
|
extended: None,
|
||||||
|
offset: None,
|
||||||
|
limit: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query(mut self, query: String) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.query = Some(query);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn categories(mut self, categories: &[i32]) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.categories.extend_from_slice(categories);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn category(mut self, category: i32) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.categories.push(category);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attributes(mut self, attributes: &[String]) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.attributes.extend_from_slice(attributes);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attribute(mut self, attribute: String) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.attributes.push(attribute);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extended(mut self, extended: bool) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.extended = Some(extended);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn offset(mut self, offset: i32) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.offset = Some(offset);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn limit(mut self, limit: i32) -> GenericSearchParametersBuilder {
|
||||||
|
self.params.limit = Some(limit);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> GenericSearchParameters {
|
||||||
|
self.params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct TVSearchParameters {
|
||||||
|
// idk what this is tbh
|
||||||
|
pub rid: Option<u32>,
|
||||||
|
/// Id of the show on TVDB.
|
||||||
|
pub tvdb_id: Option<u32>,
|
||||||
|
/// Id of the show on TVMaze.
|
||||||
|
pub tvmaze_id: Option<u32>,
|
||||||
|
/// Season number
|
||||||
|
pub season: Option<u16>,
|
||||||
|
/// Episode number
|
||||||
|
pub episode: Option<u16>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TVSearchParameters {
|
||||||
|
pub fn to_params(&self) -> String {
|
||||||
|
let mut params = String::new();
|
||||||
|
|
||||||
|
if let Some(rid) = &self.rid {
|
||||||
|
params.push_str(&format!("&rid={}", rid));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tvdb_id) = &self.tvdb_id {
|
||||||
|
params.push_str(&format!("&tvdbid={}", tvdb_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tvmaze_id) = &self.tvmaze_id {
|
||||||
|
params.push_str(&format!("&tvmazeid={}", tvmaze_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(season) = &self.season {
|
||||||
|
params.push_str(&format!("&season={}", season));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(episode) = &self.episode {
|
||||||
|
params.push_str(&format!("&ep={}", episode));
|
||||||
|
}
|
||||||
|
|
||||||
|
params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TVSearchParametersBuilder {
|
||||||
|
params: TVSearchParameters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TVSearchParametersBuilder {
|
||||||
|
pub fn new() -> TVSearchParametersBuilder {
|
||||||
|
TVSearchParametersBuilder {
|
||||||
|
params: TVSearchParameters {
|
||||||
|
rid: None,
|
||||||
|
tvdb_id: None,
|
||||||
|
tvmaze_id: None,
|
||||||
|
season: None,
|
||||||
|
episode: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rid(mut self, rid: u32) -> TVSearchParametersBuilder {
|
||||||
|
self.params.rid = Some(rid);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tvdb_id(mut self, tvdb_id: u32) -> TVSearchParametersBuilder {
|
||||||
|
self.params.tvdb_id = Some(tvdb_id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tvmaze_id(mut self, tvmaze_id: u32) -> TVSearchParametersBuilder {
|
||||||
|
self.params.tvmaze_id = Some(tvmaze_id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn season(mut self, season: u16) -> TVSearchParametersBuilder {
|
||||||
|
self.params.season = Some(season);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn episode(mut self, episode: u16) -> TVSearchParametersBuilder {
|
||||||
|
self.params.episode = Some(episode);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> TVSearchParameters {
|
||||||
|
self.params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct MovieSearchParameters {
|
||||||
|
/// Id of the movie on IMDB.
|
||||||
|
pub imdb_id: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MovieSearchParameters {
|
||||||
|
pub fn to_params(&self) -> String {
|
||||||
|
let mut params = String::new();
|
||||||
|
|
||||||
|
if let Some(imdb_id) = &self.imdb_id {
|
||||||
|
params.push_str(&format!("&imdbid={}", imdb_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MovieSearchParametersBuilder {
|
||||||
|
params: MovieSearchParameters,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MovieSearchParametersBuilder {
|
||||||
|
pub fn new() -> MovieSearchParametersBuilder {
|
||||||
|
MovieSearchParametersBuilder {
|
||||||
|
params: MovieSearchParameters {
|
||||||
|
imdb_id: None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn imdb_id(mut self, imdb_id: u32) -> MovieSearchParametersBuilder {
|
||||||
|
self.params.imdb_id = Some(imdb_id);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> MovieSearchParameters {
|
||||||
|
self.params
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
use rss::Item;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ResultError {
|
||||||
|
MissingTitle,
|
||||||
|
MissingLink,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct TorrentResult<'a> {
|
||||||
|
name: &'a str,
|
||||||
|
link: &'a str,
|
||||||
|
/* size: u64,
|
||||||
|
categories: Vec<u32>, */
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TorrentResult<'a> {
|
||||||
|
pub fn from_item(item: &'a Item) -> Result<Self, ResultError> {
|
||||||
|
let name = item.title().ok_or(ResultError::MissingTitle)?;
|
||||||
|
let link = item.link().ok_or(ResultError::MissingLink)?;
|
||||||
|
/* let size = item.enclosure().map(|e| e.length().parse::<u64>());
|
||||||
|
let categories = item.categories().ok_or(ResultError::MissingTitle)?; */
|
||||||
|
|
||||||
|
Ok(TorrentResult {
|
||||||
|
name,
|
||||||
|
link,
|
||||||
|
/* size,
|
||||||
|
categories, */
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* impl<'a> From<Item> for TorrentResult<'a> {
|
||||||
|
fn from(item: Item) -> Self {
|
||||||
|
TorrentResult {
|
||||||
|
name: item.title().unwrap(),
|
||||||
|
link: item.link().unwrap(),
|
||||||
|
size: item.size().unwrap(),
|
||||||
|
categories: item.categories().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} */
|
Loading…
Reference in New Issue