test(refactor): [BASE-03] capture media and lifecycle behavior

This commit is contained in:
Harvey Zhao committed 2026-09-10 19:55:55 +08:00
1 parent b382ec08f7
commit 9489392905
15 files changed
+1333 -20

No files matched your search

+4
View File
@@ -32,3 +32,7 @@ BASE-01 于 2026-09-10 固定核心与 chapter 的 npm 发布内容,数据在
## 公共 API 报告
BASE-02 已在内置浏览器取得核心/chapter 的同步 API 和描述符快照,见 [覆盖与重跑](api-coverage.md) 和 [冻结报告](public-api.json)。事件/生命周期、DOM、类型、分发和其他包仍由各自任务补齐。
## 事件与生命周期报告
BASE-03 已在内置浏览器实测播放推进、暂停/seek/切源、事件及销毁;两次 27 项断言通过,并保存 6 项历史问题。见 [覆盖与复现](lifecycle-coverage.md) 和 [原始报告](lifecycle.json)。受控拒绝与真正媒体播放分别标注,不代替全环境验收。
+2
View File
@@ -45,3 +45,5 @@ BASE-02 使用固定 npm artplayer 5.4.0 + artplayer-plugin-chapter 1.1.0 的 UM
4. `node refactor/scripts/api.mjs --check` 只核对持久报告和来源/快照摘要;`node --test refactor/scripts/api.test.mjs` 运行报告/比较器负例。两者不启动浏览器,CI 不能把它们当作新一轮真实浏览器执行。
yarn test:baseline / ci:check 已通过通配入口包含新增 api.test.mjs。负例涵盖缺检查、错误来源、采集失败、描述符变化、删除方法、选项/静态默认值变化和继承 API 删除。正式候选对照和浏览器自动回归由 ENG-05/07 逐步接入,当前夹具仍固定旧发布文件,不自动加载工作区候选。
BASE-03 已完成首组事件/真实媒体/生命周期采集,覆盖细分与仍未验证部分见 [生命周期基线](lifecycle-coverage.md);上表的后续责任仍按对应细项处理,不自动宣称整个契约通过。
+49
View File
@@ -0,0 +1,49 @@
# 事件、媒体与生命周期基线
BASE-03 在 Codex 内置浏览器以固定 core 5.4.0 / chapter 1.1.0 执行,样本为仓库 video.mp4、video2.mp4。来源成员、媒体 SHA-256、夹具 LF 指纹、实际 UA 和采集时间均保存在 [lifecycle.json](lifecycle.json)。
## 验证方式与范围
点击 ../fixtures/lifecycle.html 的 Run lifecycle baseline,实际加载媒体、静音播放并观察 currentTime 推进。使用 ready/原生事件/状态条件等待,单步骤超时 12 秒;暂停及销毁后状态采用至少 250ms 的帧观察窗口。这个窗口用于测量稳定性,不是用 sleep 猜测加载完成。
公开 play 拒绝和切源恢复失败通过临时替换该实例 video.play 返回受控拒绝来验证,随后恢复原生方法。恢复失败发生在真实媒体切换、真实 canplay 之后;它验证包装器的错误传播,不代表测过浏览器自动播放权限、真实网络错误或所有 codec 故障。仅精确匹配受控错误对象的 unhandledrejection 会被探针记录到 expectedUnhandled 并 preventDefault;其他拒绝一律判为采集失败。
## 固定测试 ID 与契约
| 契约/场景 | 已执行 ID | 结果或约束 |
| --- | --- | --- |
| API-04 Emitter | EVENT.chain/context-arguments/once-reentry/dispatch-snapshot/off-original-once/throw-propagation | 同步链式调用、ctx/参数、once 重入、当前派发快照、按原回调移除 once、异常原样抛出且停止余下回调 |
| API-01/05 ready | LIFE.ready-callback/ready-once-after-switches | readyCallback 的 this 和参数均为实例,正常切源不重复 ready |
| API-02 真正播放 | MEDIA.metadata/play-promise/play-result/time-advances/pause-sync-stable/seek | native video 有有效媒体尺寸和 duration;时间推进;pause 同步返回 undefined,观察期间时间稳定;seek 到达目标 |
| API-02 切源 | MEDIA.quality-preserves-time/url-resets-resumes/latest-source、LIFE.restart-url | 暂停中 switchQuality 保留时间和暂停状态;播放中 switchUrl 归零并恢复播放;restart 参数为目标 URL |
| API-02 拒绝 | MEDIA.play-rejects-original | 原始受控 media.play 错误由公开 art.play Promise 原样拒绝;不是 codec/权限测试 |
| API-06 插件 | PLUGIN.async-promise/async-registry/duplicate-throws | async 工厂返回 Promise,结果随后可见、this/参数为 art,兑现值仍为 registry;重名同步抛错并保留原结果 |
| API-05 销毁 | LIFE.destroy-retains-markup/destroy-cleans-proxy/destroy-removes-markup/constructor-plugin-error | destroy(false) 保留 art-destroy 标记,proxy 监听移除、注册表移除;destroy(true) 清空容器;插件初始化异常能传播 |
完整的 27 个 ID 以 lifecycle.mjs 的 lifecycleChecks 为准,斜线缩写共享同一前缀。其余多插件组合、异步构造失败、真实网络重试、mutex、后台/移动端、字幕/SDK/worker 资源和类型测试仍由 BASE-08、ENG-05 和对应 CORE/包任务补齐。
记录的事件顺序有语义校验:loadedmetadata 在 ready 前;首次播放 native play -> native playing -> 公开 play;公开 pause 同步发生,native pause 随后到达;质量切换的 canplay 观察回调在 restart 前。原始 trace 保留更完整数据,但不把所有 native 事件计数、绝对媒体时间或跨阶段到达的 seeked 固定为全浏览器契约。
## 历史问题台账
这些发现不是必须永久保留的 API,也不是本次已修复。对应任务需要添加候选回归并记录兼容处理;不能通过要求旧缺陷继续发生来判断重构成功。
| ID | 发布版实测 | 负责修复/核对 | 判断边界 |
| --- | --- | --- | --- |
| BASE-LIFE-01 | 连续两个 switchUrl 在最后一个源的一次 canplay 后都兑现,旧操作没有独立完成依据 | CORE-09 | 最后源正确;过期操作结果语义需要明确,不直接推断发生了内容覆盖 |
| BASE-LIFE-02 | switchUrl 后立即 destroy,Promise 在至少 250ms 观察窗口内仍未结算 | CORE-09,关联 CORE-04 | 这是有界观察;不能仅凭窗口称永远 pending。源码移除 native 事件后缺少取消结算路径 |
| BASE-LIFE-03 | 异步插件结果在 destroy 后仍被注册 | CORE-08 | 已确认晚到注册;没有将它夸大为所有插件都泄漏资源 |
| BASE-LIFE-04 | A/B 两实例,A 连续 destroy 两次后 B 从 Artplayer.instances 消失 | CORE-04 | B 仍是活实例;探针随后显式销毁 B,避免污染后续用例 |
| BASE-LIFE-05 | 构造过程插件抛错后,已初始化的 DOM 和 57 个监听清理项仍存在,但实例未进入注册表 | CORE-04 | 探针持有失败实例并主动清理;57 为本次配置实测,不冻结为跨环境数量 |
| BASE-LIFE-06 | 已发布版本切源恢复 play 拒绝,产生未处理拒绝且切源 Promise 在观察窗口内仍 pending | CORE-09 | 受控错误注入。当前工作区 switchMix 已使用 silencePromise,原 playback 测试覆盖该修复;重构必须保持这项已有改进,不能倒退成发布版行为 |
发布产物中的 switchMix、playMix、Events 与工作区源码已对照。BASE-LIFE-04 的原因是没有检查 instances.indexOf(this) 的 -1 就 splice;BASE-LIFE-03 的注册管理器没有销毁状态检查;BASE-LIFE-05 构造链没有异常回收路径。它们由后续实现任务处理,不在基线采集时修改播放器。
## 重跑和报告检查
1. `node refactor/scripts/browser-server.mjs`;默认 http://127.0.0.1:8083/fixtures/lifecycle.html。
2. Chrome 不可用时直接用内置浏览器。每次 reload 后点击 Run lifecycle baseline,确认 CAPTURE COMPLETE、errors/unhandled 为空,并阅读 historical findings;这个状态表示采集完成,不表示没有缺陷。
3. `node refactor/scripts/lifecycle.mjs --compare refactor/.cache/reports/lifecycle.json`,验证媒体、来源、事件顺序,再比较稳定的发布版观察结果。
4. `node refactor/scripts/lifecycle.mjs --check` 只验证保存的报告。`yarn test:baseline` 包含报告校验负例:伪造播放推进、意外拒绝、事件颠倒会失败,合理时钟抖动不会造成快照失败。这些 Node 命令不重新执行浏览器。
lifecycleProfile 排除波动的时钟值和 native 事件次数,但保留已观察的 Promise、插件、实例注册及失败构造结果。它用于复验固定旧版本;候选修复历史问题后应采用正向回归与差异记录,不要求候选满足同一缺陷 profile。正式自动浏览器框架与候选包矩阵由 ENG-05/07 接续。
+833
View File
@@ -0,0 +1,833 @@
{
"schemaVersion": 1,
"task": "BASE-03",
"sourceCommit": "b382ec08",
"browserSurface": "Codex in-app browser",
"report": {
"kind": "lifecycle",
"environment": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36",
"language": "zh-CN"
},
"scripts": [
"/releases/artplayer/dist/artplayer.js",
"/releases/artplayer-plugin-chapter/dist/artplayer-plugin-chapter.js",
"/fixtures/lifecycle.js"
],
"checks": [
{
"id": "LIFE.version",
"passed": true
},
{
"id": "EVENT.chain",
"passed": true
},
{
"id": "EVENT.context-arguments",
"passed": true
},
{
"id": "EVENT.once-reentry",
"passed": true
},
{
"id": "EVENT.dispatch-snapshot",
"passed": true
},
{
"id": "EVENT.off-original-once",
"passed": true
},
{
"id": "EVENT.throw-propagation",
"passed": true
},
{
"id": "LIFE.ready-callback",
"passed": true
},
{
"id": "MEDIA.metadata",
"passed": true
},
{
"id": "MEDIA.play-promise",
"passed": true
},
{
"id": "MEDIA.play-result",
"passed": true
},
{
"id": "MEDIA.time-advances",
"passed": true
},
{
"id": "MEDIA.pause-sync-stable",
"passed": true
},
{
"id": "MEDIA.seek",
"passed": true
},
{
"id": "MEDIA.quality-preserves-time",
"passed": true
},
{
"id": "MEDIA.url-resets-resumes",
"passed": true
},
{
"id": "LIFE.ready-once-after-switches",
"passed": true
},
{
"id": "LIFE.restart-url",
"passed": true
},
{
"id": "MEDIA.play-rejects-original",
"passed": true
},
{
"id": "PLUGIN.async-promise",
"passed": true
},
{
"id": "PLUGIN.async-registry",
"passed": true
},
{
"id": "PLUGIN.duplicate-throws",
"passed": true
},
{
"id": "MEDIA.latest-source",
"passed": true
},
{
"id": "LIFE.destroy-retains-markup",
"passed": true
},
{
"id": "LIFE.destroy-cleans-proxy",
"passed": true
},
{
"id": "LIFE.destroy-removes-markup",
"passed": true
},
{
"id": "LIFE.constructor-plugin-error",
"passed": true
}
],
"observations": {
"expectedUnhandled": [
{
"phase": "controlled-resume-rejection",
"message": "controlled switch resume rejection"
}
],
"emitterOrder": [
"first",
"second",
"first",
"later"
],
"playback": {
"start": 0,
"progressed": 0.196562,
"duration": 90.046009,
"width": 640,
"height": 360
},
"pause": {
"at": 0.200309,
"observedMs": 253.19999998807907,
"maxDelta": 0
},
"readyCallbacks": 1,
"restarts": [
"/assets/sample/video2.mp4",
"/assets/sample/video.mp4?normal-switch"
],
"rejectedResume": {
"mode": "controlled video.play rejection after real canplay",
"switchState": "pending",
"observedMs": 252,
"expectedUnhandled": 1
},
"concurrentSwitch": {
"outcomes": [
"fulfilled",
"fulfilled"
],
"finalSource": "/assets/sample/video2.mp4?race-last",
"canplayEvents": 1
},
"destroyPending": {
"switchState": "pending",
"observedMs": 256.80000001192093,
"latePluginRegistered": true
},
"repeatedDestroy": {
"survivorBefore": true,
"survivorAfter": false
},
"failedConstructor": {
"hasMarkup": true,
"listeners": 57,
"registered": false
},
"remainingInstances": 0
},
"findings": [
{
"id": "BASE-LIFE-06",
"observation": "Published switch leaves an unhandled rejection and an unsettled promise when resume fails",
"owner": "CORE-09"
},
{
"id": "BASE-LIFE-01",
"observation": "Superseded switch resolves along with the latest source",
"owner": "CORE-09"
},
{
"id": "BASE-LIFE-02",
"observation": "Switch remains unsettled during the bounded observation after destroy",
"owner": "CORE-09"
},
{
"id": "BASE-LIFE-03",
"observation": "Async plugin registers its result after destroy",
"owner": "CORE-08"
},
{
"id": "BASE-LIFE-04",
"observation": "Destroying an instance twice unregisters a different live instance",
"owner": "CORE-04"
},
{
"id": "BASE-LIFE-05",
"observation": "Failed constructor leaves initialized markup/listeners until explicit probe cleanup",
"owner": "CORE-04"
}
],
"trace": [
{
"phase": "initial",
"event": "video:loadstart",
"time": 0,
"paused": true,
"ready": false,
"source": "/assets/sample/video.mp4",
"argument": "loadstart"
},
{
"phase": "initial",
"event": "video:loadedmetadata",
"time": 0,
"paused": true,
"ready": false,
"source": "/assets/sample/video.mp4",
"argument": "loadedmetadata"
},
{
"phase": "initial",
"event": "video:loadeddata",
"time": 0,
"paused": true,
"ready": false,
"source": "/assets/sample/video.mp4",
"argument": "loadeddata"
},
{
"phase": "initial",
"event": "ready",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": null
},
{
"phase": "initial",
"event": "video:canplay",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": "canplay"
},
{
"phase": "play",
"event": "video:play",
"time": 0,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": "play"
},
{
"phase": "play",
"event": "video:playing",
"time": 0,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": "playing"
},
{
"phase": "play",
"event": "play",
"time": 0,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": null
},
{
"phase": "pause",
"event": "pause",
"time": 0.200309,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": null
},
{
"phase": "pause",
"event": "video:pause",
"time": 0.200309,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": "pause"
},
{
"phase": "seek",
"event": "video:seeking",
"time": 2,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": "seeking"
},
{
"phase": "seek",
"event": "video:seeked",
"time": 2,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": "seeked"
},
{
"phase": "quality-paused",
"event": "pause",
"time": 2,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4",
"argument": null
},
{
"phase": "quality-paused",
"event": "video:loadstart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "loadstart"
},
{
"phase": "quality-paused",
"event": "video:loadedmetadata",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "loadedmetadata"
},
{
"phase": "quality-paused",
"event": "video:loadeddata",
"time": 2,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "loadeddata"
},
{
"phase": "quality-paused",
"event": "video:canplay",
"time": 2,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "canplay"
},
{
"phase": "quality-paused",
"event": "restart",
"time": 2,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "/assets/sample/video2.mp4"
},
{
"phase": "url-playing",
"event": "video:seeking",
"time": 2,
"paused": false,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "seeking"
},
{
"phase": "url-playing",
"event": "video:play",
"time": 2,
"paused": false,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "play"
},
{
"phase": "url-playing",
"event": "video:seeked",
"time": 2.000308,
"paused": false,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "seeked"
},
{
"phase": "url-playing",
"event": "video:canplay",
"time": 2.000344,
"paused": false,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "canplay"
},
{
"phase": "url-playing",
"event": "video:playing",
"time": 2.000379,
"paused": false,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": "playing"
},
{
"phase": "url-playing",
"event": "play",
"time": 2.000421,
"paused": false,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": null
},
{
"phase": "url-playing",
"event": "pause",
"time": 2.220301,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4",
"argument": null
},
{
"phase": "url-playing",
"event": "video:loadstart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "loadstart"
},
{
"phase": "url-playing",
"event": "video:loadedmetadata",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "loadedmetadata"
},
{
"phase": "url-playing",
"event": "video:loadeddata",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "loadeddata"
},
{
"phase": "url-playing",
"event": "video:canplay",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "canplay"
},
{
"phase": "url-playing",
"event": "restart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "/assets/sample/video.mp4?normal-switch"
},
{
"phase": "url-playing",
"event": "video:seeking",
"time": 0,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "seeking"
},
{
"phase": "url-playing",
"event": "video:play",
"time": 0,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "play"
},
{
"phase": "url-playing",
"event": "video:seeked",
"time": 0.000355,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "seeked"
},
{
"phase": "url-playing",
"event": "video:canplay",
"time": 0.000391,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "canplay"
},
{
"phase": "url-playing",
"event": "video:playing",
"time": 0.000424,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "playing"
},
{
"phase": "url-playing",
"event": "play",
"time": 0.000473,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": null
},
{
"phase": "url-playing",
"event": "pause",
"time": 0.226534,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": null
},
{
"phase": "controlled-resume-rejection",
"event": "video:pause",
"time": 0.241612,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "pause"
},
{
"phase": "controlled-resume-rejection",
"event": "video:play",
"time": 0.241904,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "play"
},
{
"phase": "controlled-resume-rejection",
"event": "video:playing",
"time": 0.242067,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": "playing"
},
{
"phase": "controlled-resume-rejection",
"event": "play",
"time": 0.242231,
"paused": false,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": null
},
{
"phase": "controlled-resume-rejection",
"event": "pause",
"time": 0.488549,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?normal-switch",
"argument": null
},
{
"phase": "controlled-resume-rejection",
"event": "video:loadstart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "loadstart"
},
{
"phase": "controlled-resume-rejection",
"event": "video:loadedmetadata",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "loadedmetadata"
},
{
"phase": "controlled-resume-rejection",
"event": "video:loadeddata",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "loadeddata"
},
{
"phase": "controlled-resume-rejection",
"event": "video:canplay",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "canplay"
},
{
"phase": "controlled-resume-rejection",
"event": "restart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "/assets/sample/video2.mp4?resume-rejection"
},
{
"phase": "controlled-resume-rejection",
"event": "video:seeking",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "seeking"
},
{
"phase": "controlled-resume-rejection",
"event": "video:seeked",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "seeked"
},
{
"phase": "controlled-resume-rejection",
"event": "video:canplay",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": "canplay"
},
{
"phase": "controlled-resume-rejection",
"event": "pause",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": null
},
{
"phase": "concurrent-switch",
"event": "pause",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?resume-rejection",
"argument": null
},
{
"phase": "concurrent-switch",
"event": "pause",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video.mp4?race-first",
"argument": null
},
{
"phase": "concurrent-switch",
"event": "video:loadstart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?race-last",
"argument": "loadstart"
},
{
"phase": "concurrent-switch",
"event": "video:loadedmetadata",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?race-last",
"argument": "loadedmetadata"
},
{
"phase": "concurrent-switch",
"event": "video:loadeddata",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?race-last",
"argument": "loadeddata"
},
{
"phase": "concurrent-switch",
"event": "video:canplay",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?race-last",
"argument": "canplay"
},
{
"phase": "concurrent-switch",
"event": "restart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?race-last",
"argument": "/assets/sample/video.mp4?race-first"
},
{
"phase": "concurrent-switch",
"event": "restart",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?race-last",
"argument": "/assets/sample/video2.mp4?race-last"
},
{
"phase": "destroy-pending",
"event": "pause",
"time": 0,
"paused": true,
"ready": true,
"source": "/assets/sample/video2.mp4?race-last",
"argument": null
},
{
"phase": "destroy-pending",
"event": "destroy",
"time": 0,
"paused": true,
"ready": true,
"source": null,
"argument": null
},
{
"phase": "duplicate-destroy",
"event": "destroy",
"time": 0,
"paused": true,
"ready": false,
"source": null,
"argument": null
},
{
"phase": "duplicate-destroy",
"event": "destroy",
"time": 0,
"paused": true,
"ready": false,
"source": null,
"argument": null
},
{
"phase": "duplicate-destroy",
"event": "destroy",
"time": 0,
"paused": true,
"ready": false,
"source": null,
"argument": null
}
],
"errors": [],
"unhandled": [],
"capture": {
"capturedAt": "2026-09-10T11:50:21.727Z",
"releases": [
{
"name": "artplayer",
"version": "5.4.0",
"integrity": "sha512-2B+plbx8N2yNsjK4nJU3+EOG8TULm1LRZk/QPkWRAMEX2Ee/MSnZG/WJYz8kcoZxZuLKcQ3uXifqLuPxZOH29A==",
"member": "package/dist/artplayer.js",
"sha256": "f1db1fa3406aa6d41baa7c554749429a8e79f99d56a794bde23345eb9c07573f"
},
{
"name": "artplayer-plugin-chapter",
"version": "1.1.0",
"integrity": "sha512-ji66Lr2K2S+OjFMuMby3yq+pefWlOC51Sbpop/DIr2N6w3F0smrF+YZbshN1Dy8/Tx1TYpT7N6WkkZWy5Jix1w==",
"member": "package/dist/artplayer-plugin-chapter.js",
"sha256": "c2aaee11bf1db765433b01d9f8dd3978e41fa4d39655db00ebd03fc0be3e35e0"
}
],
"fixtureHashAlgorithm": "sha256-lf",
"fixtures": {
"lifecycle.html": "120851612220cb5b2101f495f3c90c7bfe1541bc81f320d700a76781689c4a0d",
"lifecycle.js": "2017800cf809c386dc06879132aa34c5524cfb404a50ecd887ba28c2639044bd"
},
"media": {
"video.mp4": "30a2de667ba267b6bc605edcee1c4bd728c048ed76f500ea0e73d1f1a6f8f980",
"video2.mp4": "24b12d40ea21ace8e16a66cdb6085391e0262c3d96f8a972ac76607f48943b64"
}
}
},
"verification": {
"repeatedCaptureAt": "2026-09-10T11:52:08.593Z",
"profileSha256": "688f99f4d69ed937bc947cf9def2c546de6b644218c9f7509ee4c54dbef17964",
"repeatComparison": "equal semantic outcomes; variable media timestamps retained only as observations"
}
}