noodlemagazine proxy implementation

This commit is contained in:
Simon
2026-03-10 18:34:06 +00:00
parent efb1eb3c91
commit 2ad131f38f
4 changed files with 186 additions and 61 deletions

View File

@@ -1,11 +1,13 @@
use ntex::web;
use crate::proxies::noodlemagazine::NoodlemagazineProxy;
use crate::proxies::spankbang::SpankbangProxy;
use crate::{proxies::sxyprn::SxyprnProxy, util::requester::Requester};
pub mod hanimecdn;
pub mod hqpornerthumb;
pub mod javtiful;
pub mod noodlemagazine;
pub mod spankbang;
pub mod sxyprn;
@@ -14,6 +16,7 @@ pub enum AnyProxy {
Sxyprn(SxyprnProxy),
Javtiful(javtiful::JavtifulProxy),
Spankbang(SpankbangProxy),
Noodlemagazine(NoodlemagazineProxy),
}
pub trait Proxy {
@@ -26,6 +29,7 @@ impl Proxy for AnyProxy {
AnyProxy::Sxyprn(p) => p.get_video_url(url, requester).await,
AnyProxy::Javtiful(p) => p.get_video_url(url, requester).await,
AnyProxy::Spankbang(p) => p.get_video_url(url, requester).await,
AnyProxy::Noodlemagazine(p) => p.get_video_url(url, requester).await,
}
}
}

View File

@@ -0,0 +1,110 @@
use ntex::web;
use serde_json::Value;
use wreq::Version;
use crate::util::requester::Requester;
#[derive(Debug, Clone)]
pub struct NoodlemagazineProxy {}
impl NoodlemagazineProxy {
pub fn new() -> Self {
NoodlemagazineProxy {}
}
fn extract_playlist(text: &str) -> Option<&str> {
text.split("window.playlist = ").nth(1)?.split(';').next()
}
fn source_score(source: &Value) -> (u8, u32) {
let file = source["file"].as_str().unwrap_or_default();
let label = source["label"].as_str().unwrap_or_default();
let is_hls = u8::from(file.contains(".m3u8"));
let quality = label
.chars()
.filter(|c| c.is_ascii_digit())
.collect::<String>()
.parse::<u32>()
.unwrap_or(0);
(is_hls, quality)
}
fn select_best_source(playlist: &str) -> Option<String> {
let json: Value = serde_json::from_str(playlist).ok()?;
let sources = json["sources"].as_array()?;
sources
.iter()
.filter(|source| {
source["file"]
.as_str()
.map(|file| !file.is_empty())
.unwrap_or(false)
})
.max_by_key(|source| Self::source_score(source))
.and_then(|source| source["file"].as_str())
.map(str::to_string)
}
pub async fn get_video_url(
&self,
url: String,
requester: web::types::State<Requester>,
) -> String {
let mut requester = requester.get_ref().clone();
let url = if url.starts_with("http://") || url.starts_with("https://") {
url
} else {
format!("https://{}", url.trim_start_matches('/'))
};
let text = requester
.get(&url, Some(Version::HTTP_2))
.await
.unwrap_or_default();
if text.is_empty() {
return String::new();
}
let Some(playlist) = Self::extract_playlist(&text) else {
return String::new();
};
Self::select_best_source(playlist).unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::NoodlemagazineProxy;
#[test]
fn extracts_playlist_from_page() {
let html = r#"
<script>
window.playlist = {"sources":[{"file":"https://cdn.example/360.mp4","label":"360p"}]};
</script>
"#;
assert_eq!(
NoodlemagazineProxy::extract_playlist(html),
Some(r#"{"sources":[{"file":"https://cdn.example/360.mp4","label":"360p"}]}"#)
);
}
#[test]
fn prefers_hls_then_highest_quality() {
let playlist = r#"{
"sources": [
{"file":"https://cdn.example/360.mp4","label":"360p"},
{"file":"https://cdn.example/720.mp4","label":"720p"},
{"file":"https://cdn.example/master.m3u8","label":"1080p"}
]
}"#;
assert_eq!(
NoodlemagazineProxy::select_best_source(playlist).as_deref(),
Some("https://cdn.example/master.m3u8")
);
}
}