From eef08401ab37ea0aee98837a8a3a0282387af1e9 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 13 May 2026 19:48:35 +0000 Subject: [PATCH] clapdat image fix --- src/providers/clapdat.rs | 87 ++++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/src/providers/clapdat.rs b/src/providers/clapdat.rs index cc03446..29f573e 100644 --- a/src/providers/clapdat.rs +++ b/src/providers/clapdat.rs @@ -88,7 +88,8 @@ impl ClapdatProvider { Channel { id: CHANNEL_ID.to_string(), name: "ClapDat".to_string(), - description: "ClapDat trending/recent feeds with tag and uploader shortcuts.".to_string(), + description: "ClapDat trending/recent feeds with tag and uploader shortcuts." + .to_string(), premium: false, favicon: "https://www.google.com/s2/favicons?sz=64&domain=clapdat.com".to_string(), status: "active".to_string(), @@ -221,7 +222,12 @@ impl ClapdatProvider { .unwrap_or(0) } - fn parse_card(&self, card: &ElementRef<'_>, link_sel: &Selector, img_sel: &Selector) -> Option { + fn parse_card( + &self, + card: &ElementRef<'_>, + link_sel: &Selector, + img_sel: &Selector, + ) -> Option { let link = card.select(link_sel).next()?; let href = link.value().attr("href")?; let url = self.normalize_url(href); @@ -240,14 +246,24 @@ impl ClapdatProvider { .select(&Self::selector("span").ok()?) .filter_map(|node| { let value = Self::normalize_text(&node.text().collect::>().join(" ")); - if value.contains(':') { Some(value) } else { None } + if value.contains(':') { + Some(value) + } else { + None + } }) .next() .unwrap_or_default(); let thumb = card .select(img_sel) - .find_map(|img| img.value().attr("src").or_else(|| img.value().attr("data-src"))) + .filter_map(|img| { + // Collect the attributes we are looking for + img.value() + .attr("src") + .or_else(|| img.value().attr("data-src")) + }) + .nth(1) // Skips the first match (index 0) and takes the second match (index 1) .map(|value| self.normalize_url(value)) .unwrap_or_default(); @@ -327,10 +343,9 @@ impl ClapdatProvider { ) -> Result> { let mut requester = requester_or_default(options, CHANNEL_ID, "search_videos"); let offset = page.saturating_sub(1) as usize * per_page; - let query_encoded = url::form_urlencoded::byte_serialize(query.as_bytes()).collect::(); - let search_url = format!( - "{SEARCH_URL}?q={query_encoded}&limit={per_page}&offset={offset}" - ); + let query_encoded = + url::form_urlencoded::byte_serialize(query.as_bytes()).collect::(); + let search_url = format!("{SEARCH_URL}?q={query_encoded}&limit={per_page}&offset={offset}"); let auth_header = format!("Bearer {SEARCH_KEY}"); let headers = vec![ ("accept".to_string(), "application/json".to_string()), @@ -376,15 +391,18 @@ impl ClapdatProvider { Option, Option, ) { - let uploader_name = Self::regex(r#"]*>[^<]*]*>\s*]*>([^<]+)

"#) - .ok() - .and_then(|re| re.captures(html)) - .and_then(|caps| { - let slug = caps.get(1)?.as_str().to_string(); - let name = Self::normalize_text(caps.get(2)?.as_str()); - if name.is_empty() { return None; } - Some((name, slug)) - }); + let uploader_name = + Self::regex(r#"
]*>[^<]*]*>\s*]*>([^<]+)

"#) + .ok() + .and_then(|re| re.captures(html)) + .and_then(|caps| { + let slug = caps.get(1)?.as_str().to_string(); + let name = Self::normalize_text(caps.get(2)?.as_str()); + if name.is_empty() { + return None; + } + Some((name, slug)) + }); let uploader = uploader_name.as_ref().map(|v| v.0.clone()); let uploader_url = uploader_name @@ -394,13 +412,14 @@ impl ClapdatProvider { .as_ref() .map(|v| format!("{CHANNEL_ID}:{}", v.1)); - let uploaded_at = Self::regex(r#"

([A-Za-z]{3}\s+\d{1,2},\s+\d{4})

"#) - .ok() - .and_then(|re| re.captures(html)) - .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) - .and_then(|value| NaiveDate::parse_from_str(&value, "%b %e, %Y").ok()) - .and_then(|date| date.and_hms_opt(0, 0, 0)) - .and_then(|dt| u64::try_from(dt.and_utc().timestamp()).ok()); + let uploaded_at = + Self::regex(r#"

([A-Za-z]{3}\s+\d{1,2},\s+\d{4})

"#) + .ok() + .and_then(|re| re.captures(html)) + .and_then(|caps| caps.get(1).map(|m| m.as_str().to_string())) + .and_then(|value| NaiveDate::parse_from_str(&value, "%b %e, %Y").ok()) + .and_then(|date| date.and_hms_opt(0, 0, 0)) + .and_then(|dt| u64::try_from(dt.and_utc().timestamp()).ok()); let tag_re = Self::regex(r#"
]*>([^<]+)"#).ok(); let tags = tag_re @@ -474,7 +493,10 @@ impl Provider for ClapdatProvider { let target = self.resolve_target(&query_value, &sort_value); let stubs = match &target { - Target::Search { query } => match self.search_videos(&options, query, page_num, per_page_num).await { + Target::Search { query } => match self + .search_videos(&options, query, page_num, per_page_num) + .await + { Ok(items) => items, Err(error) => { report_provider_error(CHANNEL_ID, "search_videos", &error.to_string()).await; @@ -497,13 +519,18 @@ impl Provider for ClapdatProvider { _ => self.parse_listing_html(&html), }; match parsed { - Ok(items) => items, - Err(error) => { - report_provider_error(CHANNEL_ID, "parse_listing_html", &error.to_string()).await; - vec![] + Ok(items) => items, + Err(error) => { + report_provider_error( + CHANNEL_ID, + "parse_listing_html", + &error.to_string(), + ) + .await; + vec![] + } } } - } Err(error) => { report_provider_error(CHANNEL_ID, "fetch_html", &error.to_string()).await; vec![]