porndish fix
This commit is contained in:
@@ -231,6 +231,10 @@ impl PorndishProvider {
|
|||||||
matches!(host, "myvidplay.com" | "www.myvidplay.com")
|
matches!(host, "myvidplay.com" | "www.myvidplay.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_vidara_host(host: &str) -> bool {
|
||||||
|
matches!(host, "vidara.so" | "www.vidara.so")
|
||||||
|
}
|
||||||
|
|
||||||
fn is_allowed_list_url(url: &str) -> bool {
|
fn is_allowed_list_url(url: &str) -> bool {
|
||||||
let Some(url) = Self::parse_url(url) else {
|
let Some(url) = Self::parse_url(url) else {
|
||||||
return false;
|
return false;
|
||||||
@@ -294,6 +298,35 @@ impl PorndishProvider {
|
|||||||
Self::is_myvidplay_host(host) && url.path().starts_with("/pass_md5/")
|
Self::is_myvidplay_host(host) && url.path().starts_with("/pass_md5/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_allowed_vidara_iframe_url(url: &str) -> bool {
|
||||||
|
let Some(url) = Self::parse_url(url) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if url.scheme() != "https" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(host) = url.host_str() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
Self::is_vidara_host(host) && url.path().starts_with("/e/")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vidara_api_url(iframe_url: &str) -> Option<String> {
|
||||||
|
let url = Self::parse_url(iframe_url)?;
|
||||||
|
if !Self::is_allowed_vidara_iframe_url(iframe_url) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let filecode = url
|
||||||
|
.path_segments()?
|
||||||
|
.filter(|segment| !segment.is_empty())
|
||||||
|
.next_back()?
|
||||||
|
.to_string();
|
||||||
|
if filecode.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(format!("https://vidara.so/api/stream?filecode={filecode}"))
|
||||||
|
}
|
||||||
|
|
||||||
fn proxied_thumb(&self, options: &ServerOptions, thumb: &str) -> String {
|
fn proxied_thumb(&self, options: &ServerOptions, thumb: &str) -> String {
|
||||||
if thumb.is_empty() {
|
if thumb.is_empty() {
|
||||||
return String::new();
|
return String::new();
|
||||||
@@ -304,6 +337,13 @@ impl PorndishProvider {
|
|||||||
build_proxy_url(options, "porndish-thumb", &strip_url_scheme(thumb))
|
build_proxy_url(options, "porndish-thumb", &strip_url_scheme(thumb))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn proxied_video(&self, options: &ServerOptions, page_url: &str) -> String {
|
||||||
|
if page_url.is_empty() || !Self::is_allowed_detail_url(page_url) {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
build_proxy_url(options, "porndish", &strip_url_scheme(page_url))
|
||||||
|
}
|
||||||
|
|
||||||
fn push_unique(target: &Arc<RwLock<Vec<FilterOption>>>, item: FilterOption) {
|
fn push_unique(target: &Arc<RwLock<Vec<FilterOption>>>, item: FilterOption) {
|
||||||
if let Ok(mut values) = target.write() {
|
if let Ok(mut values) = target.write() {
|
||||||
if !values.iter().any(|value| value.id == item.id) {
|
if !values.iter().any(|value| value.id == item.id) {
|
||||||
@@ -805,6 +845,27 @@ sys.stdout.buffer.write(response.content)
|
|||||||
Ok(format!("{base}{suffix}?token={token}&expiry={now}"))
|
Ok(format!("{base}{suffix}?token={token}&expiry={now}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn resolve_vidara_stream(&self, iframe_url: &str) -> Result<String> {
|
||||||
|
let api_url = Self::vidara_api_url(iframe_url)
|
||||||
|
.ok_or_else(|| Error::from(format!("blocked vidara iframe url: {iframe_url}")))?;
|
||||||
|
|
||||||
|
let response = Self::fetch_html(&api_url, Some(iframe_url)).await?;
|
||||||
|
let json: serde_json::Value = serde_json::from_str(&response)
|
||||||
|
.map_err(|error| Error::from(format!("vidara json parse failed: {error}")))?;
|
||||||
|
let stream_url = json
|
||||||
|
.get("streaming_url")
|
||||||
|
.and_then(|value| value.as_str())
|
||||||
|
.unwrap_or("")
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
if stream_url.is_empty() || !(stream_url.starts_with("https://") || stream_url.starts_with("http://")) {
|
||||||
|
return Err(Error::from("vidara stream missing streaming_url".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(stream_url)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_embed_source(fragment: &str) -> Result<Option<(String, String)>> {
|
fn parse_embed_source(fragment: &str) -> Result<Option<(String, String)>> {
|
||||||
let iframe_regex = Self::regex(r#"(?is)<iframe[^>]+src="([^"]+)"[^>]*>"#)?;
|
let iframe_regex = Self::regex(r#"(?is)<iframe[^>]+src="([^"]+)"[^>]*>"#)?;
|
||||||
Ok(iframe_regex.captures(fragment).and_then(|captures| {
|
Ok(iframe_regex.captures(fragment).and_then(|captures| {
|
||||||
@@ -947,25 +1008,71 @@ sys.stdout.buffer.write(response.content)
|
|||||||
item.tags = Some(tags);
|
item.tags = Some(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut fallback_embed: Option<(String, String)> = None;
|
||||||
|
let mut selected_embed: Option<(String, String)> = None;
|
||||||
|
|
||||||
for fragment in self.extract_iframe_fragments(html)? {
|
for fragment in self.extract_iframe_fragments(html)? {
|
||||||
let Some((embed_html, iframe_url)) = Self::parse_embed_source(&fragment)? else {
|
let Some((embed_html, iframe_url)) = Self::parse_embed_source(&fragment)? else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let iframe_url = self.normalize_url(&iframe_url);
|
let iframe_url = self.normalize_url(&iframe_url);
|
||||||
|
|
||||||
|
if Self::is_allowed_vidara_iframe_url(&iframe_url) {
|
||||||
|
selected_embed = Some((embed_html, iframe_url));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if fallback_embed.is_none()
|
||||||
|
&& (Self::is_allowed_myvidplay_iframe_url(&iframe_url)
|
||||||
|
|| iframe_url.starts_with("https://"))
|
||||||
|
{
|
||||||
|
fallback_embed = Some((embed_html, iframe_url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((embed_html, iframe_url)) = selected_embed.or(fallback_embed) {
|
||||||
item.embed = Some(VideoEmbed {
|
item.embed = Some(VideoEmbed {
|
||||||
html: embed_html,
|
html: embed_html,
|
||||||
source: iframe_url.clone(),
|
source: iframe_url.clone(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if iframe_url.contains("myvidplay.com") {
|
let proxy_url = self.proxied_video(options, page_url);
|
||||||
|
if Self::is_allowed_vidara_iframe_url(&iframe_url) {
|
||||||
|
match self.resolve_vidara_stream(&iframe_url).await {
|
||||||
|
Ok(_stream_url) => {
|
||||||
|
if !proxy_url.is_empty() {
|
||||||
|
item.url = proxy_url.clone();
|
||||||
|
item.formats = Some(vec![VideoFormat::new(
|
||||||
|
proxy_url,
|
||||||
|
"sd".to_string(),
|
||||||
|
"m3u8".to_string(),
|
||||||
|
)]);
|
||||||
|
} else {
|
||||||
|
item.url = page_url.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
report_provider_error_background(
|
||||||
|
"porndish",
|
||||||
|
"resolve_vidara_stream",
|
||||||
|
&format!("iframe_url={iframe_url}; error={error}"),
|
||||||
|
);
|
||||||
|
item.url = page_url.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if Self::is_allowed_myvidplay_iframe_url(&iframe_url) {
|
||||||
match self.resolve_myvidplay_stream(&iframe_url).await {
|
match self.resolve_myvidplay_stream(&iframe_url).await {
|
||||||
Ok(stream_url) => {
|
Ok(_stream_url) => {
|
||||||
item.url = stream_url.clone();
|
if !proxy_url.is_empty() {
|
||||||
let mut format =
|
item.url = proxy_url.clone();
|
||||||
VideoFormat::new(stream_url.clone(), "sd".to_string(), "mp4".to_string());
|
item.formats = Some(vec![VideoFormat::new(
|
||||||
format.add_http_header("Referer".to_string(), iframe_url.clone());
|
proxy_url,
|
||||||
item.formats = Some(vec![format]);
|
"sd".to_string(),
|
||||||
|
"mp4".to_string(),
|
||||||
|
)]);
|
||||||
|
} else {
|
||||||
|
item.url = page_url.to_string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
report_provider_error_background(
|
report_provider_error_background(
|
||||||
@@ -979,8 +1086,6 @@ sys.stdout.buffer.write(response.content)
|
|||||||
} else {
|
} else {
|
||||||
item.url = iframe_url;
|
item.url = iframe_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.formats.is_none() && item.url != page_url {
|
if item.formats.is_none() && item.url != page_url {
|
||||||
@@ -1286,6 +1391,9 @@ mod tests {
|
|||||||
assert!(PorndishProvider::is_allowed_myvidplay_pass_url(
|
assert!(PorndishProvider::is_allowed_myvidplay_pass_url(
|
||||||
"https://myvidplay.com/pass_md5/abc/def"
|
"https://myvidplay.com/pass_md5/abc/def"
|
||||||
));
|
));
|
||||||
|
assert!(PorndishProvider::is_allowed_vidara_iframe_url(
|
||||||
|
"https://vidara.so/e/abc123"
|
||||||
|
));
|
||||||
|
|
||||||
assert!(!PorndishProvider::is_allowed_list_url(
|
assert!(!PorndishProvider::is_allowed_list_url(
|
||||||
"https://169.254.169.254/latest/meta-data/"
|
"https://169.254.169.254/latest/meta-data/"
|
||||||
@@ -1299,5 +1407,8 @@ mod tests {
|
|||||||
assert!(!PorndishProvider::is_allowed_myvidplay_pass_url(
|
assert!(!PorndishProvider::is_allowed_myvidplay_pass_url(
|
||||||
"https://example.com/pass_md5/abc/def"
|
"https://example.com/pass_md5/abc/def"
|
||||||
));
|
));
|
||||||
|
assert!(!PorndishProvider::is_allowed_vidara_iframe_url(
|
||||||
|
"https://example.com/e/abc123"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use ntex::web;
|
use ntex::web;
|
||||||
|
|
||||||
|
use crate::proxies::porndish::PorndishProxy;
|
||||||
use crate::proxies::spankbang::SpankbangProxy;
|
use crate::proxies::spankbang::SpankbangProxy;
|
||||||
use crate::{proxies::sxyprn::SxyprnProxy, util::requester::Requester};
|
use crate::{proxies::sxyprn::SxyprnProxy, util::requester::Requester};
|
||||||
|
|
||||||
@@ -7,6 +8,7 @@ pub mod hanimecdn;
|
|||||||
pub mod hqpornerthumb;
|
pub mod hqpornerthumb;
|
||||||
pub mod javtiful;
|
pub mod javtiful;
|
||||||
pub mod noodlemagazine;
|
pub mod noodlemagazine;
|
||||||
|
pub mod porndish;
|
||||||
pub mod porndishthumb;
|
pub mod porndishthumb;
|
||||||
pub mod spankbang;
|
pub mod spankbang;
|
||||||
pub mod sxyprn;
|
pub mod sxyprn;
|
||||||
@@ -15,6 +17,7 @@ pub mod sxyprn;
|
|||||||
pub enum AnyProxy {
|
pub enum AnyProxy {
|
||||||
Sxyprn(SxyprnProxy),
|
Sxyprn(SxyprnProxy),
|
||||||
Javtiful(javtiful::JavtifulProxy),
|
Javtiful(javtiful::JavtifulProxy),
|
||||||
|
Porndish(PorndishProxy),
|
||||||
Spankbang(SpankbangProxy),
|
Spankbang(SpankbangProxy),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,6 +30,7 @@ impl Proxy for AnyProxy {
|
|||||||
match self {
|
match self {
|
||||||
AnyProxy::Sxyprn(p) => p.get_video_url(url, requester).await,
|
AnyProxy::Sxyprn(p) => p.get_video_url(url, requester).await,
|
||||||
AnyProxy::Javtiful(p) => p.get_video_url(url, requester).await,
|
AnyProxy::Javtiful(p) => p.get_video_url(url, requester).await,
|
||||||
|
AnyProxy::Porndish(p) => p.get_video_url(url, requester).await,
|
||||||
AnyProxy::Spankbang(p) => p.get_video_url(url, requester).await,
|
AnyProxy::Spankbang(p) => p.get_video_url(url, requester).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
369
src/proxies/porndish.rs
Normal file
369
src/proxies/porndish.rs
Normal file
@@ -0,0 +1,369 @@
|
|||||||
|
use ntex::web;
|
||||||
|
use regex::Regex;
|
||||||
|
use std::process::Command;
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::util::requester::Requester;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PorndishProxy {}
|
||||||
|
|
||||||
|
impl PorndishProxy {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_detail_url(endpoint: &str) -> Option<String> {
|
||||||
|
let endpoint = endpoint.trim();
|
||||||
|
if endpoint.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint.starts_with("http://") || endpoint.starts_with("https://") {
|
||||||
|
Some(endpoint.to_string())
|
||||||
|
} else {
|
||||||
|
Some(format!("https://{}", endpoint.trim_start_matches('/')))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_url(url: &str) -> Option<Url> {
|
||||||
|
Url::parse(url).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_porndish_host(host: &str) -> bool {
|
||||||
|
matches!(host, "www.porndish.com" | "porndish.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_myvidplay_host(host: &str) -> bool {
|
||||||
|
matches!(host, "myvidplay.com" | "www.myvidplay.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_vidara_host(host: &str) -> bool {
|
||||||
|
matches!(host, "vidara.so" | "www.vidara.so")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_allowed_detail_url(url: &str) -> bool {
|
||||||
|
let Some(url) = Self::parse_url(url) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if url.scheme() != "https" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(host) = url.host_str() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
Self::is_porndish_host(host) && url.path().starts_with("/porn/")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_allowed_myvidplay_iframe_url(url: &str) -> bool {
|
||||||
|
let Some(url) = Self::parse_url(url) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if url.scheme() != "https" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(host) = url.host_str() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
Self::is_myvidplay_host(host) && url.path().starts_with("/e/")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_allowed_myvidplay_pass_url(url: &str) -> bool {
|
||||||
|
let Some(url) = Self::parse_url(url) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if url.scheme() != "https" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(host) = url.host_str() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
Self::is_myvidplay_host(host) && url.path().starts_with("/pass_md5/")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_allowed_vidara_iframe_url(url: &str) -> bool {
|
||||||
|
let Some(url) = Self::parse_url(url) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
if url.scheme() != "https" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let Some(host) = url.host_str() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
Self::is_vidara_host(host) && url.path().starts_with("/e/")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vidara_api_url(iframe_url: &str) -> Option<String> {
|
||||||
|
let url = Self::parse_url(iframe_url)?;
|
||||||
|
if !Self::is_allowed_vidara_iframe_url(iframe_url) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let filecode = url
|
||||||
|
.path_segments()?
|
||||||
|
.filter(|segment| !segment.is_empty())
|
||||||
|
.next_back()?
|
||||||
|
.to_string();
|
||||||
|
if filecode.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(format!("https://vidara.so/api/stream?filecode={filecode}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn regex(value: &str) -> Option<Regex> {
|
||||||
|
Regex::new(value).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn fetch_with_curl_cffi(url: &str, referer: Option<&str>) -> Option<String> {
|
||||||
|
let url = url.to_string();
|
||||||
|
let referer = referer.unwrap_or("").to_string();
|
||||||
|
|
||||||
|
let output = tokio::task::spawn_blocking(move || {
|
||||||
|
Command::new("python3")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(
|
||||||
|
r#"
|
||||||
|
import sys
|
||||||
|
from curl_cffi import requests
|
||||||
|
|
||||||
|
url = sys.argv[1]
|
||||||
|
referer = sys.argv[2] if len(sys.argv) > 2 else ""
|
||||||
|
headers = {}
|
||||||
|
if referer:
|
||||||
|
headers["Referer"] = referer
|
||||||
|
|
||||||
|
response = requests.get(
|
||||||
|
url,
|
||||||
|
impersonate="chrome",
|
||||||
|
timeout=30,
|
||||||
|
allow_redirects=True,
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
if response.status_code >= 400:
|
||||||
|
sys.exit(1)
|
||||||
|
sys.stdout.buffer.write(response.content)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.arg(url)
|
||||||
|
.arg(referer)
|
||||||
|
.output()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.ok()?
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(String::from_utf8_lossy(&output.stdout).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve_first_redirect(url: &str) -> Option<String> {
|
||||||
|
let url = url.to_string();
|
||||||
|
let output = tokio::task::spawn_blocking(move || {
|
||||||
|
Command::new("python3")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(
|
||||||
|
r#"
|
||||||
|
import sys
|
||||||
|
from curl_cffi import requests
|
||||||
|
|
||||||
|
url = sys.argv[1]
|
||||||
|
response = requests.get(
|
||||||
|
url,
|
||||||
|
impersonate="chrome",
|
||||||
|
timeout=30,
|
||||||
|
allow_redirects=False,
|
||||||
|
)
|
||||||
|
location = response.headers.get("location", "")
|
||||||
|
if location:
|
||||||
|
sys.stdout.write(location)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.arg(url)
|
||||||
|
.output()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.ok()?
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
let location = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||||
|
if location.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_iframe_fragments(html: &str) -> Vec<String> {
|
||||||
|
let Some(regex) =
|
||||||
|
Self::regex(r#"const\s+[A-Za-z0-9_]+Content\s*=\s*"((?:\\.|[^"\\])*)";"#)
|
||||||
|
else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fragments = Vec::new();
|
||||||
|
for captures in regex.captures_iter(html) {
|
||||||
|
let Some(value) = captures.get(1).map(|value| value.as_str()) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let encoded = format!("\"{value}\"");
|
||||||
|
let decoded = serde_json::from_str::<String>(&encoded).unwrap_or_default();
|
||||||
|
if decoded.contains("<iframe") {
|
||||||
|
fragments.push(decoded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fragments
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_embed_source(fragment: &str) -> Option<String> {
|
||||||
|
let regex = Self::regex(r#"(?is)<iframe[^>]+src="([^"]+)"[^>]*>"#)?;
|
||||||
|
regex
|
||||||
|
.captures(fragment)
|
||||||
|
.and_then(|captures| captures.get(1))
|
||||||
|
.map(|value| value.as_str().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve_myvidplay_stream(iframe_url: &str) -> Option<String> {
|
||||||
|
if !Self::is_allowed_myvidplay_iframe_url(iframe_url) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let html = Self::fetch_with_curl_cffi(iframe_url, Some("https://www.porndish.com/")).await?;
|
||||||
|
let pass_regex = Self::regex(r#"\$\.get\(\s*['"](/pass_md5/[^'"]+)['"]"#)?;
|
||||||
|
let path = pass_regex
|
||||||
|
.captures(&html)
|
||||||
|
.and_then(|captures| captures.get(1))
|
||||||
|
.map(|value| value.as_str().to_string())?;
|
||||||
|
|
||||||
|
let token = path.trim_end_matches('/').rsplit('/').next()?.to_string();
|
||||||
|
if token.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pass_url = if path.starts_with("http://") || path.starts_with("https://") {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
let base = Url::parse(iframe_url).ok()?;
|
||||||
|
base.join(&path).ok()?.to_string()
|
||||||
|
};
|
||||||
|
if !Self::is_allowed_myvidplay_pass_url(&pass_url) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = Self::fetch_with_curl_cffi(&pass_url, Some(iframe_url))
|
||||||
|
.await?
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
if base.is_empty() || base == "RELOAD" || !base.starts_with("http") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let chars = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let now = std::time::SystemTime::now()
|
||||||
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
|
.ok()?
|
||||||
|
.as_millis();
|
||||||
|
let suffix = (0..10)
|
||||||
|
.map(|index| {
|
||||||
|
let pos = ((now + (index as u128 * 17)) % chars.len() as u128) as usize;
|
||||||
|
chars[pos] as char
|
||||||
|
})
|
||||||
|
.collect::<String>();
|
||||||
|
|
||||||
|
let stream_url = format!("{base}{suffix}?token={token}&expiry={now}");
|
||||||
|
Some(
|
||||||
|
Self::resolve_first_redirect(&stream_url)
|
||||||
|
.await
|
||||||
|
.unwrap_or(stream_url),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn resolve_vidara_stream(iframe_url: &str) -> Option<String> {
|
||||||
|
let api_url = Self::vidara_api_url(iframe_url)?;
|
||||||
|
let response = Self::fetch_with_curl_cffi(&api_url, Some(iframe_url)).await?;
|
||||||
|
let json: serde_json::Value = serde_json::from_str(&response).ok()?;
|
||||||
|
let stream_url = json
|
||||||
|
.get("streaming_url")
|
||||||
|
.and_then(|value| value.as_str())?
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
if stream_url.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(stream_url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_video_url(
|
||||||
|
&self,
|
||||||
|
url: String,
|
||||||
|
_requester: web::types::State<Requester>,
|
||||||
|
) -> String {
|
||||||
|
let Some(detail_url) = Self::normalize_detail_url(&url) else {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
if !Self::is_allowed_detail_url(&detail_url) {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(html) =
|
||||||
|
Self::fetch_with_curl_cffi(&detail_url, Some("https://www.porndish.com/")).await
|
||||||
|
else {
|
||||||
|
return String::new();
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fallback_iframe: Option<String> = None;
|
||||||
|
|
||||||
|
for fragment in Self::extract_iframe_fragments(&html) {
|
||||||
|
let Some(iframe_url) = Self::parse_embed_source(&fragment) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let iframe_url =
|
||||||
|
if iframe_url.starts_with("http://") || iframe_url.starts_with("https://") {
|
||||||
|
iframe_url
|
||||||
|
} else if iframe_url.starts_with("//") {
|
||||||
|
format!("https:{iframe_url}")
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if Self::is_allowed_vidara_iframe_url(&iframe_url) {
|
||||||
|
if let Some(stream_url) = Self::resolve_vidara_stream(&iframe_url).await {
|
||||||
|
return stream_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fallback_iframe.is_none() && Self::is_allowed_myvidplay_iframe_url(&iframe_url) {
|
||||||
|
fallback_iframe = Some(iframe_url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(iframe_url) = fallback_iframe {
|
||||||
|
if let Some(stream_url) = Self::resolve_myvidplay_stream(&iframe_url).await {
|
||||||
|
return stream_url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::PorndishProxy;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allows_only_porndish_detail_urls() {
|
||||||
|
assert!(PorndishProxy::is_allowed_detail_url(
|
||||||
|
"https://www.porndish.com/porn/example/"
|
||||||
|
));
|
||||||
|
assert!(!PorndishProxy::is_allowed_detail_url(
|
||||||
|
"https://www.porndish.com/search/example/"
|
||||||
|
));
|
||||||
|
assert!(!PorndishProxy::is_allowed_detail_url(
|
||||||
|
"https://example.com/porn/example/"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use ntex::web::{self, HttpRequest};
|
use ntex::web::{self, HttpRequest};
|
||||||
|
|
||||||
use crate::proxies::javtiful::JavtifulProxy;
|
use crate::proxies::javtiful::JavtifulProxy;
|
||||||
|
use crate::proxies::porndish::PorndishProxy;
|
||||||
use crate::proxies::spankbang::SpankbangProxy;
|
use crate::proxies::spankbang::SpankbangProxy;
|
||||||
use crate::proxies::sxyprn::SxyprnProxy;
|
use crate::proxies::sxyprn::SxyprnProxy;
|
||||||
use crate::proxies::*;
|
use crate::proxies::*;
|
||||||
@@ -22,6 +23,11 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||||||
.route(web::post().to(proxy2redirect))
|
.route(web::post().to(proxy2redirect))
|
||||||
.route(web::get().to(proxy2redirect)),
|
.route(web::get().to(proxy2redirect)),
|
||||||
)
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/porndish/{endpoint}*")
|
||||||
|
.route(web::post().to(proxy2redirect))
|
||||||
|
.route(web::get().to(proxy2redirect)),
|
||||||
|
)
|
||||||
.service(
|
.service(
|
||||||
web::resource("/noodlemagazine/{endpoint}*")
|
web::resource("/noodlemagazine/{endpoint}*")
|
||||||
.route(web::post().to(crate::proxies::noodlemagazine::serve_media))
|
.route(web::post().to(crate::proxies::noodlemagazine::serve_media))
|
||||||
@@ -63,6 +69,7 @@ fn get_proxy(proxy: &str) -> Option<AnyProxy> {
|
|||||||
match proxy {
|
match proxy {
|
||||||
"sxyprn" => Some(AnyProxy::Sxyprn(SxyprnProxy::new())),
|
"sxyprn" => Some(AnyProxy::Sxyprn(SxyprnProxy::new())),
|
||||||
"javtiful" => Some(AnyProxy::Javtiful(JavtifulProxy::new())),
|
"javtiful" => Some(AnyProxy::Javtiful(JavtifulProxy::new())),
|
||||||
|
"porndish" => Some(AnyProxy::Porndish(PorndishProxy::new())),
|
||||||
"spankbang" => Some(AnyProxy::Spankbang(SpankbangProxy::new())),
|
"spankbang" => Some(AnyProxy::Spankbang(SpankbangProxy::new())),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user