From 3d4c63610196942d85872c0677753d7360fceb0b Mon Sep 17 00:00:00 2001 From: Harvey Zhao Date: Sat, 12 Sep 2026 04:23:52 +0800 Subject: [PATCH] docs(dash): [PKG-DASH-01] freeze released SDK v4 and workspace v5 contracts --- refactor/baselines/dash-control-contract.md | 66 +++++++++ refactor/baselines/dash-control-release.json | 135 ++++++++++++++++++ .../2026-09-12-PKG-DASH-01-contract.md | 21 +++ refactor/plan.md | 11 +- refactor/progress.md | 12 ++ refactor/risk-table.md | 7 +- refactor/risks.json | 103 ++++++++++++- refactor/scripts/dash-contract.mjs | 61 ++++++++ refactor/scripts/dash-contract.test.mjs | 14 ++ refactor/tasks.json | 14 +- refactor/third-party.json | 2 +- 11 files changed, 431 insertions(+), 15 deletions(-) create mode 100644 refactor/baselines/dash-control-contract.md create mode 100644 refactor/baselines/dash-control-release.json create mode 100644 refactor/changes/2026-09-12-PKG-DASH-01-contract.md create mode 100644 refactor/scripts/dash-contract.mjs create mode 100644 refactor/scripts/dash-contract.test.mjs diff --git a/refactor/baselines/dash-control-contract.md b/refactor/baselines/dash-control-contract.md new file mode 100644 index 000000000..084fc1e8d --- /dev/null +++ b/refactor/baselines/dash-control-contract.md @@ -0,0 +1,66 @@ +# DASH Control 兼容契约 + +PKG-DASH-01;冻结来源为实际 npm `artplayer-plugin-dash-control@1.1.0` 归档和本分支 +e0d47ea7 的文件。SHA-512、SHA-256、6 个归档成员、manifest、源码和 demo 指纹见 +[发布基线](dash-control-release.json)。registry gitHead 只保留为 registry 声明,不当成 +已证明可重建该 tarball 的源码提交。当前包版本也是 1.1.0,但不等于已发布内容。 + +## SDK 兼容边界 + +已发布 README 指向 dash.js 4.5.2,归档源码调用 getBitrateInfoListFor/getQualityFor/ +setQualityFor。工作区 README 已写仅支持 5.x,并指向 5.2.1;源码改为 representations +和 setRepresentationForTypeById。因此它们不是只改格式:归一化工厂哈希也不同。 +两边 manifest 和声明相同;README、三种构建产物不同。 + +[官方 4→5 迁移说明](https://dashif.org/dash.js/pages/developers/migration-guides/4-to-5.html) +确认这些旧方法被替换。按本次旧 API 兼容目标,迁移需通过能力适配保留发布版 4.x 调用, +同时保留工作区 5.x 按稳定 ID 选择的修复;不能把现有“仅 5.x”文案当成允许破坏 npm 旧 +消费者的授权。也不以适配器存在就声称所有 SDK 小版本可用,05 至少固定 4.5.2/5.2.1 +实际浏览器媒体矩阵,并明确其他版本范围。 + +5.x selector 的 value 是首次查询列表下标,真正选择必须使用保存的 representation ID, +因为 bitrate 过滤后下标可以变化;不能换成 absoluteIndex。[官方手动选择说明](https://dashif.org/dash.js/pages/usage/abr/manual-quality-selection.html) +也说明 ID 与列表索引的区别。既有 5 项 Node 回归保护过滤、手动高亮、排序、Auto 和 ID='auto'。 + +## 公开行为 + +| 边界 | 发布版 / 工作区现状 | 迁移要求 | +| --- | --- | --- | +| 工厂 | artplayerPluginDashControl(option = {}) 返回同步安装函数 | 默认调用和函数形状保留 | +| 安装 | 不立即读 DASH;捕获 template.$video,订阅 ready/restart | 保留延迟绑定,不能要求先安装某个新核心能力 | +| 返回 | name='artplayerPluginDashControl'、update() 同步且无返回值 | 不改成 Promise,不新增必传 update 参数 | +| 宿主 | 使用调用方 art.dash,核对 getVideoElement() 与视频相同 | 不创建/销毁调用方 SDK;丢失实例的诊断与兼容性另测 | +| 配置 | quality/audio 各自 control/setting/title/auto/getName,界面默认不开 | 保留原对象调用及 JS 真值默认值 | +| 默认标签 | quality: height+'p';audio: track.lang 或 track.id;Auto/Quality/Audio | 精确保留,回调接收原 SDK 对象 | +| quality | 按 html 去重、反向原下标排序、尾部 Auto;选手动先关闭 ABR,再选级别 | v4 保持 qualityIndex;v5 保持稳定 ID,不混用两种键 | +| audio | 按 html 去重,value 是原 track 对象,无 Auto 项 | setCurrentTrack 必须收到原对象,不复制 track | +| 选中返回 | 设置 notice,依次 controls.check 和 setting.check,返回 item.html | 已有同步返回、通知格式、check 参数身份保持 | +| 表面 | dash-quality/dash-audio,右侧 control padding 0 10px;setting width=200 | 保留名称、图标、选择器格式和更新入口 | +| 函数引用 | getName 被作为普通局部函数调用,参数为当前 SDK 对象 | 不增加 this 绑定或修改回调输入 | +| SDK 设置 | 仅更新 streaming.abr.autoSwitchBitrate.video;音轨选择不改音频 ABR | 不覆盖其他调用方配置 | + +没有独立自有 CSS 或额外媒体/worker/network 资源。audio.svg 与 quality.svg 是设置菜单图标。 +插件安装时创建的监听器和 UI 应由插件释放;SDK 实例由调用方 customType 管理。 + +## 类型与分发 + +默认导出声明仍要求 option;运行时允许不传。Config.getName 参数只写 object,不能直接 +表达示例中的 height/lang;Config/Option/Result 没有可导入命名类型,也没有格式专属桥接。 +04 应验证旧编译器和已有参数推断,在兼容入口之外提供准确类型,不直接收紧旧回调。 + +保留 main/module/types/legacy、exports 的 '.' 与 './legacy' 以及原文件名。归档真实含 +README、package.json、三种 JS 和一份 .d.ts。包没有 SDK runtime dependency 或 peer +version 约束,不能因内部适配要求老消费者安装新核心。计划最终版本仍是 2.0.0,尚未升级。 + +## 已发现的待修复项 + +- DASH-SDK-01:发布版 4.x 与当前仅 5.x 的差异,02 建立两代 SDK 行为用例,03 做能力适配。 +- DASH-LIFE-01:只订阅 ready/restart,没有销毁/过期回调守卫;空列表直接 return,旧 UI 保留。 + 源或 SDK 被替换后,保留的选择器回调仍可调用旧 SDK。02 复现,03 整理所有权与移除。 +- DASH-STATE-01:当前 qualityId 真值判断可能漏掉 0;音轨只按对象身份匹配,克隆的 currentTrack + 没有选中标志;按标签去重可能隐去实际当前项。用实际 SDK 数据和受控边界分别验证。 +- DASH-TYPE-01:默认参数和 getName 类型不准确,旧解析与格式桥接需验证。 +- DASH-DEMO-01:示例重复 customType 会累积 destroy 回调,旧/新 SDK 版本的示例入口需一致。 + +这些多数是源码事实,不把未执行的媒体场景写成已复现缺陷。当前仅完成来源/契约清点, +实际 SDK 4/5 的播放、换轨、换源、生命周期、npm 安装和 8082 demo 属于后续任务。 diff --git a/refactor/baselines/dash-control-release.json b/refactor/baselines/dash-control-release.json new file mode 100644 index 000000000..6a019021c --- /dev/null +++ b/refactor/baselines/dash-control-release.json @@ -0,0 +1,135 @@ +{ + "schemaVersion": 1, + "capturedAt": "2026-09-11T20:20:05.888Z", + "sourceCommit": "e0d47ea74b25e8fc999d53d27605bf5ed551c106", + "release": { + "name": "artplayer-plugin-dash-control", + "version": "1.1.0", + "metadataUrl": "https://registry.npmjs.org/artplayer-plugin-dash-control/1.1.0", + "tarball": "https://registry.npmjs.org/artplayer-plugin-dash-control/-/artplayer-plugin-dash-control-1.1.0.tgz", + "integrity": "sha512-EssRkzYXWcln3a6nAzi4ESacBhzMih8s0bzdjXuvBBCh2UFmvK99ITD5E+0cuU8i5ZzIQGQSdce1/gANTrX0BQ==", + "sha256": "f4047a569d99b6a7de57b3719b7ad40109f2a1d360566a840f50ab32aacefcd9", + "archiveBytes": 3385, + "registryGitHead": "daf133b22630b4a0eecfa3336bbddab0e9119d96", + "files": { + "package/README.md": "98982f1123ff9a11f5d3d9a52da6e8fea9cd0989f7465605c27b0f9a46f2dbcd", + "package/dist/artplayer-plugin-dash-control.js": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "package/dist/artplayer-plugin-dash-control.legacy.js": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "package/dist/artplayer-plugin-dash-control.mjs": "e32bf08c33a481781883067dcc639f0495c9dcb8a88b91be13736370f4932fc4", + "package/package.json": "556e2adc479bab36c5973de617c0b57c5d86aa8425643a1952ab04738e9ce927", + "package/types/artplayer-plugin-dash-control.d.ts": "e15ef683e0c335d81531587828593325e037efe7ed29c022477cd14fe71aebba" + }, + "manifest": { + "name": "artplayer-plugin-dash-control", + "version": "1.1.0", + "description": "Dash control plugin for ArtPlayer", + "author": "Harvey Zhao ", + "license": "MIT", + "homepage": "https://artplayer.org", + "repository": { + "type": "git", + "url": "git+https://github.com/zhw2590582/ArtPlayer.git" + }, + "bugs": { + "url": "https://github.com/zhw2590582/ArtPlayer/issues" + }, + "keywords": [ + "html5", + "video", + "player" + ], + "exports": { + ".": { + "types": "./types/artplayer-plugin-dash-control.d.ts", + "import": "./dist/artplayer-plugin-dash-control.mjs", + "require": "./dist/artplayer-plugin-dash-control.js" + }, + "./legacy": { + "types": "./types/artplayer-plugin-dash-control.d.ts", + "import": "./dist/artplayer-plugin-dash-control.legacy.js", + "require": "./dist/artplayer-plugin-dash-control.legacy.js" + } + }, + "main": "./dist/artplayer-plugin-dash-control.js", + "module": "./dist/artplayer-plugin-dash-control.mjs", + "types": "./types/artplayer-plugin-dash-control.d.ts", + "legacy": "./dist/artplayer-plugin-dash-control.legacy.js", + "browserslist": "last 1 Chrome version" + } + }, + "source": { + "packages/artplayer-plugin-dash-control/src/index.js": "aaa7975115a6d2677f4c8d71182954edbf69a12f9ffe8bee86afa94f6b5615e8", + "packages/artplayer-plugin-dash-control/src/audio.svg": "dbee5ce7487c9f8ec927e5286188cf9439345bbe947b6c06c32c07193e01e0be", + "packages/artplayer-plugin-dash-control/src/quality.svg": "4666a4c01764422f9008e442f5a889ff9691bb0386083d673943a0718f4d680c", + "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts": "e15ef683e0c335d81531587828593325e037efe7ed29c022477cd14fe71aebba", + "packages/artplayer-plugin-dash-control/package.json": "556e2adc479bab36c5973de617c0b57c5d86aa8425643a1952ab04738e9ce927", + "packages/artplayer-plugin-dash-control/README.md": "4ef644a812059ece0afcb1cd0c8a8c64fa2532dc38a5ce87d4f1538de6a7788e", + "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.js": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5", + "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.legacy.js": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5", + "packages/artplayer-plugin-dash-control/dist/artplayer-plugin-dash-control.mjs": "2cc039698627a6baaa5da1cc50cc5db5230ae5d31152b1217dda62e38c67aa3e", + "docs/assets/example/dash.control.js": "edc4326f03262a222e06bb780aab05c903ee162829dec60970e738c1e260eef2", + "test/dash-control.test.js": "2a123bbd93659c053ab206f09ab489d04380b222885179203c0bfb401590b35d" + }, + "comparisons": [ + { + "file": "README.md", + "publishedLF": "98982f1123ff9a11f5d3d9a52da6e8fea9cd0989f7465605c27b0f9a46f2dbcd", + "workspaceLF": "4ef644a812059ece0afcb1cd0c8a8c64fa2532dc38a5ce87d4f1538de6a7788e", + "equal": false + }, + { + "file": "dist/artplayer-plugin-dash-control.js", + "publishedLF": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "workspaceLF": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5", + "equal": false + }, + { + "file": "dist/artplayer-plugin-dash-control.legacy.js", + "publishedLF": "f0cbbcf8badeaca13c05ae178932818cb52fbce37ed504ac0e62160b9c7e1145", + "workspaceLF": "248aa1162ffb2480965f8e12d766ba6b7d2abfc2d80e65e8a6e8db6bdfa750c5", + "equal": false + }, + { + "file": "dist/artplayer-plugin-dash-control.mjs", + "publishedLF": "e32bf08c33a481781883067dcc639f0495c9dcb8a88b91be13736370f4932fc4", + "workspaceLF": "2cc039698627a6baaa5da1cc50cc5db5230ae5d31152b1217dda62e38c67aa3e", + "equal": false + }, + { + "file": "package.json", + "publishedLF": "556e2adc479bab36c5973de617c0b57c5d86aa8425643a1952ab04738e9ce927", + "workspaceLF": "556e2adc479bab36c5973de617c0b57c5d86aa8425643a1952ab04738e9ce927", + "equal": true + }, + { + "file": "types/artplayer-plugin-dash-control.d.ts", + "publishedLF": "e15ef683e0c335d81531587828593325e037efe7ed29c022477cd14fe71aebba", + "workspaceLF": "e15ef683e0c335d81531587828593325e037efe7ed29c022477cd14fe71aebba", + "equal": true + } + ], + "functionComparison": { + "published": "1dbeb07766c8599076f232508ad1c50fc5b2a5ba9232e86bd715c368f01b4561", + "workspace": "497b27daf5c35d134e87905464a45a5681d9321ecf6b509ab3e0f225f2b57c58", + "equal": false + }, + "sdkBoundary": { + "publishedDemo": "dashjs 4.5.2", + "workspaceDemo": "dashjs 5.2.1", + "publishedQuality": [ + "getBitrateInfoListFor", + "getQualityFor", + "setQualityFor" + ], + "workspaceQuality": [ + "getRepresentationsByType", + "getCurrentRepresentationForType", + "setRepresentationForTypeById" + ], + "required": "Preserve released v4 callers with a capability adapter while retaining the workspace v5 stable-ID correction. No claim that SDK playback has already been validated." + }, + "sources": [ + "https://dashif.org/dash.js/pages/developers/migration-guides/4-to-5.html", + "https://dashif.org/dash.js/pages/usage/abr/manual-quality-selection.html" + ] +} diff --git a/refactor/changes/2026-09-12-PKG-DASH-01-contract.md b/refactor/changes/2026-09-12-PKG-DASH-01-contract.md new file mode 100644 index 000000000..3bfcb1d67 --- /dev/null +++ b/refactor/changes/2026-09-12-PKG-DASH-01-contract.md @@ -0,0 +1,21 @@ +# PKG-DASH-01:发布版与两代 SDK 兼容边界 + +起点 e0d47ea7,核实工作区干净。取得并校验 npm artplayer-plugin-dash-control 1.1.0 的 +真实归档,保存 6 文件指纹、公开 manifest、工作区源码/demo/声明指纹和归一化函数比较。 +具体清单见 [契约](../baselines/dash-control-contract.md) 与 [发布基线](../baselines/dash-control-release.json)。 + +发现同一版本号下:已发布包依赖 dash.js 4.x 方法,当前源码仅 5.x;README 与产物不同, +manifest/声明相同。根据已授权兼容目标,03 要同时适配发布版 4.x 调用和当前 5.x 稳定 ID +选择,不删除已存在的 5 项过滤回归。05 同时验证 4.5.2/5.2.1;不扩大成尚未验证的版本承诺。 + +新增可重跑 dash-contract 校验及两项测试,保护归档、历史源码、文件差异和函数变化。 +已有 5 项 DASH Node 测试与这两项基线检查通过。这里只是受控/归档验证,不是 SDK 浏览器验收。 +没有更改生产源码、公开声明、README、demo、依赖或构建产物;没有运行整套媒体测试。 + +SDK 适配、生命周期、UI 清理/高亮、类型和 demo 差异已登记后续负责人。任务 01 的范围是 +核对兼容契约,完成不代表这些问题已修复。后续从 PKG-DASH-02 扩展测试开始。 +本任务独立本地提交;不推送、不发布。回退仅撤回本次基线/文档和验证脚本,生产行为不变。 + +最终验证:新增两项与现有五项共 7 项通过;完整 test:baseline 37 项通过;新增脚本定向 +lint、风险校验、任务 DAG/文档链接检查通过。本任务没有运行完整 CI 或真实浏览器, +因为没有生产修改;后续实现仍须执行对应完整门槛。当前 217 项:71 done、3 doing、143 todo。 diff --git a/refactor/plan.md b/refactor/plan.md index 42c7ad3b6..4ec126eb7 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 217 项,范围 22 个包及工作区/示例。 -状态:todo 144 / doing 3 / blocked 0 / done 70 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 143 / doing 3 / blocked 0 / done 71 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -201,11 +201,11 @@ | ID | 范围 / 步骤 | 前置依赖 | 交付物 | 验收条件 | 风险 | 状态 | | --- | --- | --- | --- | --- | --- | --- | -| PKG-DASH-01 | artplayer-plugin-dash-control
核对包契约与历史用法 | BASE-05 | quality/audio、representation ID/Auto、getName 和 update | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | todo | -| PKG-DASH-02 | artplayer-plugin-dash-control
建立特有行为与错误测试 | PKG-DASH-01, ENG-03, ENG-05 | 保留已有 5 项回归,补音轨/空列表/过滤/换源 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | todo | -| PKG-DASH-03 | artplayer-plugin-dash-control
整理内部职责与资源 | PKG-DASH-02, CORE-11, CORE-14 | 稳定 ID 映射、ABR 状态与 UI 清理职责分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | +| PKG-DASH-01 | artplayer-plugin-dash-control
核对包契约与历史用法 | BASE-05 | quality/audio、representation ID/Auto、getName 和 update | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | H | done | +| PKG-DASH-02 | artplayer-plugin-dash-control
建立特有行为与错误测试 | PKG-DASH-01, ENG-03, ENG-05 | 保留已有 5 项稳定 ID 回归,补发布版 SDK 4.x/当前 5.x、音轨/空列表/过滤/换源与关闭引用 | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | H | todo | +| PKG-DASH-03 | artplayer-plugin-dash-control
整理内部职责与资源 | PKG-DASH-02, CORE-11, CORE-14 | 能力适配保留 SDK 4.x 与 5.x,稳定 ID 映射、ABR 状态、UI 清理和生命周期职责分离 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | H | todo | | PKG-DASH-04 | artplayer-plugin-dash-control
迁移自有源码和公开类型 | PKG-DASH-03, ENG-04, ENG-06, CORE-07 | dash.js adapter、selector 和回调类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | H | todo | -| PKG-DASH-05 | artplayer-plugin-dash-control
验证新旧核心和组合 | PKG-DASH-04, CORE-22 | 本地 DASH 实际 representation、高亮、恢复 Auto 和换源 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | +| PKG-DASH-05 | artplayer-plugin-dash-control
验证新旧核心和组合 | PKG-DASH-04, CORE-22 | 固定 dash.js 4.5.2/5.2.1 与本地 DASH 实际清晰度/音轨、高亮、Auto 和换源组合 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | H | todo | | PKG-DASH-06 | artplayer-plugin-dash-control
验证分发并同步文档 | PKG-DASH-05, ENG-07 | dash.control.js、支持的 dash.js 版本与产物 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | H | todo | ## 5 包迁移:artplayer-plugin-multiple-subtitles @@ -479,3 +479,4 @@ - PKG-HLS-03: [记录](changes/2026-09-12-PKG-HLS-03-modules.md) [记录](baselines/hls-modules-validation.json) [记录](hls-validation.md) - PKG-HLS-04: [记录](changes/2026-09-12-PKG-HLS-04-types.md) [记录](baselines/hls-types-validation.json) [记录](hls-validation.md) - PKG-HLS-SDK-01: [记录](changes/2026-09-12-PKG-HLS-SDK-01-integration.md) [记录](baselines/hls-sdk-validation.json) [记录](baselines/hls-sdk-diagnostics.json) +- PKG-DASH-01: [记录](changes/2026-09-12-PKG-DASH-01-contract.md) [记录](baselines/dash-control-contract.md) [记录](baselines/dash-control-release.json) diff --git a/refactor/progress.md b/refactor/progress.md index fae58b2f9..0924db3fc 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,17 @@ # 进度与证据 +## 最新完成:DASH-01 发布版与 SDK 兼容契约 + +已校验 DASH Control 1.1.0 的实际 npm 归档及 6 个成员。发现发布版使用 dash.js 4.x +方法,而当前同版本工作区只支持 5.x,不能把两者视为等价。后续 02/03/05 已明确保留 +4.x 调用并适配 5.x 稳定 ID,至少验证原示例 4.5.2 和当前示例 5.2.1。 + +新增契约校验及旧有 DASH 回归共 7 项通过,完整基线 37 项通过;无生产/依赖/类型/ +构建修改。SDK、生命周期/过期 UI、当前项定位、类型和示例问题已登记。当前 217 项: +71 done、3 doing、143 todo。下一项 PKG-DASH-02;Audio/Chapter/HLS 开放风险仍保留。 +详见 [契约](baselines/dash-control-contract.md) 和 [本次变更](changes/2026-09-12-PKG-DASH-01-contract.md)。 +本任务独立本地提交,不推送、不发布;全项目目标仍未完成。 + ## 当前实施:Chapter-05 组合验证与长标题修复检查点 正式 main/legacy 的章节基础及新旧核心/插件组合各 102 项通过,完整 CI 599 项通过。 diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 547603721..5d06aa8c0 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -34,7 +34,7 @@ | VENDOR-07 | open / 待取证 | vconsole 来源、版本与许可闭环 | SITE-01 | | VENDOR-08 | open / 待取证 | console-bundle 来源、版本与许可闭环 | SITE-01 | | SDK-01 | open / 源码/产物事实 | hls.js 实际集成验证范围 | PKG-HLS-05, EX-03 | -| SDK-02 | open / 待取证 | dash.js 实际集成验证范围 | PKG-DASH-01, EX-03 | +| SDK-02 | open / 待取证 | dash.js 实际集成验证范围 | PKG-DASH-01, EX-03, PKG-DASH-05 | | SDK-03 | open / 待取证 | flv.js 实际集成验证范围 | EX-03 | | SDK-04 | open / 待取证 | mpegts.js 实际集成验证范围 | EX-03 | | SDK-05 | open / 待取证 | webtorrent 实际集成验证范围 | EX-03 | @@ -135,3 +135,8 @@ | AUDIO-BUFFER-01 | open / 已复现 | Windows WebKit 原生媒体无法在受限响应下推进至真实缓冲 | PKG-AUDIO-05 | | CHAPTER-LAYOUT-01 | resolved / 已复现 | 长章节标题超出窄播放器进度条宽度 | PKG-CHAPTER-05 | | CHAPTER-TIMING-01 | open / 已复现 | Windows WebKit 清晰度切换曾在等待窗口内未见 restart,稍后状态恢复 | PKG-CHAPTER-05 | +| DASH-SDK-01 | open / 源码/产物事实 | npm 发布版 4.x 与工作区仅 5.x 的 SDK 接口不兼容 | PKG-DASH-02, PKG-DASH-03, PKG-DASH-05 | +| DASH-LIFE-01 | open / 源码/产物事实 | DASH 空拓扑残留 UI,监听和旧选择回调缺乏关闭守卫 | PKG-DASH-02, PKG-DASH-03 | +| DASH-STATE-01 | open / 源码/产物事实 | DASH 当前项高亮使用真值 ID、音轨身份和标签去重,边界待核实 | PKG-DASH-02, PKG-DASH-03, PKG-DASH-05 | +| DASH-TYPE-01 | open / 源码/产物事实 | DASH 类型未表达默认工厂参数、SDK 回调对象和现代模块入口 | PKG-DASH-04 | +| DASH-DEMO-01 | open / 源码/产物事实 | DASH 示例仅新 SDK 且重复安装会累积销毁回调 | PKG-DASH-06 | diff --git a/refactor/risks.json b/refactor/risks.json index 58aa0c3f1..0516fd3cd 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -624,13 +624,15 @@ "status": "open", "owners": [ "PKG-DASH-01", - "EX-03" + "EX-03", + "PKG-DASH-05" ], "evidence": [ "packages/artplayer-plugin-dash-control/src/index.js", - "docs/assets/example/dash.control.js" + "docs/assets/example/dash.control.js", + "refactor/baselines/dash-control-contract.md" ], - "compatibleResolution": "Caller supplies art.dash; homepage demo loads dash.js 5.2.1. Preserve representation IDs, Auto/manual and audio topology.", + "compatibleResolution": "Caller supplies art.dash. Preserve published 4.x callers (demo 4.5.2) and workspace 5.x stable representation IDs (demo 5.2.1); verify actual media and SDK lifecycle in PKG-DASH-05.", "closureCriteria": "固定 SDK/外部资源版本和环境;成功/失败/切换/销毁有真实证据,设备和网络限制逐项记录。" }, { @@ -2828,6 +2830,101 @@ ], "compatibleResolution": "记录原生事件和绝对/相对时序,进一步区分媒体驱动耗时、夹具和核心源操作;未证明原因前不改核心、不扩大超时或重复到绿灯。", "closureCriteria": "确认先前超时的来源并有对应可靠回归;后续单次通过不足以关闭。" + }, + { + "id": "DASH-SDK-01", + "title": "npm 发布版 4.x 与工作区仅 5.x 的 SDK 接口不兼容", + "confirmation": "source-observed", + "status": "open", + "owners": [ + "PKG-DASH-02", + "PKG-DASH-03", + "PKG-DASH-05" + ], + "evidence": [ + "refactor/baselines/dash-control-contract.md", + "refactor/baselines/dash-control-release.json", + "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", + "docs/assets/example/dash.control.js" + ], + "compatibleResolution": "按方法能力适配两代已存在用法,保留 v5 稳定 ID,不能要求旧消费者改用新 SDK。", + "closureCriteria": "归档旧用法、v4/v5 受控及实际媒体矩阵通过,清晰度键不混用。" + }, + { + "id": "DASH-LIFE-01", + "title": "DASH 空拓扑残留 UI,监听和旧选择回调缺乏关闭守卫", + "confirmation": "source-observed", + "status": "open", + "owners": [ + "PKG-DASH-02", + "PKG-DASH-03" + ], + "evidence": [ + "refactor/baselines/dash-control-contract.md", + "refactor/baselines/dash-control-release.json", + "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", + "docs/assets/example/dash.control.js" + ], + "compatibleResolution": "仅释放插件自有 ready/restart/destroy 订阅和 UI,SDK 仍由调用方拥有;替换后旧回调不写旧实例。", + "closureCriteria": "空列表、换源/SDK、销毁后引用、安装异常有回归,其他实例与 SDK 不被误释放。" + }, + { + "id": "DASH-STATE-01", + "title": "DASH 当前项高亮使用真值 ID、音轨身份和标签去重,边界待核实", + "confirmation": "source-observed", + "status": "open", + "owners": [ + "PKG-DASH-02", + "PKG-DASH-03", + "PKG-DASH-05" + ], + "evidence": [ + "refactor/baselines/dash-control-contract.md", + "refactor/baselines/dash-control-release.json", + "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", + "docs/assets/example/dash.control.js" + ], + "compatibleResolution": "验证零 ID、重复标签和克隆音轨当前项,在保持原展示语义时按实际可选项定位高亮。", + "closureCriteria": "有受控和真实 SDK 证据说明选中的 representation/track 与 UI 一致。" + }, + { + "id": "DASH-TYPE-01", + "title": "DASH 类型未表达默认工厂参数、SDK 回调对象和现代模块入口", + "confirmation": "source-observed", + "status": "open", + "owners": [ + "PKG-DASH-04" + ], + "evidence": [ + "refactor/baselines/dash-control-contract.md", + "refactor/baselines/dash-control-release.json", + "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", + "docs/assets/example/dash.control.js" + ], + "compatibleResolution": "保留旧类型推断,提供准确的 SDK 适配/回调契约和兼容格式入口,不以 any 掩盖源码。", + "closureCriteria": "严格源码、旧/新编译器消费者与隔离产物类型验证通过。" + }, + { + "id": "DASH-DEMO-01", + "title": "DASH 示例仅新 SDK 且重复安装会累积销毁回调", + "confirmation": "source-observed", + "status": "open", + "owners": [ + "PKG-DASH-06" + ], + "evidence": [ + "refactor/baselines/dash-control-contract.md", + "refactor/baselines/dash-control-release.json", + "packages/artplayer-plugin-dash-control/src/index.js", + "packages/artplayer-plugin-dash-control/types/artplayer-plugin-dash-control.d.ts", + "docs/assets/example/dash.control.js" + ], + "compatibleResolution": "验证真实 SDK 销毁和示例重载路径,对齐 v4/v5 文档与示例,不让旧监听误操作新实例。", + "closureCriteria": "真实 8082 demo、旧/新 SDK 路径与 tarball 产物一致,监听和 SDK 所有权可验证。" } ] } diff --git a/refactor/scripts/dash-contract.mjs b/refactor/scripts/dash-contract.mjs new file mode 100644 index 000000000..03e079bc1 --- /dev/null +++ b/refactor/scripts/dash-contract.mjs @@ -0,0 +1,61 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import { fileURLToPath } from 'node:url' +import { transform } from 'esbuild' +import ts from 'typescript' +import { archiveFiles, ensureArchive, hash, readMember, refactorDir } from './releases.mjs' + +const root = path.resolve(refactorDir, '..') +export const normalizeLF = value => value.toString().replace(/\r\n/g, '\n') + +export async function dashFunctionHash(code) { + const source = ts.createSourceFile('dash.js', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS) + assert.equal(source.parseDiagnostics.length, 0, 'Invalid DASH source') + const functions = ['uniqBy', 'artplayerPluginDashControl'].map((name) => { + const matches = source.statements.filter(item => ts.isFunctionDeclaration(item) && item.name?.text === name) + assert.equal(matches.length, 1, `Expected one function: ${name}`) + return matches[0].getText(source).replace(/^export\s+default\s+/, '') + }) + const normalized = await transform(functions.join('\n'), { loader: 'js', target: 'es2020', minifySyntax: true, minifyWhitespace: true }) + return hash(normalized.code) +} + +export async function verifyDashContract() { + const baseline = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/dash-control-release.json'), 'utf8')) + const { release } = baseline + const archive = await ensureArchive(release) + assert.deepEqual(archiveFiles(archive), Object.keys(release.files).sort()) + for (const [member, expected] of Object.entries(release.files)) + assert.equal(hash(readMember(archive, member)), expected, `Changed release member: ${member}`) + const manifest = JSON.parse(readMember(archive, 'package/package.json')) + assert.deepEqual(manifest, release.manifest) + for (const entry of [manifest.main, manifest.module, manifest.types, manifest.legacy]) + assert(Object.hasOwn(release.files, `package/${entry.replace(/^\.\//, '')}`)) + assert(/^[a-f\d]{40}$/.test(baseline.sourceCommit)) + const historical = new Map() + for (const [file, expected] of Object.entries(baseline.source)) { + const content = normalizeLF(execFileSync('git', ['show', `${baseline.sourceCommit}:${file}`], { cwd: root })) + assert.equal(hash(content), expected, `Changed historical source: ${file}`) + historical.set(file, content) + } + for (const comparison of baseline.comparisons) { + const published = hash(normalizeLF(readMember(archive, `package/${comparison.file}`))) + const workspace = hash(historical.get(`packages/${release.name}/${comparison.file}`)) + assert.equal(published, comparison.publishedLF) + assert.equal(workspace, comparison.workspaceLF) + assert.equal(published === workspace, comparison.equal) + } + const published = await dashFunctionHash(readMember(archive, `package/${manifest.module.replace(/^\.\//, '')}`).toString()) + const workspace = await dashFunctionHash(historical.get(`packages/${release.name}/src/index.js`)) + assert.equal(published, baseline.functionComparison.published) + assert.equal(workspace, baseline.functionComparison.workspace) + assert.equal(published === workspace, baseline.functionComparison.equal) + console.log(`Verified ${release.name}@${release.version}: archive, ${Object.keys(release.files).length} members, historical source and explicit v4/v5 factory difference`) + return { baseline, archive } +} + +if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) + await verifyDashContract() diff --git a/refactor/scripts/dash-contract.test.mjs b/refactor/scripts/dash-contract.test.mjs new file mode 100644 index 000000000..7e4dda7ed --- /dev/null +++ b/refactor/scripts/dash-contract.test.mjs @@ -0,0 +1,14 @@ +import assert from 'node:assert/strict' +// eslint-disable-next-line test/no-import-node-test -- Frozen release checks use the built-in runner. +import test from 'node:test' +import { dashFunctionHash, verifyDashContract } from './dash-contract.mjs' + +test('DASH Control release and its pre-refactor v4/v5 difference remain immutable', verifyDashContract) + +test('DASH function comparison ignores formatting but detects changed SDK calls', async () => { + const original = 'function uniqBy(a) { return a } export default function artplayerPluginDashControl(dash) { return dash.getQualityFor("video") }' + const formatted = original.replace('export default ', '').replaceAll(' }', ';\n }') + assert.equal(await dashFunctionHash(original), await dashFunctionHash(formatted)) + assert.notEqual(await dashFunctionHash(original), await dashFunctionHash(original.replace('getQualityFor', 'getCurrentRepresentationForType'))) + await assert.rejects(dashFunctionHash('function unrelated() {}'), /Expected one function/) +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index dc40ef0bf..9939cadde 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -1970,11 +1970,15 @@ "dependsOn": [ "BASE-05" ], - "status": "todo", + "status": "done", "risk": "H", "deliverable": "quality/audio、representation ID/Auto、getName 和 update", "acceptance": "源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结", - "evidence": [] + "evidence": [ + "changes/2026-09-12-PKG-DASH-01-contract.md", + "baselines/dash-control-contract.md", + "baselines/dash-control-release.json" + ] }, { "id": "PKG-DASH-02", @@ -1990,7 +1994,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "保留已有 5 项回归,补音轨/空列表/过滤/换源", + "deliverable": "保留已有 5 项稳定 ID 回归,补发布版 SDK 4.x/当前 5.x、音轨/空列表/过滤/换源与关闭引用", "acceptance": "旧版本行为可重跑,成功/失败/切源/销毁有必要断言", "evidence": [] }, @@ -2008,7 +2012,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "稳定 ID 映射、ABR 状态与 UI 清理职责分离", + "deliverable": "能力适配保留 SDK 4.x 与 5.x,稳定 ID 映射、ABR 状态、UI 清理和生命周期职责分离", "acceptance": "结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过", "evidence": [] }, @@ -2044,7 +2048,7 @@ ], "status": "todo", "risk": "H", - "deliverable": "本地 DASH 实际 representation、高亮、恢复 Auto 和换源", + "deliverable": "固定 dash.js 4.5.2/5.2.1 与本地 DASH 实际清晰度/音轨、高亮、Auto 和换源组合", "acceptance": "最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成", "evidence": [] }, diff --git a/refactor/third-party.json b/refactor/third-party.json index d61111c24..423489cef 100644 --- a/refactor/third-party.json +++ b/refactor/third-party.json @@ -1048,7 +1048,7 @@ "packages/artplayer-plugin-dash-control/src/index.js", "docs/assets/example/dash.control.js" ], - "contract": "Caller supplies art.dash; homepage demo loads dash.js 5.2.1. Preserve representation IDs, Auto/manual and audio topology.", + "contract": "Caller owns art.dash. Published plugin 1.1.0 uses dash.js 4.x (demo 4.5.2); workspace uses 5.x (demo 5.2.1). Preserve both through capability adapters and v5 stable representation IDs; actual SDK playback remains PKG-DASH-05.", "validationStatus": "Source integration observed; complete real SDK/service/device matrix not yet verified.", "demoPathsSource": "refactor/baselines/demo-inventory.json" },