Fix pornmz: match contentUrl meta case-insensitively, bound enrichment

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
This commit is contained in:
Simon
2026-10-10 07:43:48 +00:00
parent aff5df31d6
commit 5098d142f9
2 changed files with 34 additions and 4 deletions

View File

@@ -22,7 +22,7 @@ Status: `[ ]` open, `[~]` in progress, `[x]` fixed and verified, `[-]` not repro
| | Channel | Symptom | Notes |
|---|---|---|---|
| [x] | `thaiporntv` | page URL "Unsupported URL", no `formats` | player attribute renamed `data-enc` -> `data-video-sources` (reversed base64 JSON `{quality,url}`); decoder handles both. 3 formats/item, yt-dlp OK, check.py passes |
| [ ] | `pornmz` | page URL "Unsupported URL", no `formats` | populate formats from detail page |
| [x] | `pornmz` | page URL "Unsupported URL", no `formats` | site changed `itemprop="contentURL"` -> `contentUrl`; selector now case-insensitive. Enrichment concurrency 10 + 25s per-item timeout. Origin is slow (15s+/detail) so first feed 25-50s. check.py passes |
| [ ] | `coomer` | page URL "Unsupported URL"; media host CF-gated here | check whether `formats` are populated for videos |
| [ ] | `tube8` | page URL 404; `/proxy/tube8/{id}` 502 | |
| [ ] | `hypnotube` | page URLs 404 on all sampled items | URL construction |

View File

@@ -835,7 +835,8 @@ impl PornmzProvider {
}
fn meta_itemprop(document: &Html, itemprop: &str) -> Option<String> {
let selector = Self::selector(&format!("meta[itemprop=\"{itemprop}\"]")).ok()?;
// The site mixes `contentURL` / `contentUrl`; match the value case-insensitively.
let selector = Self::selector(&format!("meta[itemprop=\"{itemprop}\" i]")).ok()?;
document
.select(&selector)
.next()
@@ -1016,9 +1017,21 @@ impl PornmzProvider {
let enriched = stream::iter(items.into_iter().map(|item| {
let provider = self.clone();
let options = options.clone();
async move { provider.enrich_item(item, &options).await }
async move {
// Detail pages are slow (~15s); don't let one hung page stall the list.
let fallback = item.clone();
match tokio::time::timeout(
std::time::Duration::from_secs(25),
provider.enrich_item(item, &options),
)
.await
{
Ok(enriched) => enriched,
Err(_) => fallback,
}
}
}))
.buffer_unordered(6)
.buffer_unordered(10)
.collect::<Vec<_>>()
.await;
@@ -1239,6 +1252,23 @@ mod tests {
assert!(item.uploadedAt.is_some());
}
#[test]
fn reads_lowercase_content_url_meta() {
let provider = PornmzProvider::new();
let item = VideoItem::new(
"pm1".to_string(),
"T".to_string(),
"https://pornmz.com/video/id=pm1".to_string(),
CHANNEL_ID.to_string(),
String::new(),
0,
);
let html = r#"<div class="entry-content"><meta itemprop="contentUrl" content="https://video.twimg.com/x/pl/a.m3u8" /></div>"#;
let item = provider.apply_detail_video(item, html).expect("detail");
assert_eq!(item.url, "https://video.twimg.com/x/pl/a.m3u8");
assert_eq!(item.formats.as_ref().map(|f| f.len()), Some(1));
}
#[test]
fn derives_mp4_media_extension() {
assert_eq!(