97 lines
6.6 KiB
JavaScript
97 lines
6.6 KiB
JavaScript
import { api } from "../api.js";
|
||
import { escapeHtml } from "../format.js";
|
||
|
||
function weightRows(weights) {
|
||
return Object.entries(weights || {})
|
||
.map(([metric, value]) => `<li><code>${escapeHtml(metric)}</code> ${(Number(value) * 100).toFixed(0)}%</li>`)
|
||
.join("");
|
||
}
|
||
|
||
export async function renderAbout(root) {
|
||
const data = await api("/api/about");
|
||
const analytics = data.analytics || {};
|
||
const followers = data.followers || {};
|
||
const coverage = followers.videos_total
|
||
? Math.round((followers.videos_covered / followers.videos_total) * 100)
|
||
: 0;
|
||
|
||
root.innerHTML = `<section class="page about-page">
|
||
<header class="page-head">
|
||
<h1>数据说明 <span class="page-sub">统计口径与方法</span></h1>
|
||
</header>
|
||
|
||
<section class="panel">
|
||
<h2>数据来源与更新</h2>
|
||
<ul class="about-list">
|
||
<li>视频元数据、播放与互动来自 B 站公开接口;粉丝数来自 <code>relation/stat</code>,匿名可用。</li>
|
||
<li>发现与收藏夹同步每小时运行一次,每日快照在 00:01 采集;最新数据日:<strong>${escapeHtml(data.data_date || "暂无")}</strong>。</li>
|
||
<li>当前快照覆盖 <strong>${data.snapshot_days}</strong> 天${data.snapshot_first_date ? `(起始 ${escapeHtml(data.snapshot_first_date)})` : ""};历史快照无法从 B 站回填,只能从部署当天开始积累。</li>
|
||
<li>数据库 schema 版本 <code>${data.schema_version}</code>;${data.preview ? "当前为预览数据,不影响真实数据库。" : "当前为真实数据库。"}</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<h2>三个时间概念</h2>
|
||
<ul class="about-list">
|
||
<li><strong>发布时间</strong>(<code>pubdate</code>):视频在 B 站上线的时刻。</li>
|
||
<li><strong>入库时间</strong>(<code>first_seen</code>):本站第一次看到并写入该视频的时刻。人工加收藏或搜索确认都可能导致入库晚于发布。</li>
|
||
<li><strong>快照日期</strong>(<code>stat_date</code>):每天采集一次播放与互动,写入 <code>snapshot</code>。缺失日不插值,曲线直接断开。</li>
|
||
</ul>
|
||
<p class="section-note">因此,同一个视频的“发布后 7 天数据”和“入库后 7 天数据”可能不是同一段区间;详情页会同时展示发布时间与入库时间。</p>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<h2>指标口径</h2>
|
||
<ul class="about-list">
|
||
<li><strong>播放 / 投币 / 收藏 / 评论</strong>:直接取当日快照;弹幕与转发不单列,已并入“互动”。</li>
|
||
<li><strong>互动</strong>:点赞 + 投币 + 收藏。综合热度也使用全部七项指标,按配置权重加权。</li>
|
||
<li><strong>互动率</strong>:(点赞 + 投币 + 收藏) / 播放;播放不足 100 不计算。</li>
|
||
<li><strong>综合热度</strong>:每项先 <code>log1p</code>,再按中位数与 IQR 做稳健标准化,最后加权求和。分数只用于同一批数据内排序,不能跨日期比较。</li>
|
||
<li><strong>内容效率</strong>:播放 / 内容小时。内容小时 = 视频总时长(多 P 为各 P 之和)÷ 3600。</li>
|
||
<li><strong>互动效率</strong>:互动 / 内容小时,互动口径同上。</li>
|
||
<li><strong>UP 内容效率</strong>:把该 UP 的全部视频合并后计算,并用 Gamma–Poisson 经验贝叶斯向全库均值收缩,避免单条视频或极小样本冲榜。收缩强度 κ ${analytics.shrink_minutes ? `固定为 ${analytics.shrink_minutes} 分钟` : "取全库 UP 总内容分钟的中位数"}。</li>
|
||
<li><strong>累计触达</strong>:总播放。它是规模指标,不代表吸引力;高产 UP 排在前列是正常结果。</li>
|
||
<li><strong>分 P</strong>:<code>pages</code> 是分 P 数,<code>duration</code> 是该 bvid 各 P 时长之和。B 站不公开单 P 播放,因此没有单 P 榜;分 P 数只用于详情展示,不参与排序。</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<h2>榜单筛选</h2>
|
||
<ul class="about-list">
|
||
<li>内容效率与互动效率只统计时长 ≥ <strong>${analytics.min_duration_seconds}</strong> 秒的视频,排除结构不同的短视频与切片。</li>
|
||
<li>只统计发布满 <strong>${analytics.maturity_days}</strong> 天的视频;reaction 视频通常在两周内接近平台期,过早比较会系统性低估新视频。</li>
|
||
<li>不引入无界的“年龄惩罚”:超过成熟窗口后不再按天加分或扣分。</li>
|
||
<li><code>duration</code> 为空或 ≤ 0 的视频(通常只在失效视频上出现)不进入任何时长相关榜,也不做插补。</li>
|
||
<li>当前配置的最小播放门槛为 <strong>${analytics.min_view_for_efficiency}</strong>。</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<h2>综合热度权重</h2>
|
||
<ul class="about-list about-weights">${weightRows(data.heat_weights)}</ul>
|
||
<p class="section-note">权重是主观配置,可在配置文件中调整;页面展示的是当前生效值。</p>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<h2>UP 粉丝快照</h2>
|
||
<ul class="about-list">
|
||
<li>视频入库时记录一次 UP 当前粉丝数,写入 <code>video.up_follower</code>;同一 UP 的旧视频不会被新快照覆盖。</li>
|
||
<li><code>up_profile.follower_first</code> 保留首次采集值,<code>follower_latest</code> 随刷新更新。</li>
|
||
<li>历史视频的粉丝数是回填值,会标记 <code>backfilled</code>;它与真实的“入库时”存在时间差,暂不用于榜单排序。</li>
|
||
<li>当前覆盖:${followers.videos_covered} / ${followers.videos_total} 条视频(${coverage}%),涉及 ${followers.ups_tracked} 个 UP;缺失 ${followers.videos_missing} 条,可运行 <code>app.py backfill-followers</code> 补齐。</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section class="panel">
|
||
<h2>已知局限</h2>
|
||
<ul class="about-list">
|
||
<li>播放是累计值,受发布时间影响;效率榜用成熟窗口过滤,但无法完全消除历史推荐差异。</li>
|
||
<li>B 站不公开完播率与单 P 播放,内容小时是“供给时长”,不是“实际观看时长”。</li>
|
||
<li>多 P 可能让同一用户产生多次播放计数,本站不单独校正,属于已知偏差。</li>
|
||
<li>粉丝数可能包含不活跃账号;粉丝快照暂不参与任何榜单。</li>
|
||
<li>样本不足(少于 2 天快照、单视频 UP)会显示提示,不伪造 0。</li>
|
||
</ul>
|
||
</section>
|
||
</section>`;
|
||
}
|