From 2287f368810460b7e22e910f474c8b838e69c5c3 Mon Sep 17 00:00:00 2001
From: Harold <8866033@gmail.com>
Date: Sun, 19 Apr 2026 20:33:01 +0800
Subject: [PATCH] fix: restore cupfox category listing
---
py/tests/test_茶杯狐.py | 73 +++++++++++++++++++++++++++++++++++++++++
py/茶杯狐.py | 35 +++++++++++++++-----
2 files changed, 100 insertions(+), 8 deletions(-)
diff --git a/py/tests/test_茶杯狐.py b/py/tests/test_茶杯狐.py
index 92ff381..3b5d483 100644
--- a/py/tests/test_茶杯狐.py
+++ b/py/tests/test_茶杯狐.py
@@ -33,6 +33,14 @@ class TestCupfoxSpider(unittest.TestCase):
self.spider._decode_play_id("play/abc123"),
"https://www.cupfox.ai/play/abc123.html",
)
+ self.assertEqual(
+ self.spider._encode_detail_id("/video/119983.html"),
+ "detail/video/119983",
+ )
+ self.assertEqual(
+ self.spider._decode_detail_id("detail/video/119983"),
+ "https://www.cupfox.ai/video/119983.html",
+ )
def test_merge_set_cookie_and_cookie_header(self):
jar = {}
@@ -83,6 +91,32 @@ class TestCupfoxSpider(unittest.TestCase):
self.assertEqual(len(calls), 3)
self.assertEqual(calls[2]["headers"]["Cookie"], "session=abc; shield=passed")
+ def test_request_with_firewall_accepts_capitalized_set_cookie_header(self):
+ calls = []
+
+ def fake_request(url, method="GET", body=None, headers=None):
+ calls.append({"url": url, "method": method, "body": body, "headers": headers or {}})
+ if len(calls) == 1:
+ return {
+ "status_code": 200,
+ "text": '
',
+ "headers": {"Set-Cookie": "PHPSESSID=abc123; path=/"},
+ }
+ if "robot.php" in url:
+ self.assertEqual(headers["Cookie"], "PHPSESSID=abc123")
+ return {"status_code": 200, "text": '{"msg":"ok"}', "headers": {}}
+ return {
+ "status_code": 200,
+ "text": "ok",
+ "headers": {},
+ }
+
+ self.spider._request_text = fake_request
+ html = self.spider._request_with_firewall("https://www.cupfox.ai/type/1-2.html")
+
+ self.assertEqual(html, "ok")
+ self.assertEqual(calls[2]["headers"]["Cookie"], "PHPSESSID=abc123")
+
def test_extract_player_data_reads_embedded_json(self):
html = ''
data = self.spider._extract_player_data(html)
@@ -159,6 +193,45 @@ class TestCupfoxSpider(unittest.TestCase):
self.assertEqual(search["list"][0]["vod_remarks"], "搜索备注")
self.assertNotIn("pagecount", search)
+ def test_category_content_uses_plain_type_path_for_first_page(self):
+ seen = []
+
+ def fake_request(url):
+ seen.append(url)
+ return """
+
+
+
![]()
+
+ """
+
+ self.spider._request_with_firewall = fake_request
+ self.spider.categoryContent("1", "1", False, {})
+
+ self.assertEqual(seen, ["https://www.cupfox.ai/type/1.html"])
+
+ def test_parse_cards_supports_video_detail_links(self):
+ html = """
+
+ """
+ items = self.spider._parse_cards(html)
+
+ self.assertEqual(
+ items,
+ [
+ {
+ "vod_id": "detail/video/119983",
+ "vod_name": "挽救计划",
+ "vod_pic": "https://www.cupfox.ai/poster.jpg",
+ "vod_remarks": "正片",
+ }
+ ],
+ )
+
def test_detail_content_builds_play_sources(self):
html = """
详情标题
diff --git a/py/茶杯狐.py b/py/茶杯狐.py
index 4545a23..af8d5b4 100644
--- a/py/茶杯狐.py
+++ b/py/茶杯狐.py
@@ -39,12 +39,21 @@ class Spider(BaseSpider):
return urljoin(self.host + "/", str(path or "").strip())
def _encode_detail_id(self, href):
- matched = re.search(r"/movie/([^/?#]+)\.html", self._build_url(href))
- return f"detail/{matched.group(1)}" if matched else ""
+ movie_match = re.search(r"/movie/([^/?#]+)\.html", self._build_url(href))
+ if movie_match:
+ return f"detail/{movie_match.group(1)}"
+ video_match = re.search(r"/video/([^/?#]+)\.html", self._build_url(href))
+ if video_match:
+ return f"detail/video/{video_match.group(1)}"
+ return ""
def _decode_detail_id(self, vod_id):
- matched = re.search(r"^detail/([^/?#]+)$", str(vod_id or "").strip())
- return self._build_url(f"/movie/{matched.group(1)}.html") if matched else ""
+ raw = str(vod_id or "").strip()
+ video_match = re.search(r"^detail/video/([^/?#]+)$", raw)
+ if video_match:
+ return self._build_url(f"/video/{video_match.group(1)}.html")
+ movie_match = re.search(r"^detail/(?:movie/)?([^/?#]+)$", raw)
+ return self._build_url(f"/movie/{movie_match.group(1)}.html") if movie_match else ""
def _encode_play_id(self, href):
matched = re.search(r"/play/([^/?#]+)\.html", self._build_url(href))
@@ -67,6 +76,15 @@ class Spider(BaseSpider):
def _cookie_header(self, cookie_jar):
return "; ".join([f"{key}={value}" for key, value in cookie_jar.items()])
+ def _header_value(self, headers, name, default=None):
+ if not isinstance(headers, dict):
+ return default
+ lowered = str(name or "").lower()
+ for key, value in headers.items():
+ if str(key).lower() == lowered:
+ return value
+ return default
+
def _extract_firewall_token(self, html_text):
matched = re.search(r'var\s+token\s*=\s*encrypt\("([^"]+)"\)', str(html_text or ""))
return matched.group(1) if matched else ""
@@ -94,13 +112,13 @@ class Spider(BaseSpider):
return {
"status_code": response.status_code,
"text": response.text or "",
- "headers": dict(response.headers or {}),
+ "headers": {str(key).lower(): value for key, value in dict(response.headers or {}).items()},
}
def _request_with_firewall(self, url):
cookie_jar = {}
first = self._request_text(url)
- self._merge_set_cookie(cookie_jar, first["headers"].get("set-cookie", []))
+ self._merge_set_cookie(cookie_jar, self._header_value(first["headers"], "set-cookie", []))
if not re.search(r"人机验证|verifyBox", first["text"] or ""):
if int(first["status_code"] or 0) != 200:
raise ValueError(f"HTTP {first['status_code']} @ {url}")
@@ -130,7 +148,7 @@ class Spider(BaseSpider):
body=verify_body,
headers=verify_headers,
)
- self._merge_set_cookie(cookie_jar, verify["headers"].get("set-cookie", []))
+ self._merge_set_cookie(cookie_jar, self._header_value(verify["headers"], "set-cookie", []))
second_headers = {}
solved_cookie = self._cookie_header(cookie_jar)
@@ -256,7 +274,8 @@ class Spider(BaseSpider):
def categoryContent(self, tid, pg, filter, extend):
page = int(pg)
- html = self._request_with_firewall(self._build_url(f"/type/{tid}-{page}.html"))
+ path = f"/type/{tid}.html" if page == 1 else f"/type/{tid}-{page}.html"
+ html = self._request_with_firewall(self._build_url(path))
items = self._parse_cards(html)
return {"page": page, "limit": self.page_limit, "total": page * len(items), "list": items}