From f95c9526f221666884cbec90935ed9498408a7a2 Mon Sep 17 00:00:00 2001 From: yggverse Date: Sat, 6 Sep 2025 18:41:30 +0300 Subject: [PATCH] use shared `btracker-fs` library --- Cargo.toml | 16 +++-- src/main.rs | 3 +- src/public.rs | 180 -------------------------------------------------- 3 files changed, 12 insertions(+), 187 deletions(-) delete mode 100644 src/public.rs diff --git a/Cargo.toml b/Cargo.toml index a0599b1..a336a86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,11 +11,17 @@ repository = "https://github.com/yggverse/btracker" # homepage = "https://yggverse.github.io" [dependencies] -clap = { version = "4.5", features = ["derive"] } -rocket = "0.5" -librqbit-core = "5.0" +btracker-fs = { version = "0.1", features = ["public"] } chrono = { version = "0.4.41", features = ["serde"] } +clap = { version = "4.5", features = ["derive"] } +librqbit-core = "5.0" +rand = "0.9" +rocket = "0.5" +rocket_dyn_templates = { version = "0.2", features = ["tera"] } url = { version = "2.5", features = ["serde"] } urlencoding = "2.1" -rocket_dyn_templates = { version = "0.2", features = ["tera"] } -rand = "0.9" + +# development +# [patch.crates-io] +# btracker-fs = { git = "https://github.com/yggverse/btracker-fs.git" } +# btracker-fs = { path = "../btracker-fs" } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 055bc4d..f1a4e9e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,14 +4,13 @@ extern crate rocket; mod config; mod feed; mod meta; -mod public; mod scraper; mod torrent; +use btracker_fs::public::{Order, Public, Sort}; use config::Config; use feed::Feed; use meta::Meta; -use public::{Order, Public, Sort}; use rocket::{State, http::Status, response::content::RawXml, serde::Serialize}; use rocket_dyn_templates::{Template, context}; use scraper::{Scrape, Scraper}; diff --git a/src/public.rs b/src/public.rs deleted file mode 100644 index 9a80c64..0000000 --- a/src/public.rs +++ /dev/null @@ -1,180 +0,0 @@ -use chrono::{DateTime, Utc}; -use std::{fs, io::Error, path::PathBuf, time::SystemTime}; - -#[derive(Clone, Debug, Default)] -pub enum Sort { - #[default] - Modified, -} - -#[derive(Clone, Debug, Default)] -pub enum Order { - #[default] - Asc, - Desc, -} - -pub struct Torrent { - pub bytes: Vec, - pub time: DateTime, -} - -pub struct Public { - default_capacity: usize, - pub default_limit: usize, - root: PathBuf, -} - -impl Public { - // Constructors - - pub fn init( - root: PathBuf, - default_limit: usize, - default_capacity: usize, - ) -> Result { - if !root.is_dir() { - return Err("Public root is not directory".into()); - } - Ok(Self { - default_capacity, - default_limit, - root: root.canonicalize().map_err(|e| e.to_string())?, - }) - } - - // Getters - - pub fn torrent(&self, info_hash: librqbit_core::Id20) -> Option { - let mut p = PathBuf::from(&self.root); - p.push(format!("{}.{E}", info_hash.as_string())); - Some(Torrent { - bytes: fs::read(&p).ok()?, - time: p.metadata().ok()?.modified().ok()?.into(), - }) - } - - pub fn torrents( - &self, - keyword: Option<&str>, - sort_order: Option<(Sort, Order)>, - start: Option, - limit: Option, - ) -> Result<(usize, Vec), Error> { - let f = self.files(keyword, sort_order)?; - let t = f.len(); - let l = limit.unwrap_or(t); - let mut b = Vec::with_capacity(l); - for file in f.into_iter().skip(start.unwrap_or_default()).take(l) { - b.push(Torrent { - bytes: fs::read(file.path)?, - time: file.modified.into(), - }) - } - Ok((t, b)) - } - - pub fn href(&self, info_hash: &str, path: &str) -> Option { - let mut relative = PathBuf::from(info_hash); - relative.push(path); - - let mut absolute = PathBuf::from(&self.root); - absolute.push(&relative); - - let c = absolute.canonicalize().ok()?; - if c.starts_with(&self.root) && c.exists() { - Some(relative.to_string_lossy().into()) - } else { - None - } - } - - // Helpers - - fn files( - &self, - keyword: Option<&str>, - sort_order: Option<(Sort, Order)>, - ) -> Result, Error> { - let mut files = Vec::with_capacity(self.default_capacity); - for dir_entry in fs::read_dir(&self.root)? { - let entry = dir_entry?; - let path = entry.path(); - if !path.is_file() || path.extension().is_none_or(|e| e != E) { - continue; - } - if let Some(k) = keyword - && !k.trim_matches(S).is_empty() - && !librqbit_core::torrent_metainfo::torrent_from_bytes(&fs::read(&path)?) - .is_ok_and(|m: librqbit_core::torrent_metainfo::TorrentMetaV1Owned| { - k.split(S) - .filter(|s| !s.is_empty()) - .map(|s| s.trim().to_lowercase()) - .all(|q| { - m.info_hash.as_string().to_lowercase().contains(&q) - || m.info - .name - .as_ref() - .is_some_and(|n| n.to_string().to_lowercase().contains(&q)) - || m.comment - .as_ref() - .is_some_and(|c| c.to_string().to_lowercase().contains(&q)) - || m.created_by - .as_ref() - .is_some_and(|c| c.to_string().to_lowercase().contains(&q)) - || m.publisher - .as_ref() - .is_some_and(|p| p.to_string().to_lowercase().contains(&q)) - || m.publisher_url - .as_ref() - .is_some_and(|u| u.to_string().to_lowercase().contains(&q)) - || m.announce - .as_ref() - .is_some_and(|a| a.to_string().to_lowercase().contains(&q)) - || m.announce_list.iter().any(|l| { - l.iter().any(|a| a.to_string().to_lowercase().contains(&q)) - }) - || m.info.files.as_ref().is_some_and(|f| { - f.iter().any(|f| { - let mut p = PathBuf::new(); - f.full_path(&mut p).is_ok_and(|_| { - p.to_string_lossy().to_lowercase().contains(&q) - }) - }) - }) - }) - }) - { - continue; - } - files.push(File { - modified: entry.metadata()?.modified()?, - path, - }) - } - if let Some((sort, order)) = sort_order { - match sort { - Sort::Modified => match order { - Order::Asc => files.sort_by(|a, b| a.modified.cmp(&b.modified)), - Order::Desc => files.sort_by(|a, b| b.modified.cmp(&a.modified)), - }, - } - } - Ok(files) - } -} - -// Local members - -/// Torrent file extension -const E: &str = "torrent"; - -/// Search keyword separators -const S: &[char] = &[ - '_', '-', ':', ';', ',', '(', ')', '[', ']', '/', '!', '?', ' ', // @TODO make optional -]; - -struct File { - modified: SystemTime, - path: PathBuf, -}