From 552f8c8627fa54154a40f809b19f0e99a5daa4e3 Mon Sep 17 00:00:00 2001 From: Xientra Date: Wed, 19 Nov 2025 03:01:40 +0000 Subject: [PATCH 1/4] feat: add support for DigitalCore --- .../migrations/0022_digitalcore_support.sql | 2 + backend/src/models/indexer.rs | 13 ++- .../src/services/user_stats/digitalcore.rs | 110 ++++++++++++++++++ backend/src/services/user_stats/mod.rs | 1 + 4 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 backend/migrations/0022_digitalcore_support.sql create mode 100644 backend/src/services/user_stats/digitalcore.rs diff --git a/backend/migrations/0022_digitalcore_support.sql b/backend/migrations/0022_digitalcore_support.sql new file mode 100644 index 0000000..6ab8dfd --- /dev/null +++ b/backend/migrations/0022_digitalcore_support.sql @@ -0,0 +1,2 @@ +INSERT INTO indexers (name, auth_data) VALUES +('DigitalCore', '{"user_id": {"value": "", "explanation": "Repeat instructions for pass_cookie, except copy the number after ''uid=''."}, "pass_cookie": {"value": "", "explanation": "1) Log in to this tracker. 2) Open up the DevTools. 3) Navigate to the Network tab. 4) Click on the Doc button (Chrome Browser) or HTML button (FireFox). 5) Refresh the page by pressing F5. 6) Click on the first row entry. 7) Select the Headers tab on the Right panel. 8) Find ''cookie:'' in the Request Headers section. 9) Copy the value after ''pass='' and paste it here."}}'); diff --git a/backend/src/models/indexer.rs b/backend/src/models/indexer.rs index ae1433f..eaa18b5 100644 --- a/backend/src/models/indexer.rs +++ b/backend/src/models/indexer.rs @@ -10,10 +10,11 @@ use crate::{ services::user_stats::{ aither::AitherScraper, anime_bytes::AnimeBytesScraper, anthelion::AnthelionScraper, blutopia::BlutopiaScraper, broadcasthenet::BroadcasthenetScraper, - darkpeers::DarkPeersScraper, fear_no_peer::FearNoPeerScraper, - gazelle_games::GazelleGamesScraper, homiehelpdesk::HomieHelpDeskScraper, - ita_torrents::ItaTorrentsScraper, lst::LSTScraper, myanonamouse::MyAnonamouseScraper, - oldtoons::OldToonsScraper, only_encodes::OnlyEncodesScraper, orpheus::OrpheusScraper, + darkpeers::DarkPeersScraper, digitalcore::DigitalCoreScraper, + fear_no_peer::FearNoPeerScraper, gazelle_games::GazelleGamesScraper, + homiehelpdesk::HomieHelpDeskScraper, ita_torrents::ItaTorrentsScraper, lst::LSTScraper, + myanonamouse::MyAnonamouseScraper, oldtoons::OldToonsScraper, + only_encodes::OnlyEncodesScraper, orpheus::OrpheusScraper, phoenix_project::PhoenixProjectScraper, rastastugan::RastastuganScraper, redacted::RedactedScraper, reel_flix::ReelFlixScraper, seed_pool::SeedPoolScraper, upload_cx::UploadCXScraper, yoinked::YoinkedScraper, yu_scene::YuSceneScraper, @@ -157,6 +158,10 @@ impl Indexer { static HOMIE_HELP_DESK_SCRAPER: HomieHelpDeskScraper = HomieHelpDeskScraper; &HOMIE_HELP_DESK_SCRAPER } + "DigitalCore" => { + static DIGITAL_CORE_SCRAPER: DigitalCoreScraper = DigitalCoreScraper; + &DIGITAL_CORE_SCRAPER + } _ => { return Err(Error::CouldNotScrapeIndexer( "indexer has no scraper".into(), diff --git a/backend/src/services/user_stats/digitalcore.rs b/backend/src/services/user_stats/digitalcore.rs new file mode 100644 index 0000000..03b7d0a --- /dev/null +++ b/backend/src/services/user_stats/digitalcore.rs @@ -0,0 +1,110 @@ +use serde::Deserialize; + +use async_trait::async_trait; + +use crate::{ + error::{Error, Result}, + models::{ + indexer::{Indexer, Scraper}, + user_stats::UserProfileScraped, + }, +}; + +#[derive(Debug, Deserialize)] +struct DigitalCoreResponse { + bonuspoang: f64, + downloaded: i64, + downloaded_real: i64, + uploaded: i64, + uploaded_real: i64, + // leechbonus: f32, + #[serde(rename = "peersSeeder")] + peers_seeder: i32, + #[serde(rename = "peersLeecher")] + peers_leecher: i32, + warned: String, + #[serde(rename = "torrentComments")] + torrent_comments: i32, + donor: String, + #[serde(rename = "forumPosts")] + forum_posts: i32, + invitees: i32, +} + +impl From for UserProfileScraped { + fn from(wrapper: DigitalCoreResponse) -> Self { + let ratio = if wrapper.downloaded == 0 && wrapper.uploaded > 0 { + f32::MAX + } else { + wrapper.uploaded as f32 / wrapper.downloaded as f32 + }; + + UserProfileScraped { + uploaded: wrapper.uploaded, + downloaded: wrapper.downloaded, + ratio, + donor: Some(wrapper.donor.eq("yes")), + warned: Some(wrapper.warned.eq("yes")), + seeding: Some(wrapper.peers_seeder), + leeching: Some(wrapper.peers_leecher), + bonus_points: Some(wrapper.bonuspoang as i64), + uploaded_real: Some(wrapper.uploaded_real), + downloaded_real: Some(wrapper.downloaded_real), + torrent_comments: Some(wrapper.torrent_comments), + posts: Some(wrapper.forum_posts), + invited: Some(wrapper.invitees), + ..Default::default() + } + } +} + +pub struct DigitalCoreScraper; + +#[async_trait] +impl Scraper for DigitalCoreScraper { + async fn scrape( + &self, + indexer: Indexer, + client: &reqwest::Client, + ) -> Result { + let user_id = indexer + .auth_data + .get("user_id") + .ok_or("DigitalCore user_id not found.") + .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? + .get("value") + .ok_or("DigitalCore user_id value not found") + .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? + .as_str() + .unwrap(); + + let res = client + .get(format!("https://digitalcore.club/api/v1/users/{}", user_id)) + .header( + "Cookie", + format!( + "uid={}; pass={}", + user_id, + indexer + .auth_data + .get("pass_cookie") + .ok_or("DigitalCore pass cookie not found.") + .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? + .get("value") + .ok_or("DigitalCore pass cookie value not found") + .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? + .as_str() + .unwrap() + ), + ) + .send() + .await + .map_err(|e| Error::CouldNotScrapeIndexer(e.to_string()))?; + + let body = res.text().await.unwrap(); + let response = serde_json::from_str::(&body) + .map_err(|e| Error::CouldNotScrapeIndexer(e.to_string()))?; + + Ok(response.into()) + } +} diff --git a/backend/src/services/user_stats/mod.rs b/backend/src/services/user_stats/mod.rs index 5b3db98..1ae27fd 100644 --- a/backend/src/services/user_stats/mod.rs +++ b/backend/src/services/user_stats/mod.rs @@ -5,6 +5,7 @@ pub mod blutopia; pub mod broadcasthenet; pub mod common; pub mod darkpeers; +pub mod digitalcore; pub mod fear_no_peer; pub mod gazelle_games; pub mod homiehelpdesk; From 8c4bbfc8550c93b5b8d4c960c15476982cf900b7 Mon Sep 17 00:00:00 2001 From: Xientra Date: Wed, 19 Nov 2025 19:55:54 +0000 Subject: [PATCH 2/4] docs: add DigitalCore to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 13f43b4..3ad12f3 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ You can then visit the frontend at `http://localhost:3000` and the swagger at `h * BLU * BTN * DarkPeers +* DigitalCore * FNP * GGn * HomieHelpDesk From 95c299a6350cd72092c9219355ac962e1482cd5f Mon Sep 17 00:00:00 2001 From: Xientra Date: Wed, 19 Nov 2025 19:59:21 +0000 Subject: [PATCH 3/4] feat: simplify DigitalCore indexer settings --- .../migrations/0022_digitalcore_support.sql | 2 +- .../src/services/user_stats/digitalcore.rs | 41 ++++++++++--------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/backend/migrations/0022_digitalcore_support.sql b/backend/migrations/0022_digitalcore_support.sql index 6ab8dfd..bd94a78 100644 --- a/backend/migrations/0022_digitalcore_support.sql +++ b/backend/migrations/0022_digitalcore_support.sql @@ -1,2 +1,2 @@ INSERT INTO indexers (name, auth_data) VALUES -('DigitalCore', '{"user_id": {"value": "", "explanation": "Repeat instructions for pass_cookie, except copy the number after ''uid=''."}, "pass_cookie": {"value": "", "explanation": "1) Log in to this tracker. 2) Open up the DevTools. 3) Navigate to the Network tab. 4) Click on the Doc button (Chrome Browser) or HTML button (FireFox). 5) Refresh the page by pressing F5. 6) Click on the first row entry. 7) Select the Headers tab on the Right panel. 8) Find ''cookie:'' in the Request Headers section. 9) Copy the value after ''pass='' and paste it here."}}'); +('DigitalCore', '{"cookies": {"value": "", "explanation": "1) Log in to this tracker. 2) Open up the DevTools. 3) Navigate to the Network tab. 4) Click on the Doc button (Chrome Browser) or HTML button (FireFox). 5) Refresh the page by pressing F5. 6) Click on the first row entry. 7) Select the Headers tab on the Right panel. 8) Find ''cookie:'' in the Request Headers section. 9) Copy the value and paste it here."}}'); diff --git a/backend/src/services/user_stats/digitalcore.rs b/backend/src/services/user_stats/digitalcore.rs index 03b7d0a..546e0bb 100644 --- a/backend/src/services/user_stats/digitalcore.rs +++ b/backend/src/services/user_stats/digitalcore.rs @@ -67,35 +67,38 @@ impl Scraper for DigitalCoreScraper { indexer: Indexer, client: &reqwest::Client, ) -> Result { - let user_id = indexer + let cookies = indexer .auth_data - .get("user_id") - .ok_or("DigitalCore user_id not found.") + .get("cookies") + .ok_or("DigitalCore cookies not found.") .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? .get("value") - .ok_or("DigitalCore user_id value not found") + .ok_or("DigitalCore cookies not found.") .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? .as_str() .unwrap(); + if !cookies.contains("uid=") { + return Err(Error::CouldNotScrapeIndexer( + "Cannot find cookie for 'uid'.".to_string(), + )); + } + + if !cookies.contains("pass=") { + return Err(Error::CouldNotScrapeIndexer( + "Cannot find cookie for 'pass'.".to_string(), + )); + } + + let uid_cookie = cookies.split("uid=").collect::>()[1] + .split(";") + .collect::>()[0]; + let res = client - .get(format!("https://digitalcore.club/api/v1/users/{}", user_id)) + .get(format!("https://digitalcore.club/api/v1/users/{}", uid_cookie)) .header( "Cookie", - format!( - "uid={}; pass={}", - user_id, - indexer - .auth_data - .get("pass_cookie") - .ok_or("DigitalCore pass cookie not found.") - .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? - .get("value") - .ok_or("DigitalCore pass cookie value not found") - .map_err(|e| Error::CouldNotScrapeIndexer(e.into()))? - .as_str() - .unwrap() - ), + cookies ) .send() .await From 6aeece621bf07aa05891f56f5ed590ac70042ccc Mon Sep 17 00:00:00 2001 From: Xientra Date: Wed, 19 Nov 2025 20:01:21 +0000 Subject: [PATCH 4/4] style: run cargo fmt --- backend/src/services/user_stats/digitalcore.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/services/user_stats/digitalcore.rs b/backend/src/services/user_stats/digitalcore.rs index 546e0bb..c746565 100644 --- a/backend/src/services/user_stats/digitalcore.rs +++ b/backend/src/services/user_stats/digitalcore.rs @@ -95,11 +95,11 @@ impl Scraper for DigitalCoreScraper { .collect::>()[0]; let res = client - .get(format!("https://digitalcore.club/api/v1/users/{}", uid_cookie)) - .header( - "Cookie", - cookies - ) + .get(format!( + "https://digitalcore.club/api/v1/users/{}", + uid_cookie + )) + .header("Cookie", cookies) .send() .await .map_err(|e| Error::CouldNotScrapeIndexer(e.to_string()))?;