melonstube fix

This commit is contained in:
Simon
2026-07-06 17:51:06 +00:00
parent 94cc3d7bbb
commit 3b591c1950
2 changed files with 54 additions and 24 deletions

View File

@@ -10,9 +10,9 @@ use async_trait::async_trait;
use base64::{Engine as _, engine::general_purpose::STANDARD};
use chrono::{Duration as ChronoDuration, Utc};
use error_chain::error_chain;
use percent_encoding::{NON_ALPHANUMERIC, utf8_percent_encode};
use regex::Regex;
use scraper::{Html, Selector};
use url::form_urlencoded::Serializer;
pub const CHANNEL_METADATA: crate::providers::ProviderChannelMetadata =
crate::providers::ProviderChannelMetadata {
@@ -118,14 +118,9 @@ impl MelonstubeProvider {
format!("{BASE_URL}/rating")
}
}
Target::Search { query, page } => {
let encoded = utf8_percent_encode(query, NON_ALPHANUMERIC).to_string();
if *page > 1 {
format!("{BASE_URL}/search?q={encoded}&page={page}")
} else {
format!("{BASE_URL}/search?q={encoded}")
}
}
Target::Search { .. } => unreachable!(
"Target::Search is resolved asynchronously via resolve_search_base_url"
),
}
}
@@ -386,13 +381,58 @@ impl MelonstubeProvider {
}
}
fn build_search_request_body(query: &str) -> String {
let mut serializer = Serializer::new(String::new());
serializer.append_pair("search_query[query]", query);
serializer.finish()
}
async fn resolve_search_base_url(query: &str, options: &ServerOptions) -> Result<String> {
let search_url = format!("{BASE_URL}/searching/by-form");
let body = Self::build_search_request_body(query);
let mut requester = requester_or_default(options, CHANNEL_ID, "resolve_search_base_url");
let response = requester
.post(
&search_url,
&body,
vec![
("Content-Type", "application/x-www-form-urlencoded"),
("Referer", BASE_URL),
("User-Agent", BROWSER_UA),
],
)
.await
.map_err(|e| Error::from(format!("search submit failed url={search_url}: {e}")))?;
Ok(response.url().to_string().trim_end_matches('/').to_string())
}
async fn fetch_and_parse(
&self,
cache: VideoCache,
target: Target,
options: ServerOptions,
) -> Result<Vec<VideoItem>> {
let url = Self::build_url(&target);
let url = match &target {
Target::Search { query, page } => {
let base = match Self::resolve_search_base_url(query, &options).await {
Ok(base) => base,
Err(e) => {
report_provider_error(CHANNEL_ID, "fetch_and_parse.search", &e.to_string())
.await;
return Ok(vec![]);
}
};
if *page > 1 {
format!("{base}?page={page}")
} else {
base
}
}
_ => Self::build_url(&target),
};
if let Some((time, items)) = cache.get(&url) {
if time.elapsed().unwrap_or_default().as_secs() < 300 {
@@ -513,20 +553,10 @@ mod tests {
}
#[test]
fn builds_search_urls() {
fn builds_search_request_body() {
assert_eq!(
MelonstubeProvider::build_url(&super::Target::Search {
query: "teen".to_string(),
page: 1
}),
"https://www.melonstube.com/search?q=teen"
);
assert_eq!(
MelonstubeProvider::build_url(&super::Target::Search {
query: "teen".to_string(),
page: 2
}),
"https://www.melonstube.com/search?q=teen&page=2"
MelonstubeProvider::build_search_request_body("adriana chechik"),
"search_query%5Bquery%5D=adriana+chechik"
);
}