From 004399ecbe3282a41e359c4fce323dee7df59dec Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 5 Apr 2026 21:13:05 +0000 Subject: [PATCH] flaresolverr session cycling --- Cargo.toml | 1 + build.rs | 2 ++ src/providers/sextb.rs | 4 +-- src/providers/vjav.rs | 7 ++++ src/util/flaresolverr.rs | 70 ++++++++++++++++++++++++++++++++++++---- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ebf0a0..5883d05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ chrono = "0.4.44" md5 = "0.8.0" [lints.rust] +warnings = "deny" unexpected_cfgs = "allow" # Or keep it as a warning but whitelist the cfg: # unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_error_description_deprecated)'] } diff --git a/build.rs b/build.rs index 72d646d..12d246a 100644 --- a/build.rs +++ b/build.rs @@ -1,3 +1,5 @@ +#![deny(warnings)] + use std::env; use std::fs; use std::path::PathBuf; diff --git a/src/providers/sextb.rs b/src/providers/sextb.rs index 4ba8d65..bc52b8e 100644 --- a/src/providers/sextb.rs +++ b/src/providers/sextb.rs @@ -172,8 +172,8 @@ impl SextbProvider { fn build_channel(&self, _clientversion: ClientVersion) -> Channel { let genres = self.genres.read().map(|value| value.clone()).unwrap_or_default(); - let studios = self.studios.read().map(|value| value.clone()).unwrap_or_default(); - let actresses = self + let _studios = self.studios.read().map(|value| value.clone()).unwrap_or_default(); + let _actresses = self .actresses .read() .map(|value| value.clone()) diff --git a/src/providers/vjav.rs b/src/providers/vjav.rs index bd099b0..4c7351d 100644 --- a/src/providers/vjav.rs +++ b/src/providers/vjav.rs @@ -122,6 +122,7 @@ struct DetailResponse { related_searches: Option>, } +#[allow(dead_code)] #[derive(Debug, Deserialize, Clone, Default)] struct DetailVideo { #[serde(default)] @@ -152,6 +153,7 @@ struct DetailVideo { related_searches: Option>, } +#[allow(dead_code)] #[derive(Debug, Deserialize, Clone, Default)] struct DetailStatistics { #[serde(default)] @@ -164,6 +166,7 @@ struct DetailStatistics { dislikes: i64, } +#[allow(dead_code)] #[derive(Debug, Deserialize, Clone, Default)] struct DetailUser { #[serde(default)] @@ -176,12 +179,14 @@ struct DetailUser { subscribers_count: String, } +#[allow(dead_code)] #[derive(Debug, Deserialize, Clone, Default)] struct DetailChannel { #[serde(default)] title: String, } +#[allow(dead_code)] #[derive(Debug, Deserialize, Clone, Default)] struct DetailCategory { #[serde(default)] @@ -228,6 +233,7 @@ struct MembersResponse { members: Vec, } +#[allow(dead_code)] #[derive(Debug, Deserialize, Clone, Default)] struct MemberSummary { #[serde(default)] @@ -240,6 +246,7 @@ struct MemberSummary { statistics: MemberStatistics, } +#[allow(dead_code)] #[derive(Debug, Deserialize, Clone, Default)] struct MemberStatistics { #[serde(default)] diff --git a/src/util/flaresolverr.rs b/src/util/flaresolverr.rs index ac001b9..e78dac6 100644 --- a/src/util/flaresolverr.rs +++ b/src/util/flaresolverr.rs @@ -1,7 +1,8 @@ -use std::{collections::HashMap, env}; +use std::{collections::HashMap, env, sync::Arc}; use serde_json::json; use serde_json::Value; +use tokio::sync::Mutex; use wreq::{Client, Proxy}; use wreq_util::Emulation; @@ -51,11 +52,22 @@ pub struct FlareSolverrResponse { pub version: String, } +#[derive(Clone)] pub struct Flaresolverr { url: String, proxy: bool, } +#[derive(Debug, Default)] +struct SessionState { + ready_session: Option, +} + +fn global_session_state() -> &'static Arc> { + static STATE: std::sync::OnceLock>> = std::sync::OnceLock::new(); + STATE.get_or_init(|| Arc::new(Mutex::new(SessionState::default()))) +} + impl Flaresolverr { pub fn new(url: String) -> Self { Self { url, proxy: false } @@ -144,18 +156,64 @@ impl Flaresolverr { Ok(typed) } + async fn ensure_ready_session_locked( + &self, + state: &mut SessionState, + ) -> Result> { + if let Some(existing) = state.ready_session.clone() { + return Ok(existing); + } + let created = self.create_session().await?; + state.ready_session = Some(created.clone()); + Ok(created) + } + pub async fn solve( &self, request: FlareSolverrRequest, ) -> Result> { - let session = self.create_session().await?; - let result = self.solve_with_session(request, &session).await; - if let Err(error) = self.destroy_session(&session).await { + // Keep one ready session globally and rotate it per solve: + // - solve with current ready session + // - create replacement session in parallel + // - destroy old session + // - keep replacement as new ready session + let session_state = global_session_state().clone(); + let mut state = session_state.lock().await; + let active_session = self.ensure_ready_session_locked(&mut state).await?; + + let replacement_creator = { + let solver = self.clone(); + tokio::spawn(async move { solver.create_session().await }) + }; + + let solve_result = self.solve_with_session(request, &active_session).await; + + let replacement_session = match replacement_creator.await { + Ok(Ok(session)) => session, + Ok(Err(error)) => { + eprintln!( + "FlareSolverr replacement session creation failed, retrying inline: {}", + error + ); + self.create_session().await? + } + Err(join_error) => { + eprintln!( + "FlareSolverr replacement task join failed, retrying inline: {}", + join_error + ); + self.create_session().await? + } + }; + + if let Err(error) = self.destroy_session(&active_session).await { eprintln!( "FlareSolverr session cleanup failed for session '{}': {}", - session, error + active_session, error ); } - result + + state.ready_session = Some(replacement_session); + solve_result } }