feat(web): include removed in visible stats; cover bang badge

站内展示/统计统一 VISIBLE(含 removed):热度、UP 列表、总览
active_ups/初导入计数、说明页粉丝覆盖、黑名单视频数。采集队列仍
只 active/unfaved。失效片封面右上角感叹号徽标,点击/tap 提示
「该视频已失效」;手动/候选入库仍可复活 removed→active。
This commit is contained in:
bot-ci committed 2026-11-03 23:45:44 +08:00
1 parent 6c73904b25
commit 0c660c6474
7 files changed
+122 -17

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
"""失效封面徽标静态落地检查: uv run python tests/test_removed_cover_mark.py"""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
STATIC = ROOT / "reaction_board" / "web" / "static"
def test_removed_cover_mark_markup():
components = (STATIC / "components.js").read_text(encoding="utf-8")
assert "export function removedCoverMark" in components
assert 'data-removed-tip' in components
assert "该视频已失效" in components
assert "${removedCoverMark(item)}" in components
app = (STATIC / "app.js").read_text(encoding="utf-8")
assert 'closest("[data-removed-tip]")' in app
assert 'toast("该视频已失效")' in app
css = (STATIC / "style.css").read_text(encoding="utf-8")
assert ".cover-removed-mark" in css
video = (STATIC / "views" / "video.js").read_text(encoding="utf-8")
assert "removedCoverMark(detail)" in video
def main():
test_removed_cover_mark_markup()
print("OK: 失效封面徽标静态落地检查通过")
if __name__ == "__main__":
main()
+31 -9
View File
@@ -38,11 +38,11 @@ def test_overview():
assert overview["data_date"] == DATES[-1]
assert overview["snapshot_days"] == 3
assert overview["is_first_day"] is False
assert overview["initial_import_count"] == 2
assert overview["initial_import_count"] == 3 # 含 removed 104
assert overview["counts"]["active"] == 2
assert overview["counts"]["unfaved"] == 1
assert overview["counts"]["removed"] == 1
assert overview["counts"]["active_ups"] == 2
assert overview["counts"]["active_ups"] == 3 # 含 UP C(仅 removed)
assert overview["counts"]["candidates_pending"] == 1
assert overview["totals"]["view"] == 1200 + 5200 + 120
assert overview["interactions"] > 0
@@ -68,7 +68,7 @@ def test_video_listing_filters_and_sorting():
heat = q.list_videos(sort="heat")
aids = [item["aid"] for item in heat["items"]]
# 热度榜仍按门槛过滤;104 removed 无成功快照,不进热度
# 热度门槛榜口径含 removed;104 无成功快照仍不进热度
assert 104 not in aids
# 综合热度榜应用最小播放门槛 2000:101(1200)与 103(120)被排除
assert aids == [102], aids
@@ -111,6 +111,27 @@ def test_video_listing_filters_and_sorting():
db.close()
def test_heat_ranking_includes_removed_with_metrics():
"""removed 进入热度门槛榜口径;有成功快照且过门槛时可见。"""
with tempfile.TemporaryDirectory() as tmp:
db, q = setup(tmp)
try:
db.write(lambda conn: conn.execute(
"UPDATE snapshot SET ok = 1, view = 5000, like = 10, coin = 1, "
"favorite = 1, share = 0, reply = 0, danmaku = 0, err = NULL "
"WHERE aid = 104 AND stat_date = ?",
(DATES[1],),
))
DASHBOARD_CACHE.clear()
heat = q.list_videos(sort="heat")
aids = [item["aid"] for item in heat["items"]]
assert 104 in aids
assert next(item for item in heat["items"] if item["aid"] == 104)["status"] == "removed"
finally:
db.close()
def test_growth_and_cold_start():
with tempfile.TemporaryDirectory() as tmp:
db, q = setup(tmp)
@@ -191,13 +212,14 @@ def test_up_queries():
try:
ups = q.list_ups()
assert ups["sort"] == "avg_views"
assert ups["total"] == 2
assert ups["total"] == 3 # 含仅有 removed 视频的 UP C
assert ups["items"][0]["up_mid"] == 1
assert ups["items"][0]["video_count"] == 2
assert ups["items"][0]["total_views"] == 6400
assert ups["items"][0]["avg_views"] == 3200.0
assert ups["items"][0]["up_face"] == "https://i0.hdslb.com/bfs/face/a.jpg"
assert ups["items"][0]["follower_latest"] == 12345
assert {item["up_mid"] for item in ups["items"]} == {1, 2, 3}
one = q.list_ups(min_videos=2)
assert one["total"] == 1
@@ -228,8 +250,8 @@ def test_up_queries():
# 大小写不敏感
lower = q.list_ups(q="up b")
assert lower["total"] == 1 and lower["items"][0]["up_mid"] == 2
# 命中多个 UP
assert q.list_ups(q="UP")["total"] == 2
# 命中多个 UP(含 UP C)
assert q.list_ups(q="UP")["total"] == 3
finally:
db.close()
@@ -479,7 +501,7 @@ def test_year_filter_and_published_sort():
assert added["total"] == 4 # 含 removed
ups = q.list_ups(sort="efficiency", year=2026)
assert ups["year"] == 2026 and ups["total"] == 2
assert ups["year"] == 2026 and ups["total"] == 3
ups_none = q.list_ups(sort="views", year=2025)
assert ups_none["total"] == 0
finally:
@@ -506,9 +528,9 @@ def test_about_payload():
"growth_rate_prior": 1000,
}
assert about["heat_weights"]["view"] == 0.30
assert about["followers"]["videos_total"] == 3
assert about["followers"]["videos_total"] == 4 # 含 removed
assert about["followers"]["videos_covered"] == 3
assert about["followers"]["videos_missing"] == 0
assert about["followers"]["videos_missing"] == 1
assert about["folders"][0]["media_id"] == 3700023351
assert about["preview"] is False
finally: