的设计与实现)
IPTVnator Web 播放器共享控制设置Shared Controls的设计与实现【免费下载链接】iptvnator:tv: Cross-platform IPTV player application with multiple features, such as support of m3u and m3u8 playlists, favorites, TV guide, TV archive/catchup and more.项目地址: https://gitcode.com/GitHub_Trending/ip/iptvnator导读IPTVnator 在app-player-controls共享控制层之上为 HTML5、Video.js、ArtPlayer 三种 Web 播放引擎引入了统一的控件体系并通过一个持久化的实验性设置项让用户自行选择是否启用。本文以 web-player-shared-controls-setting 设计文档 为核心骨架结合仓库内SettingsStore、设置表单、播放器宿主组件与测试用例的源码实现完整还原该设置从“设计契约 → 表单 UI → 会话级不可变快照”的落地链路。读完本文你将理解为什么该设置只面向 Web 播放器而不影响 Embedded MPV 与外部播放器、设置如何在无需重启应用的前提下作用于下一次播放会话、以及控件模式在单个会话生命周期内保持不可变的底层原因。背景四条播放路径与共享控制层在讨论设置项之前需要先理解 IPTVnator 当前的播放架构。共享的app-player-controls呈现层已经集成了四条播放路径播放路径控件策略Embedded MPVframe-copy 帧拷贝无条件使用app-player-controls视频渲染进 DOM canvas必须使用 DOM 控件Embedded MPVnative-view 原生视图使用合成器安全的 legacy 控件坞DOM 控件无法可靠叠放在原生表面之上HTML5hls.js可选用共享控件Video.js可选用共享控件ArtPlayer可选用共享控件其中三个 Web 引擎共享同一个名为WEB_PLAYER_SHARED_CONTROLS的注入令牌InjectionToken该令牌定义在 web-player-controls.flag.ts。设计文档描述的初始状态是该令牌从编译期常量WEB_PLAYER_SHARED_CONTROLS_ENABLED false解析意味着要启用共享控件必须先改代码、重新构建并重启应用无法进行低成本的灰度验证。设计文档进一步指出一个关键事实Settings 路由不会保持一个活跃的播放器挂载。因此设置变更不需要在运行中的引擎上即时生效也不需要保留播放位置——只要在“下一次播放会话创建”时应用保存的选择即可。这正是整个运行时数据流设计的出发点。设计目标与非目标Goals目标在 Playback 区段增加一个实验性复选框为 HTML5、Video.js、ArtPlayer 开启共享控件将选择持久化到权威的SettingsStore让保存的选择在不重启、不重载应用的情况下作用于下一次 Web 播放会话保持控件模式在单个播放器会话生命周期内不可变保持默认关闭default-off行为并与已存在的旧版存储设置兼容保持 Embedded MPV 与外部播放器的行为完全不变。Non-goals非目标不将共享 Web 控件设为默认不在 legacy 与 shared 控件之间动态切换已激活的播放器不为 HTML5、Video.js、ArtPlayer 分别提供独立偏好不让 Embedded MPV native-view 使用 DOM 覆盖层不允许 frame-copy Embedded MPV 回退到 legacy 控件坞不控制外部 MPV/VLC 进程的原生 UI。这组“非目标”很重要它明确划定了该设置的职责边界避免因过度设计破坏已验证的播放引擎行为。Embedded MPV 语义为什么这是“Web 播放器专属”设置新复选框被刻意设计为 Web 播放器专属原因在于 Embedded MPV 的两条渲染路径对控件形态有硬性约束Frame-copy 路径视频被上传到渲染进程 canvasapp-player-controls是其必需组成部分。若再给 Embedded MPV 一个共享控件开关只会重复一个本来就必需的功能属于无效选项Native-view 路径视频托管在平台原生表面中DOM 控件无法可靠叠放其上因此合成器安全的 legacy 控件坞仍是必需的。已有的embeddedMpvFrameCopy设置继续负责选择渲染引擎并且仍然需要重启应用才生效——它与新的 Web 播放器控件偏好相互独立、互不干扰。外部 MPV/VLC 进程则始终拥有自己的控件 UI。这一语义从实现上也可以验证WebPlayerViewComponent为WEB_PLAYER_SHARED_CONTROLS提供组件级 provider但只有三个 Web 引擎组件会注入该令牌Embedded MPV 组件不注入令牌因此其控件形态完全由引擎类型驱动frame-copy用共享控件、native-view 用 legacy 坞。候选方案评估设计文档对比了三种方案1. 条件式全局 Web 播放器偏好被选中一个设置统一控制 HTML5、Video.js、ArtPlayer 三者复选框仅在选中其中之一时显示。优点与现有单一 rollout 令牌一致、设置 UI 始终与当前选中的播放器相关、避免暗示该选项会影响 Embedded MPV 或外部播放器。2. 始终可见的“在可用处使用共享控件”偏好被否决对 frame-copy Embedded MPV 是无效操作、对 native-view 不支持、对外部播放器超出应用范围条件语义难以用标签解释清楚。3. 为每个播放引擎提供独立偏好被否决三个 Web 集成刻意共享同一个 rollout 契约逐引擎偏好会徒增设置项与测试组合且当前没有产品需求支撑。结论是一个条件显示的、面向三类 Web 引擎的单一偏好是语义最清晰、实现成本最低的方案。设置契约SettingsStore 与可选布尔字段接口定义在共享设置接口中新增一个可选布尔字段/** * Use IPTVnators shared controls in HTML5, Video.js, and ArtPlayer. * Missing values remain off for compatibility with older saved settings. */ webPlayerSharedControls?: boolean;规范要求规范默认值为false加载不含该字段的旧设置对象时必须规范化normalize为false保存设置时必须将完整解析后的值写回存储。当前仓库中该字段的规范化与序列化实现在 settings-store.service.tsgetSettings()中以store.webPlayerSharedControls?.() ! false输出完整布尔值确保存储中始终写入已解析的完整状态。对应的契约测试位于 settings-store.service.spec.ts覆盖了缺失字段、布尔真值、畸形字符串如false与保存序列化等场景。设计演进说明需要特别指出设计文档撰写时规划的规范默认值是falsedefault-off编译期常量WEB_PLAYER_SHARED_CONTROLS_ENABLED false。而当前仓库的实现已经落地并进一步演进为default-ON默认开启复选框成为“回到厂商原生控件”的退出开关settings-store.service.ts 的DEFAULT_SETTINGS中为webPlayerSharedControls: trueweb-player-controls.flag.ts 中WEB_PLAYER_SHARED_CONTROLS_ENABLED true注释明确“默认开启用户通过Settings.webPlayerSharedControls选择退出”settings-form.utils.ts 表单默认true序列化时value.webPlayerSharedControls ?? true。这一演进同样反映在权威文档中player-controls-contract.md 记录了 “Settings.webPlayerSharedControlsis default-ON: an absent stored value means…”AGENTS.md也同步说明“PersistedSettings.webPlayerSharedControlsis default-ON”。读者在阅读或贡献代码时应以当前仓库的 default-ON 行为为准设计文档中的 default-off 属于方案定稿时的初始契约两者在“规范化、持久化、会话快照”等机制上完全一致。表单契约renderer-only 状态设置表单新增webPlayerSharedControls控件默认false按设计文档当前实现为true。表单的水合hydration、序列化、重置行为、备份/导出以及既有 Save 工作流全部继续使用完整的Settings对象无需新增专用代码路径。这是纯渲染进程状态Electron 主进程行为与 preload IPC 都不需要新增命令。运行时数据流从设置到下一次会话设计文档给出了完整的运行时解析流程SettingsStore加载webPlayerSharedControls缺失时默认为false设置页通过既有 Save 动作更新并持久化该值离开设置页时该路由被销毁——变更期间没有活跃的播放器下一次WebPlayerViewComponent从SettingsStore解析一个组件作用域的布尔快照HTML5、Video.js 或 ArtPlayer 通过WEB_PLAYER_SHARED_CONTROLS接收该快照并恰好构造一套控件系统在该播放器宿主被销毁、新播放会话创建之前快照不会改变。不可变快照的底层原理“不可变”不是实现细节而是 Video.js 与 ArtPlayer 的构造期约束厂商控件、快捷键、手势、插件、源桥source bridge与共享适配器必须原子化配置。如果用一个响应式布尔值只翻转模板、而引擎仍按另一种模式配置就会产生控件系统与引擎状态不一致的撕裂状态。因此实现采用Angular 组件级 provider 工厂。核心代码在 web-player-shared-controls.tsexport function resolveWebPlayerSharedControls(): boolean { const storedValue inject(SettingsStore).webPlayerSharedControls?.(); return typeof storedValue boolean ? storedValue : WEB_PLAYER_SHARED_CONTROLS_ENABLED; }WebPlayerViewComponent在Component.providers中声明该工厂见 web-player-view.component.ts。Angular 会在WebPlayerViewComponent的元素注入器中创建并缓存这个原始布尔值HTML5、Video.js、ArtPlayer 三个子组件因此各自接收到一个不可变的值用于它们共享的构造期分支判断——这恰好满足了“一个会话只渲染/初始化一套控件系统”的约束。而 Embedded MPV 不注入该令牌继续由引擎类型驱动。配套测试 web-player-view.component.shared-controls.spec.ts 验证了两个关键性质修改SettingsStore信号只影响下一次宿主创建已存在的宿主在其存活期间令牌值保持不变。设置 UIPlayback 区段的条件复选框设计文档对 UI 的要求是在 Playback 区段增加一个标准setting-item复用既有 Material 复选框与布局样式不引入新的颜色、间距规则或一次性组件。标签含义Use unified controls for web players (experimental)为 Web 播放器使用统一控件实验性描述含义在 HTML5、Video.js 和 ArtPlayer 中使用 IPTVnator 的共享控件可见性仅当当前选中的播放器为videojs、html5或artplayer时显示隐藏场景Embedded MPV、外部 MPV、VLC 下隐藏为行与复选框提供稳定的测试选择器data-test-idweb-player-shared-controls-setting与data-test-idweb-player-shared-controls-toggle为每个 locale 文件补充相同的标签与描述键保持 i18n 键集合对齐。模板实现当前实现位于 settings-playback-section.component.html条件渲染结构与设计文档完全一致if (isWebPlayerSelected()) { div classsetting-item >pnpm nx build shared-interfaces --skip-nx-cache pnpm nx test services --skip-nx-cache --runInBand \ --testPathPatternssettings-store.service.spec2. 设置表单与 UI表单创建与序列化包含新字段复选框从存储设置水合并包含在 Save 载荷中行为为 HTML5、Video.js、ArtPlayer 时可见Embedded MPV、外部 MPV、VLC 时隐藏所有 locale 的翻译键对齐。pnpm nx test web --skip-nx-cache --runInBand \ --testPathPatternssettings-playback-section.component.spec|settings.component.spec当前仓库的 settings-playback-section.component.spec.ts 与 settings.component.form.spec.ts 即覆盖了上述水合/保存/可见性断言。3. 播放宿主新建的 HTML5、Video.js、ArtPlayer 会话收到保存的布尔快照false保留厂商/原生 UItrue挂载共享控件路径Embedded MPV 选择忽略该 Web 偏好单个会话永远不会同时渲染/初始化两套控件系统。pnpm nx test ui-playback --skip-nx-cache --runInBand \ --testPathPatternsweb-player-view.component.shared-controls pnpm nx test ui-playback --skip-nx-cache4. E2E 验证Web浏览器apps/web-e2e/src/settings.e2e.ts中验证保存后刷新页面复选框保持勾选Electron 桌面端apps/electron-backend-e2e/src/settings.e2e.ts中验证跨应用重启持久化以及“保存后首个 HTML5 会话渲染app-player-controls且原生video[controls]数量为 0”的端到端冒烟断言。pnpm nx run web-e2e:e2e-ci--src/settings.e2e.ts pnpm nx run electron-backend-e2e:e2e-ci--src/settings.e2e.ts5. 全量回归阶梯设计文档要求最终跑通shared-interfaces构建、services/web/ui-playback测试、相关项目 lint、typecheck:web、i18n:check、web生产构建以及 web/Electron 两套 settings E2E全部命令退出码为 0 才视为通过。文档影响与仓库现状设计文档要求同步更新以下文档将“仅编译期默认关闭的 rollout 描述”替换为“持久化的实验性偏好”player-controls-contract.md权威控件契约记录Settings.webPlayerSharedControls的默认值、WEB_PLAYER_SHARED_CONTROLS_ENABLED回退常量与WEB_PLAYER_SHARED_CONTROLS会话快照三者关系AGENTS.md 与 CLAUDE.md保持共享控件描述的同步README.md在 Playback 功能列表中补充“可选统一控件实验性”条目Embedded MPV 架构文档仅在现有 frame-copy 共享控件与 native-view legacy 坞的区分会产生歧义时才需要澄清。当前仓库中该功能已完整落地设置契约libs/services、表单与 UIapps/web/src/app/settings/、播放宿主快照libs/ui/playback/src/lib/web-player-view/、国际化apps/web/src/assets/i18n/以及两端 E2E 覆盖均已就位。如果你想实际体验在 Web 或桌面端打开「设置 → 播放」将播放器切换为 HTML5、Video.js 或 ArtPlayer 之一即可看到该实验性复选框保存后返回播放页面下一次会话即按保存的选择使用 IPTVnator 的统一控件。【免费下载链接】iptvnator:tv: Cross-platform IPTV player application with multiple features, such as support of m3u and m3u8 playlists, favorites, TV guide, TV archive/catchup and more.项目地址: https://gitcode.com/GitHub_Trending/ip/iptvnator创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考