Fix hornyleak/hentaimama providers returning wrong media
hornyleak: yt-dlp's generic extractor on the detail page picked up the
site's own 728x90 banner-ad <video> tag instead of following the real
/embed/{id}/ iframe, so every item streamed an ad. Now resolve the
actual HLS master playlist via the existing embed -> hdplayer.gives
chain and ship it as an explicit format.
hentaimama: search/genre/studio results are series cards, and
resolving them only followed the *last* episode link, so a 4-episode
series only ever surfaced 1 item. Now expands every series match into
all of its episodes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017haiuheHbREQdnh3B5rqgc
This commit is contained in:
@@ -344,13 +344,13 @@ impl HentaimamaProvider {
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn last_episode_url(html: &str) -> Result<Option<String>> {
|
||||
fn episode_urls(html: &str) -> Result<Vec<String>> {
|
||||
let document = Html::parse_document(html);
|
||||
let selector = Self::selector("a.dt-se-item")?;
|
||||
Ok(document
|
||||
.select(&selector)
|
||||
.filter_map(|e: ElementRef| e.value().attr("href").map(|s| s.to_string()))
|
||||
.last())
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn post_id_regex() -> &'static Regex {
|
||||
@@ -484,13 +484,25 @@ impl HentaimamaProvider {
|
||||
Some(item)
|
||||
}
|
||||
|
||||
/// Search/genre/studio archives only expose series (show) cards, so a match
|
||||
/// is represented by its most recent episode - the same shape the latest
|
||||
/// feed already returns, just reached through one extra hop.
|
||||
async fn resolve_series_to_item(requester: &mut Requester, series_url: &str) -> Option<VideoItem> {
|
||||
let html = Self::fetch_html(requester, series_url, BASE_URL).await.ok()?;
|
||||
let episode_url = Self::last_episode_url(&html).ok().flatten()?;
|
||||
Self::resolve_episode_url(requester, &episode_url).await
|
||||
/// Search/genre/studio archives only expose series (show) cards, so each
|
||||
/// match is expanded into every episode of that series - the same shape
|
||||
/// the latest feed already returns per-episode, just reached through one
|
||||
/// extra hop.
|
||||
async fn resolve_series_to_items(requester: &mut Requester, series_url: &str) -> Vec<VideoItem> {
|
||||
let Ok(html) = Self::fetch_html(requester, series_url, BASE_URL).await else {
|
||||
return vec![];
|
||||
};
|
||||
let Ok(episode_urls) = Self::episode_urls(&html) else {
|
||||
return vec![];
|
||||
};
|
||||
stream::iter(episode_urls.into_iter().map(|episode_url| {
|
||||
let mut req = requester.clone();
|
||||
async move { Self::resolve_episode_url(&mut req, &episode_url).await }
|
||||
}))
|
||||
.buffer_unordered(4)
|
||||
.filter_map(|item| async move { item })
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
}
|
||||
|
||||
async fn resolve_episode_url(requester: &mut Requester, episode_url: &str) -> Option<VideoItem> {
|
||||
@@ -574,10 +586,10 @@ impl HentaimamaProvider {
|
||||
let urls = Self::parse_series_card_urls(&html)?;
|
||||
stream::iter(urls.into_iter().take(per_page.max(1)).map(|url| {
|
||||
let mut req = requester.clone();
|
||||
async move { Self::resolve_series_to_item(&mut req, &url).await }
|
||||
async move { Self::resolve_series_to_items(&mut req, &url).await }
|
||||
}))
|
||||
.buffer_unordered(4)
|
||||
.filter_map(|item| async move { item })
|
||||
.flat_map(stream::iter)
|
||||
.collect::<Vec<_>>()
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -663,9 +663,16 @@ impl HornyleakProvider {
|
||||
listing_referer: String,
|
||||
mut requester: Requester,
|
||||
) -> VideoItem {
|
||||
// The page URL is yt-dlp-resolvable, so enrichment is only used for tags
|
||||
// and uploader fields. The HLS embed chain (kept as a private helper
|
||||
// for future use) is intentionally not invoked here.
|
||||
// yt-dlp's generic extractor on the page URL grabs the site's own
|
||||
// `728x90-1.mp4` banner-ad `<video>` tag instead of drilling into the
|
||||
// `/embed/{id}/` iframe, so the page URL alone is NOT reliably
|
||||
// yt-dlp-resolvable to the real content. Resolve the actual HLS
|
||||
// master playlist via the embed -> hdplayer chain and ship it as an
|
||||
// explicit format; only fall back to the bare page URL if that fails.
|
||||
let hls_url = self
|
||||
.resolve_hls_url(&card.id, &card.page_url, requester.clone())
|
||||
.await;
|
||||
|
||||
let detail = requester
|
||||
.get_with_headers(
|
||||
&card.page_url,
|
||||
@@ -688,6 +695,13 @@ impl HornyleakProvider {
|
||||
card.thumb,
|
||||
card.duration,
|
||||
);
|
||||
if let Some(hls_url) = hls_url {
|
||||
item.formats = Some(vec![crate::videos::VideoFormat::m3u8(
|
||||
hls_url,
|
||||
"auto".to_string(),
|
||||
"m3u8".to_string(),
|
||||
)]);
|
||||
}
|
||||
if let Some(views) = card.views {
|
||||
item = item.views(views);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user