Check if a search result is a torrent we can cross seed,

This commit is contained in:
seanomik 2022-06-21 18:36:59 -04:00 committed by SeanOMik
parent 507b0e0594
commit 9a7d08cb81
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
5 changed files with 111 additions and 9 deletions

15
Cargo.lock generated
View File

@ -45,6 +45,17 @@ dependencies = [
"tokio 1.19.2", "tokio 1.19.2",
] ]
[[package]]
name = "async-recursion"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cda8f4bcc10624c4e85bc66b3f452cca98cfa5ca002dc83a16aad2367641bea"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "atom_syndication" name = "atom_syndication"
version = "0.11.0" version = "0.11.0"
@ -191,6 +202,7 @@ name = "cross-seed"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"argmap", "argmap",
"async-recursion",
"bytes 1.1.0", "bytes 1.1.0",
"figment", "figment",
"futures 0.3.21", "futures 0.3.21",
@ -820,8 +832,7 @@ dependencies = [
[[package]] [[package]]
name = "magnet-url" name = "magnet-url"
version = "2.0.0" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/SeanOMik/magnet-url-rs.git?branch=main#9e1bf9a9a85e83b99b10c8475b0a4a10e6d897e8"
checksum = "ac3b4c4004e88aca00cc0c60782e5642c8fc628deca19e530ce58aa76e737d74"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"regex", "regex",

View File

@ -13,12 +13,13 @@ 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 = { git = "https://github.com/SeanOMik/magnet-url-rs.git", branch = "main" }
serde_with = "1.14.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"
async-recursion = "1.0.0"
reqwest = {version = "0.11", default_features = false, features = ["gzip", "json", "rustls-tls"]} reqwest = {version = "0.11", default_features = false, features = ["gzip", "json", "rustls-tls"]}
urlencoding = "2.1.0" urlencoding = "2.1.0"

View File

@ -8,7 +8,7 @@ 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, AnnounceList};
use crate::torznab::{GenericSearchParameters, SearchFunction}; use crate::torznab::{GenericSearchParameters, SearchFunction};
use crate::torznab::search_parameters::{GenericSearchParametersBuilder, MovieSearchParametersBuilder}; use crate::torznab::search_parameters::{GenericSearchParametersBuilder, MovieSearchParametersBuilder};
@ -46,7 +46,7 @@ async fn main() {
// Get config and debug the torrents // Get config and debug the torrents
let config = Config::new(); let config = Config::new();
info!("Searching torrents in: {}", config.torrents_path_str()); info!("Searching for torrents in: {}", config.torrents_path_str());
let mut indexers = config.indexers.clone(); let mut indexers = config.indexers.clone();
@ -62,6 +62,7 @@ async fn main() {
debug!(" Can Search: {:?}", indexer.client.as_ref().unwrap().capabilities.searching_capabilities); debug!(" Can Search: {:?}", indexer.client.as_ref().unwrap().capabilities.searching_capabilities);
} }
// Log the amount of torrents.
let torrent_files = read_torrents(config.torrents_path()).unwrap(); let torrent_files = read_torrents(config.torrents_path()).unwrap();
info!("Found {} torrents", torrent_files.len()); info!("Found {} torrents", torrent_files.len());
@ -77,6 +78,8 @@ async fn main() {
let torrent = Arc::new(torrent); let torrent = Arc::new(torrent);
for indexer in indexers.iter() { for indexer in indexers.iter() {
info!("Checking for \"{}\"", torrent.name);
let mut indexer = Arc::clone(indexer); let mut indexer = Arc::clone(indexer);
let torrent = Arc::clone(&torrent); let torrent = Arc::clone(&torrent);
indexer_handles.push(tokio::spawn(async move { indexer_handles.push(tokio::spawn(async move {
@ -88,7 +91,46 @@ async fn main() {
.build(); .build();
let results = client.search(SearchFunction::Search, generic).await.unwrap(); let results = client.search(SearchFunction::Search, generic).await.unwrap();
//println!("Results: {:?}", results); // The first result should be the correct one.
if let Some(result) = results.first() {
let found_torrent = result.download_torrent().await.unwrap();
if let Some(found_announces) = &found_torrent.announce_list {
// Some urls can be encoded so we need to decode to compare them.
let found_announces: Vec<Vec<String>> = found_announces.iter()
.map(|a_list| a_list.iter().map(|a| urlencoding::decode(a).unwrap().to_string()).collect::<Vec<String>>())
.collect();
if let Some(torrent_announces) = &torrent.announce_list {
let mut found_announces_flat: Vec<&String> = Vec::new();
for i in found_announces.iter() {
for j in i.iter() {
found_announces_flat.push(j);
}
}
let mut flat_announces: Vec<&String> = Vec::new();
for i in torrent_announces.iter() {
for j in i.iter() {
flat_announces.push(j);
}
}
// Check if the announce urls from the found torrent are in the one
// that is on the file system.
let mut in_tracker = true;
for found_url in found_announces_flat.iter() {
in_tracker = in_tracker && flat_announces.contains(found_url);
}
if !in_tracker {
info!("Found a cross-seedable torrent for {}", found_torrent.name);
} else {
debug!("Found the torrent in its original indexer, skipping...");
}
}
}
}
}, },
None => { None => {
panic!("idfk"); panic!("idfk");

View File

@ -1,7 +1,9 @@
#[derive(Debug)] #[derive(Debug)]
pub enum ClientError { pub enum ClientError {
HttpError(reqwest::Error), HttpError(reqwest::Error),
SearchResultError(super::ResultError) SearchResultError(super::ResultError),
InvalidRedirect,
TorrentError(lava_torrent::LavaTorrentError),
} }
impl From<reqwest::Error> for ClientError { impl From<reqwest::Error> for ClientError {
@ -14,4 +16,10 @@ impl From<super::ResultError> for ClientError {
fn from(e: super::ResultError) -> Self { fn from(e: super::ResultError) -> Self {
ClientError::SearchResultError(e) ClientError::SearchResultError(e)
} }
}
impl From<lava_torrent::LavaTorrentError> for ClientError {
fn from(e: lava_torrent::LavaTorrentError) -> Self {
ClientError::TorrentError(e)
}
} }

View File

@ -1,15 +1,21 @@
use lava_torrent::torrent::v1::Torrent;
use rss::Item; use rss::Item;
use async_recursion::async_recursion;
use super::ClientError;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResultError { pub enum ResultError {
MissingTitle, MissingTitle,
MissingLink, MissingLink,
InvalidRedirect,
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct TorrentResult { pub struct TorrentResult {
name: String, pub name: String,
link: String, pub link: String,
/* size: u64, /* size: u64,
categories: Vec<u32>, */ categories: Vec<u32>, */
} }
@ -28,6 +34,40 @@ impl TorrentResult {
categories, */ categories, */
}) })
} }
#[async_recursion]
async fn download_impl(&self, client: &reqwest::Client, url: &str) -> Result<Torrent, ClientError> {
let res = client
.get(&self.link)
.send().await?;
if res.status() == 301 {
let headers = res.headers();
if let Some(location) = headers.get(reqwest::header::LOCATION) {
let location = location.to_str().unwrap();
self.download_impl(client, location).await
} else {
Err(ClientError::InvalidRedirect)
}
} else {
let bytes = res.bytes().await?;
if url.starts_with("magnet:?") {
let magnet = magnet_url::Magnet::new(url).unwrap();
todo!() // TODO
} else {
let torrent = Torrent::read_from_bytes(bytes)?;
Ok(torrent)
}
}
}
pub async fn download_torrent(&self) -> Result<Torrent, ClientError> {
self.download_impl(&reqwest::Client::default(), &self.link).await
}
} }
/* impl<'a> From<Item> for TorrentResult<'a> { /* impl<'a> From<Item> for TorrentResult<'a> {