- 音视频
- 直播
- 移动开发
【免费下载链接】pure_live
纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。
本文基于 docs/MISSEVAN_STAGED_ADAPTER_AUDIT_2026_09_08.md 整理,围绕 pure_live 项目中"猫耳 FM(Missevan)"平台适配器的暂存审计全过程展开。该审计记录了一次典型的"只暂存 API/站点适配器、暂不开放入口"的平台扩展实践:从公开接口逆向核验、媒体证据探测,到真实接口暴露的分页反例、暂存实现不变量与验证记录,再到尚未关闭的接入门禁清单。读者可从中掌握如何以可回滚、不污染主线的方式为聚合直播应用新增平台,以及如何用"契约驱动 + 证据留痕"的方式做平台扩展审计。
一、审计背景:一次"仅暂存、不接入"的平台扩展
本批工作是用户要求的平台扩展,而不是已注册平台的缺陷修复,起点为提交b02290fbccc04d21ecbf08b93084c25fdcfbf0b0。审计的核心纪律有四点:
- 仅暂存 API/站点适配器:没有加入 Sites 注册表、设置迁移或任何用户入口,参考平台未注册数仍为 15;
- 不提升版本、不合并上游、不发布;
- 本批没有手机、ADB、MT、Root/LSP 操作,全程只读;
- 可整体回滚:回滚只需移除两个适配器及其测试/探针,不涉及用户数据或当前候选 APK。审计时 Android 待安装候选继续为
b5f39c2b,已验证安装仍为80b7431c,本批没有构建新包。
这种"代码先行、入口后置"的节奏,避免了平台接入对既有分发流程的干扰,也让后续正式接入可以基于一份冻结的、有据可查的契约进行验收。
二、来源与独立核验方法
审计没有执行或合并任何外部实现,而是"只读比较两个冻结实现",并叠加官网实测:
- 参考实现一:bililive-go 的
missevan.go,提供房间 API、status.open、FLV 入口及AudioOnly提示;审计方未复制其宽松路径拆分、以及"所有非 200/非零 code 均视为不存在"的行为; - 参考实现二:biliup 的
missevan.rs,与前者共享同一公开详情契约;审计方未复制其"所有非零 code 视为下播、宽松 host 正则"的行为; - 猫耳 FM 官网
https://fm.missevan.com/与其页面实际引用的官方脚本bundle.30d30c6d.js:仅下载为文本阅读、未执行脚本,用于核对/apibaseURL、目录参数、分类、详情、HTTPS 转换,以及score(热度)与attention_count(粉丝)的文案语义。
采样从当地 09-08、UTC 09-07 16:15 起,全部为匿名 HTTP/媒体元数据采样,没有注入任何账号 Cookie/Token。原始响应含临时 CDN 签名,统一保留在忽略目录local-artifacts/work/reverse-api/missevan-20260908/,不进入 Git;evidence-index.json保存原始文件的 SHA256 与字节数,审计记录只记 host/字段/结果,不公开签名 URL。这一做法值得借鉴:证据留痕但敏感数据隔离,既能事后复核,又不把临时签名带进版本库。
三、公开请求合同:六个端点的实测结果
下表为审计实测的公开接口合同(GET 路径均相对于https://fm.missevan.com/api/v2/),也是 lib/shared/platforms/missevan/missevan_api.dart 中MissevanApi各方法实现的依据:
GET 路径(相对https://fm.missevan.com/api/v2/) | 实测结果 | 本批处理 |
|---|---|---|
meta/data | 200/code0;5 个父 catalog、18 个子 catalog、6 个首页 tab | 暂以官网 6 个 tab 为分类:catalog 与 tag 分开传递;子分类展示后续核验 |
chatroom/open/list?p=1与p=2 | 200/code0;info.Datas,声明 pagesize20 但实测 20 或 22 条,pagination 包含 p/pagesize/count/maxpage | 保留原生页码与全部返回条目,hasMore 看 p/maxpage,不按名义 20 条裁剪;每次 1 请求、最多 100 条及 1MiB 响应上限 |
同目录加catalog_id=104 | 200/code0,20 条;响应 catalog_id 可以是子分类 | 按父筛选结果接纳,不错误要求每行 catalog_id 等于父 ID |
同目录加tag_id=1 | 200/code0,20 条 | tag 和 catalog 不同命名空间,不互相替换 |
live/{room_id} | 200/code0,info.room/creator/websocket/vtuber | 绑定请求房间及 creator 身份,只读 room/creator;未接入 websocket |
live/1 | HTTP 404/code500030004 | notFound;其他未知错误保持失败,不伪造下播 |
在源码实现中,这些合同被逐条固化为校验逻辑。例如 missevan_api.dart 的_get对 HTTP 状态码做了分类:200正常、401/403归类access、404归类notFound、429归类rateLimited、>=500归类service,其余视为transport;而在业务码层面,只有明确观测到的code == 500030004(即不存在的房间)才判定 notFound,其他非零 code 一律保持失败(service),绝不把未知错误伪造为"已下播"。这一点与参考实现"所有非零 code 视为下播"的宽松行为形成了鲜明对比。
MissevanApi定义的失败枚举完整覆盖八种情形(missevan_api.dart):
enum MissevanFailure { transport, access, rateLimited, service, notFound, schema, cancelled, qualityUnavailable }异常对象不包含响应正文或签名 URL,避免敏感信息随异常传播。
四、字段语义:几个容易被误读的关键字段
公开列表与详情中存在若干反直觉的字段组合,审计明确了它们的语义边界:
status.broadcasting=false与status.open=1可同时出现,因此不能用前者过滤全部直播;判断开播的权威依据是status.open。_room转换(missevan_api.dart)只接受open为 0 或 1,其他值(未知/missing/nonbinary)一律抛schema错误,保证"明确 open=0 才判定下播"。score映射热度(AudienceMetricType.popularity),online=0、accumulation不作为同时在线人数;只有详情接口明确给出 followers 时才写入粉丝值——粉丝值取自statistics.attention_count,且仅在detail路径中赋值(missevan_api.dart)。- 搜索 endpoint 出现在官方脚本的连麦/PK 调用中,尚未证明其全站搜索语义,因此本批没有把它伪装成公开全站搜索。这一点在后续批次实现关键词搜索时被重新评估(详见第八节)。
五、媒体证据:一个房间的外部 FFprobe 证据
审计对同一个公开音乐房间的 HTTP 地址,经官网相同的 HTTPS 转换后分别采样:
- FLV:
d1-missevan04.bilivideo.com,200;只读 13 字节文件头时 flags=5; - HLS:
d1-missevan104.bilivideo.com,200;359 B 媒体列表,EXTINF 存在; - FFprobe 分别以 18 秒总预算、5 秒读超时、1MiB probe/3 秒分析上限检查两个来源,均退出 0、stderr 为空:AAC 48kHz 双声道 + H.264 16×16。
审计反复强调这条证据的边界:这是一个房间的外部 FFprobe 证据,不是所有猫耳房间的纯音频证明、应用首帧证明或 FFmpegKit 录制证明。因此:
- 不复制参考实现的全平台 AudioOnly 强制值;
- 不从 "FM" 名称推断无视频轨;
- 小尺寸画面/封面展示和真正无视频轨仍需另行验收。
采样记录写入media-probe.json,资源记录为20260907T162303322Z-missevan-media-contract.json,总耗时 7.533 秒、结束时活跃重型进程数为 0。这种"单点证据 + 明确证据边界"的表述方式,避免了把局部观测泛化成平台级结论。
播放地址的校验在源码中同样严格(missevan_api.dart 的mediaUrl):只接纳已见过的*.bilivideo.com子域、HTTP(S) 协议、默认端口、对应媒体扩展名(HLS 必须以.m3u8结尾、FLV 必须以.flv结尾),拒绝 userinfo、fragment、重复或毫秒级expires;expires必须是 10 位数字。HTTPS 转换保留签名 query 并清除 HTTP 默认端口,与官方客户端行为一致。
六、真实接口揭示的分页反例:pagesize=20 却有 22 条
这是本批最有价值的技术发现。首轮 59/59 确定性测试全部通过,但生产 API 探针在目录处遭遇 schema 失败(记录20260907T163503131Z-missevan-staged-contracts.json保留失败,当轮未进入 analyze)。与此同时,公开 HTTP 对照显示主页pagesize=20却返回 22 个Datas条目,category 和 p2 各 20 条。
随后核对官方脚本中的ROOM_GET_LIST_SUCCESS处理逻辑,真相浮现:官方 reducer对整份Datas转换并按 room_id 追加去重,滚动条件是pagination.page < page_count后请求 page+1——即以"服务器页"为单位滚动,而不是按固定 20 条计算跨页 offset,也不应按 trace 中的推广字段猜测并删除两条。据此修订:
- 新增
MissevanDirectoryPage,保留rooms/page/maxPage/count与hasMore(missevan_api.dart),每次读取一个真实服务器页; - 所有返回行在 100 条安全上限内保留并去重(
putIfAbsent按 room_id); directory(pageSize)仅为暂存 LiveSite 兼容参数(页面数字是原生服务器页,不是按 UI 偏好尺寸计算的偏移量),不代表服务器支持客户端请求大小;- 正式 UI 接入必须消费原生分页元数据,不能直接用 30 条阈值判断末页;
- 22 条推广混合 fixture 覆盖了 19/20 尾部条目保留、后续 21..65 无跳过、元数据终止条件三类场景。
该契约在源码中的对应实现位于directoryPage(missevan_api.dart):请求chatroom/open/list?p=page,校验响应中的pagination.p与请求页码一致、pagesize必须等于serverPageSize(20)、maxPage/count非负、Datas行数不超过 100,且当page > maxPage时拒绝非空行。同时 lib/shared/platforms/live_directory.dart 的LiveDirectoryPage契约明确注释:"行数不是分页证据"——服务器可能注入推广条目,或适配器可能排除已关闭房间。
七、暂存实现与不变量
7.1 API 层的传输纪律
MissevanApi(lib/shared/platforms/missevan/missevan_api.dart)的生产请求复用已有 Dio 客户端,保留应用代理/超时基础,但强化了以下纪律:
- 禁跟随 API 重定向(
followRedirects: false),validateStatus放行一切状态码交由上层分类; - 响应 1MiB 上限 + 20 秒总 body deadline:
readBody(missevan_api.dart)用StreamIterator逐块累计,BytesBuilder边收边计,慢速滴流无法无限续期超时;所有退出路径(含超时、超限)都取消 body 订阅释放流; - 注入式响应同样按UTF-8 字节数复核 1MiB 上限,防止字符串长度与实际字节数不一致导致的绕过;
- 请求前后检查取消状态,过期响应不提交为房间状态;
- 房间数字 ID 与链接做精确 host/path 校验(
roomId正则^[1-9][0-9]{0,17}$,roomFromUri限定fm.missevan.com/live/{id},见 missevan_api.dart)。
7.2 Site 层的契约实现
lib/shared/platforms/missevan/missevan_site.dart 的MissevanSite实现了LiveSiteRoomRefresher、LiveSiteRecordRoomResolver、LivePlayRecoveryResolver、LivePlayLeaseMetadata、LiveSiteDirectoryPager、LiveCancellableSearch、LiveSearchPaginationPolicy、LiveSiteExternalRoomResolver等接口,覆盖刷新、录制详情、同协议重新解析与 lease 元数据:
- 录制详情与刷新:
getRoomDetailForRefresh、getRoomDetailForRecording均走_api.detail,在detailIdentity为 null 时直接返回原房间,避免无谓请求; - lease 元数据:
expires按 Unix 秒解释,getPlayUrlInvalidAt返回硬失效时刻,getPlayUrlRefreshAt在其基础上提前 1 分钟作为续签窗口(missevan_site.dart)。审计特别强调"提前 1 分钟刷新与硬失效是不同字段",续签仍调用新详情,不重复使用旧 quality.data; - 播放质量:质量 ID 为
hls/flv,标签只写协议,不虚构分辨率或音频码率;单条LivePlayQuality(id 10000、quality 原画)聚合 FLV/HLS 两条线路(missevan_api.dart)。LivePlayLeaseMetadata接口在 lib/shared/platforms/live_site.dart 中定义,明确区分"预取 URL 可开始新连接的最终时刻"与"过期缓存必须丢弃"两个语义; - 精确搜索 + 分页策略:
searchRoomsCancellable先尝试把关键词解析为房间 ID(数字 ID 或fm.missevan.com/live/{id}链接)走详情单结果;解析失败且非 URL 才走关键词分页(missevan_site.dart)。supportsSearchPaginationFor(对应 lib/shared/platforms/live_search.dart 的LiveSearchPaginationPolicy)拒绝 ID、拒绝带 scheme 的外链,保证"链接直查"与"关键词翻页"互不污染; - 外部打开:
externalRoomTarget把房间映射到官网https://fm.missevan.com/live/{id}作为 web 目标。
7.3 影响面控制
本批不修改其他平台、全局播放器、录制器或持久化模型,没有新增任何第三方依赖——适配器完全依托现有 Dio、现有接口抽象与现有取消机制完成,这也是它能做到"整体可回滚"的前提。
八、验证记录:确定性测试、生产探针与风格门禁
审计留下了完整的分层验证轨迹:
- 首轮:59/59 确定性测试通过,但真实探针因"20 声明/22 实返"反例失败,原始日志与上方记录完整保留;
- 第二轮:58 通过、1 失败(记录
20260907T163932244Z-missevan-staged-contracts.json)。失败原因是新增推广位 fixture 对泛型List调用insertAll时类型不匹配,传输注入边界把该异常归类为transport;补上明确 Map 列表类型后重跑,而不是再次放宽 API 业务校验; - 行为复测:59/59 通过(猫耳 27、相邻 TwitCasting 32);生产 Dio/API 探针 1/1 通过,覆盖 6 分类、分类 20 房、首页 22 房、第二页 20 房、HLS/FLV、新详情刷新、expires 和 404 分类(见
adapter-probe.json,UTC 16:41:42),没有获取媒体片段或执行 StreamResolverService/原生录制; - 风格门禁:记录
20260907T164254996Z-missevan-staged-contracts.json的 unit/probe 均为 0,但 analyze 有 1 条括号风格 info,整体记录保持 failed;为该if补花括号,无行为变化,不重复网络或整批测试。最终四文件 analyze 无诊断(51.7 秒),记录20260907T164503960Z-missevan-staged-style.jsonsucceeded;含资源守卫全过程 82.249 秒、结束活跃重型进程 0。
外部逆向工具方面,reverse-api-engineer CLI 已核对 0.13.0/schema1 并执行 dry-run,但没有运行其外部 agent、生成客户端或捕获 HAR;cli-preflight.json中的 run/HAR/script 均未产生,实际证据全部来自直接公开 HTTP 与官方源码,不标记为浏览器 HAR 回放。
当前仓库中保留了配套的探针测试,可复现同类验证(均为环境变量控制的可选真实网络测试,默认 skip):
- tool/probes/missevan_public_contract_probe_test.dart:通过已注册适配器走匿名目录/详情/恢复契约,校验分类、首页/第二页、详情刷新、质量解析、lease 元数据与 notFound 分类,设置
PURELIVE_MISSEVAN_LIVE_PROBE=1启用; - tool/probes/missevan_keyword_search_probe_test.dart:验证公开关键词搜索的分页语义(第 2 页必须出现第 1 页没有的房间),设置
PURELIVE_MISSEVAN_SEARCH_LIVE_PROBE=1启用; - tool/probes/missevan_recording_probe_test.dart:走真实 FFmpegKit 原生录制,覆盖段增长、显式 stop、MP4 提交、源片段清理与独立 ffprobe/ffmpeg 全量解码,设置
PURELIVE_MISSEVAN_RECORDING_PROBE=1等环境变量启用; - test/shared/platforms/missevan_danmaku_session_test.dart:校验游客会话 cookie 提取——线上会同时下发
FM_SESS.sig与FM_SESS两个 Set-Cookie,正则只取FM_SESS且到分号为止,避免 dio 对同名多值抛异常导致三次重试全部落空。
九、尚未关闭的接入门禁
审计明确列出开放入口前必须完成的门禁,这也是"暂存"与"接入"之间的全部差距:
- UI 分页桥接:消费原生目录分页元数据的 UI 分页桥接(包括筛选后少于 20 条但
hasMore仍为 true 的场景)、Sites 注册、平台默认配置迁移与用户排序保留、链接识别、搜索能力/UI 声明、热度能力表及平台图标; - 播放/录制链路集成:播放/录制 headers 与现有代理、StreamResolverService、签名续租调用链的集成测试——外部探针不替代应用代理验收;
- 原生录制验收:FFmpegKit 原生录制、完整严格解码、停止/转封装/源文件保留、音频模式/前后台/封面与 16×16 视频表现;真正无视频轨需另行测试;
- 离线样本补充:公开离线房间与结构漂移样本。本批的离线是确定性 fixture,不是实测离线房间;目录跨请求会动态变化,测试只保证静态页算术与页内去重,不承诺实时列表无重排;
- 遗留能力:搜索、弹幕协议、子分类、Android 及 Windows 原生验收仍待完成。
结论措辞非常克制:"以上完成相应合同后再开放入口并更新平台数量;不是现在就宣布'猫耳已接入'。"
十、从当前仓库快照看后续进展
本文所依据的审计快照日期为 2026-09-08。在后续批次中(可从当前仓库快照观察到),猫耳 FM 的接入逐步落地:
- 平台注册:
missevanSite已进入 lib/domains/live/data/platforms/sites.dart 的supportedSiteIds(sites.dart),并配有assets/images/missevan.png图标(sites.dart)与Site(id: missevan, liveSite: MissevanSite())构建条目(sites.dart); - 搜索能力:在 lib/domains/live/presentation/search/search_capability.dart 中声明为
liveAndOffline、支持分页、不支持 web 搜索; - 播放请求头:播放/录制 headers 已接入全局 lib/domains/live/data/playback_header_resolver.dart,复用
MissevanApi.playHeaders(Referer/Origin/UA); - 弹幕实现:审计时"未接入 websocket",当前仓库已有完整的 lib/shared/platforms/missevan/missevan_danmaku.dart 实现(游客会话
FM_SESScookie 获取、wss://im.missevan.com/ws心跳、brotli 帧解码、statistics热度/在线人数更新),说明弹幕协议契约已在后续核验落地; - 观众指标设置:猫耳 FM 出现在 lib/features/settings/pages/audience_metric_settings_page.dart 的观众指标配置列表中,印证了
score(热度)与真实在线人数的展示语义区分。
十一、总结:一次可复用的平台扩展审计范式
从这份审计可以提炼出 pure_live 平台扩展的四条可复用经验:
- 契约先行、证据留痕:把公开接口的每个端点的实测结果、字段语义、分页行为固化成表格与代码校验,原始响应连同 SHA256 存忽略目录,敏感签名不进 Git;
- 不复制参考实现的宽松行为:对参考代码逐条甄别,明确"哪些行为要学、哪些行为要拒"(如非零 code 一律下播、宽松 host 正则、全平台 AudioOnly 强制值);
- 用真实接口反例校准测试:确定性测试全绿不代表契约正确,生产探针暴露的"pagesize=20 却返回 22 条"最终修正了分页模型——行数不是分页证据,服务器页元数据才是;
- 接入门禁清单化:暂存只交付适配器本身,Sites 注册、UI 分页、代理/录制链路、原生验收、离线样本逐项列门禁,全部闭环后才开放入口并更新平台数量。
- 音视频
- 直播
- 移动开发
【免费下载链接】pure_live
纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。
相关推荐
pure_live 接入 AcFun 直播:公开接口合同实测、适配器设计与质量门禁审计
pure_live 接入 AcFun 直播:公开接口合同实测、适配器设计与质量门禁审计 本指南以 docs/ACFUN_ADAPTER_AUDIT_2026_0
音视频直播移动开发pure_live 原生目录分页接入实践:猫耳 FM 目录页的 LiveDirectoryController 设计与行为合同
pure_live 原生目录分页接入实践:猫耳 FM 目录页的 LiveDirectoryController 设计与行为合同 本文围绕 pure_live 项
音视频直播移动开发Pure Live 猫耳 FM(Missevan)平台接入实战:从公开 API 合同到原生录制校验
Pure Live 猫耳 FM(Missevan)平台接入实战:从公开 API 合同到原生录制校验 本篇基于 Pure Live 仓库中的接入审计文档 docs
音视频直播移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考