Add fullporner provider with xiaoshenke proxy

- Implement fullporner.com provider with search, pagination, categories, pornstars
- Add xiaoshenke.net proxy for Cloudflare-protected embed videos
- Proxy fetches signed URLs via FlareSolverr and streams video content
- Fix quality bitmask mapping (1=360p, 2=480p, 4=720p, 8=1080p)
- Support HEAD/GET/Range for video streaming

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Simon
2026-10-08 17:33:09 +00:00
parent ea567a791e
commit e204202611
12 changed files with 1039 additions and 43 deletions

View File

@@ -25,14 +25,14 @@ type AnyErr = Box<dyn std::error::Error + Send + Sync + 'static>;
#[derive(serde::Serialize, serde::Deserialize, Clone)]
pub struct Requester {
#[serde(skip)]
client: Client,
pub(crate) client: Client,
#[serde(skip)]
cookie_jar: Arc<Jar>,
pub(crate) cookie_jar: Arc<Jar>,
#[serde(skip)]
debug_trace_id: Option<String>,
proxy: bool,
pub(crate) debug_trace_id: Option<String>,
pub(crate) proxy: bool,
flaresolverr_session: Option<String>,
user_agent: Option<String>,
pub(crate) user_agent: Option<String>,
}
impl fmt::Debug for Requester {
@@ -75,7 +75,7 @@ impl Requester {
.map(|host| host.trim_start_matches("www.").to_ascii_lowercase())
}
fn remember_flaresolverr_user_agent(url: &str, user_agent: &str) {
pub(crate) fn remember_flaresolverr_user_agent(url: &str, user_agent: &str) {
if user_agent.is_empty() {
return;
}
@@ -155,7 +155,7 @@ impl Requester {
default.clone()
}
fn store_flaresolverr_cookies(
pub(crate) fn store_flaresolverr_cookies(
&mut self,
request_url: &str,
cookies: &[crate::util::flaresolverr::FlaresolverrCookie],
@@ -280,7 +280,7 @@ impl Requester {
.unwrap_or_else(|| "none".to_string())
}
fn build_client(cookie_jar: Arc<Jar>, user_agent: Option<&str>) -> Client {
pub(crate) fn build_client(cookie_jar: Arc<Jar>, user_agent: Option<&str>) -> Client {
let mut builder = Client::builder()
.tls_cert_verification(false)
.emulation(Emulation::Firefox151)
@@ -427,12 +427,66 @@ impl Requester {
Ok(response)
}
/// Like [`get_raw_with_headers`], but disables redirect-following so a 3xx response's
/// `Location` header can be inspected directly.
pub async fn get_no_redirect_raw_with_headers(
&mut self,
url: &str,
headers: Vec<(String, String)>,
) -> Result<Response, wreq::Error> {
self.adopt_remembered_identity(url);
let cookie_preview = self
.cookie_header_for_url(url)
.map(|cookie| crate::util::flow_debug::preview(&cookie, 160))
.unwrap_or_else(|| "none".to_string());
#[cfg(not(feature = "debug"))]
let _ = &cookie_preview;
crate::flow_debug!(
"trace={} requester get_no_redirect_raw_with_headers url={} headers={} cookies={} proxy={}",
self.debug_trace_id().unwrap_or("none"),
crate::util::flow_debug::preview(url, 120),
headers.len(),
cookie_preview,
self.proxy
);
let mut builder = Client::builder()
.tls_cert_verification(false)
.emulation(Emulation::Firefox151)
.cookie_provider(self.cookie_jar.clone())
.redirect(Policy::none());
if let Some(user_agent) = self.user_agent.as_deref() {
let mut hdrs = HeaderMap::new();
if let Ok(value) = HeaderValue::from_str(user_agent) {
hdrs.insert(USER_AGENT, value);
builder = builder.default_headers(hdrs);
}
}
let client = builder.build().expect("Failed to create HTTP client");
let mut request = client.get(url).version(Version::HTTP_11);
if self.proxy {
if let Ok(proxy_url) = env::var("BURP_URL") {
let proxy = Proxy::all(&proxy_url).unwrap();
request = request.proxy(proxy);
}
}
// Set custom headers
for (key, value) in headers.iter() {
request = request.header(key, value);
}
let response = request.send().await?;
self.store_response_cookies(url, &response);
Ok(response)
}
pub async fn get_raw_with_headers(
&mut self,
url: &str,
headers: Vec<(String, String)>,
) -> Result<Response, wreq::Error> {
self.get_raw_with_headers_timeout(url, headers, None).await
self.get_raw_with_headers_timeout(url, headers, None, None).await
}
pub async fn get_raw_with_headers_timeout(
@@ -440,21 +494,28 @@ impl Requester {
url: &str,
headers: Vec<(String, String)>,
timeout: Option<Duration>,
http_version: Option<Version>,
) -> Result<Response, wreq::Error> {
let cookie_preview = self.debug_cookie_preview_from_owned_headers(url, &headers);
#[cfg(not(feature = "debug"))]
let _ = &cookie_preview;
crate::flow_debug!(
"trace={} requester get_raw_with_headers url={} headers={} cookies={} proxy={}",
"trace={} requester get_raw_with_headers url={} headers={} cookies={} http_version={:?} proxy={}",
self.debug_trace_id().unwrap_or("none"),
crate::util::flow_debug::preview(url, 120),
headers.len(),
cookie_preview,
http_version,
self.proxy
);
let client = Self::build_client(self.cookie_jar.clone(), self.user_agent.as_deref());
let mut request = client.get(url).version(Version::HTTP_11);
let mut request = client.get(url);
if let Some(version) = http_version {
request = request.version(version);
} else {
request = request.version(Version::HTTP_11);
}
if let Some(timeout) = timeout {
request = request.timeout(timeout);
}