feat(web): show first-RE time on up page, drop decoupled folder badges
This commit is contained in:
1 parent
2ff3e7952b
commit
5d7521075a
4 files changed
+6
-7
No files matched your search
+2
-1
@@ -535,7 +535,7 @@ P3 实现说明:
|
||||
| UP主(`/ups`) | 独立入口,不再与视频混排;卡片显示 UP 头像、视频数与总时长、最新粉丝数,以及当前排序的主/副指标(「总播放」排序时副指标为「平均播放」);排序下拉含内容效率、互动效率、内容量、总播放、视频数、平均播放、总互动、最近发布;不提供按视频数筛选。 |
|
||||
| 数据说明(`/about`) | 数据来源与更新、发布时间/入库时间/快照日期、指标公式、时长与成熟窗口筛选、粉丝快照覆盖与已知局限;数值由 `/api/about` 动态读取。 |
|
||||
| 视频详情 | 元数据、分 P 数、七项指标、内容/互动效率、入库时粉丝数、单指标趋势、快照表、同 UP 对比。 |
|
||||
| UP 页 | 该 UP 的头像、全部视频、聚合趋势、单视频对比、总时长与播放·小时、入库时与当前粉丝数。 |
|
||||
| UP 页 | 该 UP 的头像、mid、首RE时间、最近发布时间、全部视频、聚合趋势、单视频对比、总时长与播放·小时、入库时与当前粉丝数。 |
|
||||
| 粉丝页 | 粉丝总数、日增、7 日净增、累计增长(标注起始日期);支持预设和自选日期,范围超过一年时按每月峰值日期聚合;最近记录默认 14 天并显示“较上一成功日”增量;不叠加视频数据。 |
|
||||
| 候选池 | 待确认候选、规则命中、剩余保留时间;操作指向 Telegram/CLI。 |
|
||||
| 系统状态 | 最近扫描、同步、快照、下次运行、备份、鉴权和黑名单。 |
|
||||
@@ -754,6 +754,7 @@ uv run python tests/test_moves.py
|
||||
- `ViewStat` / `VideoDetail` 增加 UP `mid/name/face` 解析;
|
||||
- UP 列表卡片由视频封面改为 UP 头像(无头像时显示首字母占位)、常驻显示最新粉丝数;「总播放」排序时副指标显示「平均播放」,避免与主指标重复;
|
||||
- UP 详情页补充头像;
|
||||
- UP 详情页移除已解耦的收藏夹徽标,改为显示「首RE时间」(该 UP 最早视频发布时间)与最近发布时间;`/api/ups/{mid}` 不再返回 `folders`;
|
||||
- `/api/ups` 返回 `up_face` 与 `follower_latest`,`/api/ups/{mid}` 返回 `up_face`;
|
||||
- 自动化测试更新:`test_migrations.py`、`test_database.py`、`test_web_queries.py`、`test_bilibili_client.py`、`test_library.py`、`test_snapshot.py`、`web_fixtures.py`。
|
||||
|
||||
|
||||
@@ -1270,9 +1270,6 @@ class DashboardQueries:
|
||||
view_by_date = {str(row["stat_date"]): int(row["view"] or 0) for row in rows}
|
||||
dates = _date_range(min(view_by_date), latest) if view_by_date else []
|
||||
items.sort(key=lambda item: ((item["metrics"] or {}).get("view", 0), item["pubdate"]), reverse=True)
|
||||
folder_titles = sorted({
|
||||
str(item["folder_title"]) for item in items if item.get("folder_title")
|
||||
})
|
||||
return {
|
||||
"up_mid": mid,
|
||||
"up_name": items[0].get("up_name") or f"UP {mid}",
|
||||
@@ -1304,7 +1301,6 @@ class DashboardQueries:
|
||||
"follower_latest": None if profile is None else profile["follower_latest"],
|
||||
"first_pubdate": min(item["pubdate"] for item in items),
|
||||
"latest_pubdate": max(item["pubdate"] for item in items),
|
||||
"folders": folder_titles,
|
||||
"blacklisted": bool(conn.execute(
|
||||
"SELECT 1 FROM up_blacklist WHERE mid = ?", (mid,)
|
||||
).fetchone()),
|
||||
|
||||
@@ -49,10 +49,10 @@ export async function renderUp(root, { params, query }) {
|
||||
<h1>${escapeHtml(up.up_name || `UP ${mid}`)}</h1>
|
||||
<div class="detail-meta">
|
||||
<span>mid ${up.up_mid}</span>
|
||||
<span>首RE时间 ${escapeHtml(formatDate(up.first_pubdate))}</span>
|
||||
${up.blacklisted ? badge("黑名单 · 仅影响发现", "warning") : ""}
|
||||
${(up.folders || []).map((title) => badge(title)).join("")}
|
||||
</div>
|
||||
<p class="section-note">首次发布 ${escapeHtml(formatDate(up.first_pubdate))} · 最近发布 ${escapeHtml(formatDate(up.latest_pubdate))}</p>
|
||||
<p class="section-note">最近发布 ${escapeHtml(formatDate(up.latest_pubdate))}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -196,6 +196,8 @@ def test_up_queries():
|
||||
assert detail["series"]["dates"] == list(DATES)
|
||||
assert detail["series"]["views"] == [6000, 6200, 6400]
|
||||
assert detail["space_url"].endswith("/1")
|
||||
assert "folders" not in detail
|
||||
assert detail["first_pubdate"] > 0
|
||||
|
||||
blacklisted = q.up_detail(2)
|
||||
assert blacklisted["blacklisted"] is True
|
||||
|
||||
Reference in new issue
Block a user