From 2c456dff59503a7a6e3347968458e852b9561f58 Mon Sep 17 00:00:00 2001 From: Simon Date: Wed, 23 Sep 2026 07:42:21 +0000 Subject: [PATCH] Fix coomer: bogus video formats on image posts, and its CDN reachability in check.py - coomer.rs: only emit a `formats` entry for actual video extensions. Image-only posts (very common - many creators post photos, not just videos) were getting a `formats[0]` pointing at a static jpg/png, which a player would try and fail to stream as video; the preview URL already covers showing the image. - coomer.rs tests: fix stale fixtures that baked a `/data/` prefix into `file.path`, which doesn't match the real API's path shape (the code prepends `/data/` itself) and was doubling it into `/data/data/...`. This was masking the formats bug above until the fixture was corrected. - check.py: coomer.st/data/... 302s to a load-balanced nN.coomer.st CDN node; every node tested (n1/n2/n3) is a hard connect-timeout from this sandbox's egress while coomer.st itself is a normal 200 - the same IP-reputation-style CDN block already documented for ripnsfw's doodstream/lulustream edges. Added `.coomer.st` to the CF-protected suffix allowlist, and made the url/thumb checks follow redirects before testing it (previously only the format-url check did). Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01QTqf6orbHZ9rFFpVpcgzcR --- check.py | 12 ++++++++++-- src/providers/coomer.rs | 28 +++++++++++++++++----------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/check.py b/check.py index 3414de8..2b4fdbc 100644 --- a/check.py +++ b/check.py @@ -127,6 +127,12 @@ _CF_PROTECTED_SUFFIXES = ( # lulustream's signed HLS edge (reached via ripnsfw's /proxy/lulustream/...). # Returns 403/522 to datacenter IPs regardless of UA/TLS impersonation. ".tnmr.org", + # coomer's file CDN: coomer.st/data/... 302s to a load-balanced nN.coomer.st + # edge node (n1, n2, n3, n4, ...); every node is a hard connect-timeout from + # this sandbox's egress (verified: n1/n2/n3 all time out while coomer.st + # itself is a normal 200) — an IP-reputation-style CDN block, not a code + # defect. The leading dot means bare `coomer.st` itself is unaffected. + ".coomer.st", ) @@ -325,7 +331,8 @@ def check_video(video: dict, channel_id: str, results: Results, run_ytdlp: bool) else: ok, code = http_ok(vurl) if not ok: - if _is_cf_protected(vurl): + resolved_vurl = follow_proxy_redirect(vurl) + if _is_cf_protected(vurl) or _is_cf_protected(resolved_vurl): results.warn(channel_id, f"{label}: url unreachable HTTP={code} (CF-protected host, expected): {vurl}") else: results.err(channel_id, f"{label}: url unreachable HTTP={code}: {vurl}") @@ -336,7 +343,8 @@ def check_video(video: dict, channel_id: str, results: Results, run_ytdlp: bool) if thumb: ok, code = http_ok(thumb) if not ok: - if _is_cf_protected(thumb): + resolved_thumb = follow_proxy_redirect(thumb) + if _is_cf_protected(thumb) or _is_cf_protected(resolved_thumb): results.warn(channel_id, f"{label}: thumb unreachable HTTP={code} (CF-protected host, expected): {thumb}") else: results.err(channel_id, f"{label}: thumb unreachable HTTP={code}: {thumb}") diff --git a/src/providers/coomer.rs b/src/providers/coomer.rs index 2651750..4941f06 100644 --- a/src/providers/coomer.rs +++ b/src/providers/coomer.rs @@ -462,18 +462,24 @@ impl CoomerProvider { if let Some(ref url) = format_url { let ext = ext_hint.unwrap_or_else(|| "mp4".to_string()); let is_video = matches!(ext.as_str(), "mp4" | "webm" | "mov" | "mkv"); - let mut format = VideoFormat::new(url.clone(), "original".to_string(), ext.clone()); - // The /data/... path 302s to the n*.coomer.st CDN; that CDN will - // serve the bytes without a Referer requirement, but the *initial* - // redirect to the CDN does require a valid coomer.st session cookie - // and Referer. Set the Referer so the 302 actually returns the - // Location header. - format = format.http_header("Referer".to_string(), REFERER.to_string()); - format = format.http_header("User-Agent".to_string(), BROWSER_UA.to_string()); + // An image-only post (very common - many creators post photos, + // not just videos) has nothing playable; emitting a "format" + // pointing at a static jpg/png just gives players a broken + // pseudo-video to try to stream. The preview URL already covers + // showing the image. if is_video { + let mut format = + VideoFormat::new(url.clone(), "original".to_string(), ext.clone()); + // The /data/... path 302s to the n*.coomer.st CDN; that CDN will + // serve the bytes without a Referer requirement, but the *initial* + // redirect to the CDN does require a valid coomer.st session cookie + // and Referer. Set the Referer so the 302 actually returns the + // Location header. + format = format.http_header("Referer".to_string(), REFERER.to_string()); + format = format.http_header("User-Agent".to_string(), BROWSER_UA.to_string()); format = format.video_ext(ext); + item.formats = Some(vec![format]); } - item.formats = Some(vec![format]); } // Drop the auto-generated preview URL if it duplicates the format URL. @@ -793,7 +799,7 @@ mod tests { "service": "onlyfans", "title": "First post", "published": "2025-01-02T03:04:05Z", - "file": {"name": "clip.mp4", "path": "/data/abc/clip.mp4"}, + "file": {"name": "clip.mp4", "path": "/abc/clip.mp4"}, "attachments": [] }"#; let post: ApiPost = serde_json::from_str(json).expect("parse"); @@ -826,7 +832,7 @@ mod tests { "service": "fansly", "title": "", "substring": "

Hello there friends

", - "file": {"name": "p.jpg", "path": "/data/x/p.jpg"} + "file": {"name": "p.jpg", "path": "/x/p.jpg"} }"#; let post: ApiPost = serde_json::from_str(json).expect("parse"); let item = CoomerProvider::build_video_item(post).expect("item");