v0.5.114: add Douyu streamers with fan enrichment and live probe.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
voson
2026-07-30 02:42:47 +08:00
co-authored by Cursor
parent fdd926e9bb
commit 38f46ad2ea
17 changed files with 477 additions and 38 deletions
+65
View File
@@ -17,6 +17,12 @@ sys.path.insert(0, str(ROOT / "web"))
from web import notify_site_traffic, refresh_web
from web.fetch_stratz_meta import preserve_failed_meta_brackets
from web.fetch_streamer_live import probe_streamers
from web.fetch_streamers import (
DOUYU_AUTHOR_HASH_RE,
extract_douyu_up_id_from_room_html,
extract_profile_from_douyu_data,
parse_douyu_dollar_data,
)
from web.refresh_cache import restore_patches, restore_streamers
from shared.http_utils import write_json_atomic
from shared.paths import HEROES_JSON, RELATIONS_JSON, WEB_FRONTEND
@@ -260,6 +266,65 @@ class LiveProbeFallbackTests(unittest.TestCase):
self.assertFalse(row["is_live"])
self.assertNotIn("live_probed_at", row)
def test_douyu_probe_path_writes_live_flag(self) -> None:
payload = {
"streamers": [
{
"id": "gouhuang",
"platform": "douyu",
"live_url": "https://www.douyu.com/957090",
}
]
}
with patch("web.fetch_streamer_live.probe_douyu", return_value=True):
live, offline, fail = probe_streamers(payload)
self.assertEqual((live, offline, fail), (1, 0, 0))
self.assertTrue(payload["streamers"][0]["is_live"])
self.assertIn("live_probed_at", payload["streamers"][0])
class DouyuProfileParseTests(unittest.TestCase):
def test_author_and_author_video_hashes(self) -> None:
self.assertEqual(
DOUYU_AUTHOR_HASH_RE.search(
"https://v.douyu.com/author/JPw9YOLKlw5X"
).group(1),
"JPw9YOLKlw5X",
)
self.assertEqual(
DOUYU_AUTHOR_HASH_RE.search(
"https://v.douyu.com/author-video/JPw9YOLKlw5X"
).group(1),
"JPw9YOLKlw5X",
)
def test_dollar_data_maps_fan_fields(self) -> None:
blob = (
'{type:"0",uid:"13506421",upId:"JPw9YOLKlw5X",upFollowNum:"16",'
'subscribeNum:"465781",playCount:"26074077",'
'avatar:"https://example.com/a.jpg",'
'name: "18yearsold\\u5929\\u6b8b\\u5c11\\u5e74K9",'
'contents: "",roomId:"235520",roomName: "k9 room"}'
)
profile = extract_profile_from_douyu_data(parse_douyu_dollar_data(blob))
self.assertEqual(profile["nickname"], "18yearsold天残少年K9")
self.assertEqual(profile["unique_id"], "235520")
self.assertEqual(profile["following_count"], 16)
self.assertEqual(profile["follower_count"], 465781)
self.assertEqual(profile["total_favorited"], 26074077)
self.assertEqual(profile["profile_url"], "https://v.douyu.com/author/JPw9YOLKlw5X")
self.assertEqual(profile["live_url"], "https://www.douyu.com/235520")
self.assertNotIn("signature", profile)
def test_room_html_up_id_extraction(self) -> None:
plain = '{"rid":957090,"up_id":"EqAvg1lQD75L","ban_display":0}'
escaped = (
r'{\"rid\":957090,\"up_id\":\"EqAvg1lQD75L\",\"ban_display\":0}'
)
self.assertEqual(extract_douyu_up_id_from_room_html(plain), "EqAvg1lQD75L")
self.assertEqual(extract_douyu_up_id_from_room_html(escaped), "EqAvg1lQD75L")
self.assertIsNone(extract_douyu_up_id_from_room_html("<html></html>"))
class NotifySummaryTests(unittest.TestCase):
def test_skipped_summary_is_labeled(self) -> None: