@video-lab/embed-helper 4.1.2 → 4.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,6 +8,12 @@ iframe 里播放器广播出来的契约事件。宿主页面必须允许 `<scri
8
8
 
9
9
  只依赖 [`@video-lab/protocol`](https://www.npmjs.com/package/@video-lab/protocol),无框架依赖。
10
10
 
11
+ 安装包附带[宿主接入 AI Skill](./skills/host-integration/SKILL.md)和
12
+ [静态 iframe URL 参考](./skills/host-integration/references/static-iframe.md)。若使用 npm 包,
13
+ 复制整个 `skills/host-integration/` 到宿主 AI 的 Skill 目录并核对版本;纯 URL 用户可由部署方
14
+ 离线取得与已部署 iframe 应用版本匹配的整套资料。画中画、封面、Loading、QoE 和 403 的
15
+ 模式限制见[功能配方](./skills/host-integration/references/feature-recipes.md)。具体 API 以本 README、导出类型和部署版本为准。
16
+
11
17
  ## 两种用法
12
18
 
13
19
  ### 1. `<script>` 标签(无构建工具)
@@ -162,4 +168,4 @@ iframe 须使用支持该功能、已部署的精确版本;旧 iframe 可能
162
168
 
163
169
  静态 iframe 使用 `createPageFullscreenController({ iframe, origin, adapter, onChange? })`;宿主提供 `setActive(active)` / `dispose()`,销毁时调用控制器 `destroy()`。此通道只处理网页全屏。
164
170
 
165
- 详见[网页全屏接入与完整参考实现](../../docs/guides/PAGE-FULLSCREEN.md)。iframe 需要新版宿主与 iframe 协商支持;未启用时新入口不可用。浏览器原生全屏接口保持独立。
171
+ 网页全屏的宿主责任与接口见[包内 API 参考](./skills/host-integration/references/offline-api.md#网页全屏)。iframe 需要新版宿主与 iframe 协商支持;未启用时新入口不可用。浏览器原生全屏接口保持独立。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@video-lab/embed-helper",
3
- "version": "4.1.2",
3
+ "version": "4.1.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -27,11 +27,12 @@
27
27
  "dist/**/*.d.ts",
28
28
  "dist/**/*.d.cts",
29
29
  "dist/**/*.d.mts",
30
- "dist/**/*.css"
30
+ "dist/**/*.css",
31
+ "skills/**"
31
32
  ],
32
33
  "sideEffects": false,
33
34
  "dependencies": {
34
- "@video-lab/protocol": "4.1.2"
35
+ "@video-lab/protocol": "4.1.4"
35
36
  },
36
37
  "devDependencies": {
37
38
  "tsdown": "^0.22.9",
@@ -43,7 +44,7 @@
43
44
  "access": "public"
44
45
  },
45
46
  "scripts": {
46
- "build": "tsdown",
47
+ "build": "node ../../scripts/sync-public-host-skill.mjs embed-helper && tsdown",
47
48
  "dev": "tsdown --watch --no-clean",
48
49
  "test": "vitest run",
49
50
  "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json --noEmit"
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: host-integration
3
+ description: >-
4
+ 在业务宿主中接入 Video Lab Player 时使用。按 React/Vue inline、React/Vue iframe
5
+ 或静态 iframe URL 选择接入面,完成可验证的播放器 demo 与责任交接。
6
+ metadata:
7
+ skillVersion: '3'
8
+ sdkMajor: '4'
9
+ embedApp: '4.0.5'
10
+ contract: '4.0.0'
11
+ supportedPackages: >-
12
+ @video-lab/react@4.1.4, @video-lab/vue@4.1.4,
13
+ @video-lab/react-frame@4.1.4, @video-lab/vue-frame@4.1.4,
14
+ @video-lab/embed-helper@4.1.4
15
+ ---
16
+
17
+ # Video Lab Player · 宿主接入
18
+
19
+ ## 取得资料
20
+
21
+ 五个直接接入包均附带 `skills/host-integration/`。从**已安装版本**复制整个目录到宿主 AI
22
+ 的 Skill 目录,核对 `supportedPackages`;不要只复制 `SKILL.md` 而漏掉 `references/`。
23
+ 纯静态 URL、未安装 npm 包的宿主,请部署方离线提供整个 Skill 目录、受控 origin、媒体源与
24
+ 该部署的 `release.json`。核对清单中的 `embedApp`、`contract` 与上方同名元数据相等,
25
+ 不相等时向部署方索取对应版本资料;有文档站时也可从站点取得同版本资料。
26
+ 不要从示例地址猜生产配置。
27
+ Skill 是宿主 AI 的资料,不是浏览器运行时代码或 iframe 部署物。
28
+
29
+ ## 只读当前接入面
30
+
31
+ | 宿主条件 | 接入方式 | 必读参考 |
32
+ | --- | --- | --- |
33
+ | React 页面,需要直连内核 | `@video-lab/react` | [React inline](references/react-inline.md) |
34
+ | Vue 页面,需要直连内核 | `@video-lab/vue` | [Vue inline](references/vue-inline.md) |
35
+ | React 页面,需要 iframe 隔离及命令 | `@video-lab/react-frame` | [React iframe](references/react-iframe.md) |
36
+ | Vue 页面,需要 iframe 隔离及命令 | `@video-lab/vue-frame` | [Vue iframe](references/vue-iframe.md) |
37
+ | CMS、Markdown 或纯 HTML,只能嵌 URL | 静态 iframe URL,`@video-lab/embed-helper` 可选 | [静态 iframe URL](references/static-iframe.md) |
38
+
39
+ 选定一种后,先读该模式参考;按功能词查[共用 API 索引](references/api-index.md),
40
+ 需要画中画、品牌覆盖层、QoE 或 403 换源时读[常见功能配方](references/feature-recipes.md)。
41
+ 无法访问文档站时,[离线 API 参考](references/offline-api.md)覆盖媒体源、Provider、语言、
42
+ 网页全屏、命令、事件与错误的查找入口。
43
+ 参考页给出起步示例和差异;**参数含义、完整签名与可用范围以已安装包 README、导出类型及实际部署的 iframe 版本为准**。
44
+ 不要从另一个版本或另一个接入面的示例猜测 API。静态 URL 没有命令通道;需要宿主下发播放、
45
+ 不重建 iframe 的换源、字幕或弹幕命令时,选框架组件。
46
+
47
+ 回答接入问题时按“功能词 → 所选模式参考 → 对应配方或离线 API 参考 → 该版本包 README 的具体章节 → 导出类型”查找。
48
+ 这些包内资料覆盖公开接入面;只有文档与类型互相矛盾或缺失时才交还维护者核实源码,不要把扫描
49
+ SDK 源码当成宿主接入的默认步骤。不要把仓库维护者的 `.claude/` 或 `CLAUDE.md` 复制给宿主。
50
+
51
+ ## 实施与验收
52
+
53
+ 1. 取得业务服务签发的完整媒体源;iframe 模式还需部署方提供受控 origin 和可用版本。鉴权、权益、CORS、字幕资源、弹幕审核与限流由宿主或服务端负责;不要传业务请求头、token 或身份给 SDK。
54
+ 2. 用所选参考页建立最小 demo;有事件能力时接入 `ready`、完整事件、`playablechange` 与 `error`。纯 iframe 标签拿不到事件,不能把 `iframe.onload` 当作 `ready`。命令失败和播放期错误分别处理;品牌 UI、业务 CTA、焦点与页面布局由宿主负责。
55
+ 3. 验证首次加载、首帧、人工暂停、自动播放受阻、可恢复与终态错误、签名换源、卸载清理。运行时 Loading 仅在 `!playable && recoverable` 时盖住画面;`retry()` 接受请求不等于已经恢复。
56
+ 4. 若做遥测,只记录允许的播放器事实;不要上传完整媒体 URL、签名、token、cookie、身份或弹幕正文,也不要逐条上传高频 `timeupdate`。
57
+
58
+ 交接时说明:所选方式和版本、已验证的行为、SDK 已完成的能力、宿主与服务端待办、模式限制及尚未验证的真实环境。宿主需要隐藏单个内置控件时使用初始化 `controlVisibility`(静态 URL 为 `controlPlay` 等参数);网页全屏需要宿主布局适配器。具体字段见所选模式参考和包 README。
@@ -0,0 +1,47 @@
1
+ # 共用 API 索引
2
+
3
+ 这页是查找入口,不是第二份类型定义。先选模式,再读[离线 API 参考](offline-api.md)与所选包 README 的配置、事件、命令章节;完整字段以该版本包的导出类型和 `@video-lab/protocol` 为准。
4
+
5
+ ## 用功能词找配置
6
+
7
+ | 接入方的问题 | 四个框架组件的入口 | 静态 iframe URL | 继续读哪里 |
8
+ | --- | --- | --- | --- |
9
+ | 只隐藏画中画或某个内置控件 | 初始化 `controlVisibility.pip=false` 等 | `controlPip=0` 等八个 URL 键 | [常见功能配方](feature-recipes.md)、包 README“内置控件按需隐藏” |
10
+ | 内置封面 / 暂停图 | `poster` / `pauseImage` | `poster` / `pauseImage` 及各自 `Fit` | [常见功能配方](feature-recipes.md)、包 README“Poster 上叠自定义 Loading” |
11
+ | 宿主自定义封面、暂停、Loading | React `renderPoster` / `renderPause` / `renderLoading`;Vue `#poster` / `#pause` / `#loading` | URL 无 renderer / slot;有脚本时由宿主自行覆盖 | [常见功能配方](feature-recipes.md)、所选模式参考 |
12
+ | 自动播放被阻止 | React `onAutoplayBlocked`;Vue `@autoplay-blocked` | 有脚本时订阅事件;纯 iframe 标签无事件 | 包 README“Events”或“完整事件与命令失败” |
13
+ | 403 / 签名过期 | `resolveSource`,返回完整新源或 `null` | 有脚本时监听 `E_AUTH_EXPIRED` 并替换 iframe URL | [常见功能配方](feature-recipes.md) |
14
+ | QoE / 用户行为 | React `onPlayerEvent`;Vue `@player-event` → `@video-lab/telemetry` | 有脚本时 helper `onAny()`;纯 iframe 标签无事件 | [常见功能配方](feature-recipes.md);安装遥测包后读其自带 README |
15
+ | 运行时换源、静音、语言 | `source` prop、`muted` / `locale` prop;相应句柄方法 | 重建 iframe URL | 所选包 README“Props”“Template ref”或“用法” |
16
+ | 清晰度、字幕菜单 | `ready.quality` / `ready.subtitles` 建菜单;`setQuality` / `setSubtitle` 切换 | URL 无轨列表和命令 | 所选包 README“Events”“Template ref”或“完整事件与命令失败” |
17
+ | 弹幕渲染 | `danmaku` + `pushDanmaku` 等公开方法;服务由宿主提供 | URL 无弹幕输入 / 命令 | 所选组件包 README 的“相关”中的业务接入场景;静态模式明确不支持 |
18
+ | 原生全屏 / 网页全屏 | 句柄方法;网页全屏另传 `pageFullscreen` 布局适配器 | 有脚本时使用 helper controller | 所选包 README 的“宿主网页全屏” |
19
+ | 终态错误、重试和卸载 | `playablechange` 决策;`performErrorAction()`;组件卸载清理 | helper 可收事件;重试只能重建 URL;退订并 `destroy()` | 包 README“统一恢复”或“生命周期”,[常见功能配方](feature-recipes.md) |
20
+
21
+ 上表给出**查找路径**,不宣称所有字段都可在运行时修改。四个组件的业务需求应先看所选包
22
+ README:React 是“用法”“完整事件与命令失败”,Vue 是“Props”“Events”“Template ref”;
23
+ 静态 URL 看 `@video-lab/embed-helper/README.md` 的“API”“边界”。公开类型只用于确认精确签名,
24
+ 不必打开 SDK 的 `src/`。
25
+
26
+ | 问题 | 四个框架组件 | 静态 iframe URL |
27
+ | --- | --- | --- |
28
+ | 媒体与起播 | `source` / `resolveSource`、`autoplay`、`muted`、`loop`、`playsinline`、`preload`、`startTime` | `src` 必填;`type`、`live`、`lowLatency`、`autoplay`、`muted`、`loop`、`playsinline`、`preload`、`startTime` 可选;不支持宿主函数 `resolveSource` |
29
+ | 画面与控件 | `poster`、`pauseImage`、`controls`、`controlVisibility`、`showDefaultLoadingOverlay`、`showLoadingText`、`showErrorOverlay`、`errorButtonColors`、`interactive` | `poster` / `posterFit` / `posterLoading`、`pauseImage` / `pauseImageFit`、`controls`、八个 `control*`、`showDefaultLoadingOverlay`、`showLoadingText`、`showErrorOverlay`、`errorButtonBackground` / `errorButtonForeground`、`interactive` |
30
+ | 初始播放参数 | `volume`、`playbackRate`、`preset`、`locale` | 同名 URL 参数;仅内置语言,不传自定义 `messages` |
31
+ | 功能扩展 | `source.subtitles`、`danmaku`、`pageFullscreen`、`debug`;iframe 组件另有 `origin`、`iframeVersion` | `debug` 仅供排查;URL 不接收字幕/弹幕数据,网页全屏需宿主 helper/controller |
32
+ | 命令 | 组件句柄的公开方法;iframe 命令异步 | 无命令通道;`embed-helper` 只监听事件 |
33
+ | 完整事件 | React `onPlayerEvent`;Vue `@player-event` | `createEmbedListener({ origin, iframe }).onAny()`;只有订阅后的未来事件 |
34
+
35
+ 八个控件名为 `play`、`progress`、`time`、`volume`、`playbackRate`、`fullscreen`、`cssFullscreen`、`pip`;静态 URL 对应 `controlPlay`、`controlProgress`、`controlTime`、`controlVolume`、`controlPlaybackRate`、`controlFullscreen`、`controlCssFullscreen`、`controlPip`。`controls=false` 整体关闭优先。
36
+
37
+ React 组件还可传容器 `className`、`style`、`children`;Vue 组件有对应的模板与插槽用法。它们是框架视图接口,不是 `PlayerConfig`,须看所选包的导出类型。`debug` 在 inline 无通信层,因此不会产生日志;只有 iframe 模式用来排查握手与消息。
38
+
39
+ 还可按需查[离线 API 参考](offline-api.md)的媒体源、Provider、语言、网页全屏、事件与错误:
40
+
41
+ - **媒体源、字幕、源候选**:所选包 README 的 `source` 说明及公开 `MediaSource` 类型。媒体封装候选不表达会员权益。
42
+ - **Loading、错误、恢复**:所选包 README 的场景表;UI 以 `playablechange` 的 `playable`、`recoverable`、`action` 决策,`error` 用于诊断。`firstframe` 每个播放会话都可能再次发生。
43
+ - **完整事件与错误码**:`PlayerEvent` 用 `event` 区分、数据在 `payload`,没有 `type` 字段;错误码以 `@video-lab/protocol` 的导出类型和 README 为准。
44
+ - **签名失效**:四个组件可提供 `resolveSource`,由服务端给完整新源或返回 `null`;静态 URL 要由宿主替换整个 iframe URL。
45
+ - **生命周期**:框架组件卸载自动清理;静态 helper 必须退订并 `destroy()`。iframe `load` 事件不证明播放器 `ready`。
46
+
47
+ 不要从这个索引推断某字段可以在挂载后热更新。初始化参数、动态更新方式和平台限制由所选包 README 说明;不确定时查导出类型与运行时验证,不私发 `postMessage` 或直接使用内核私有 API。
@@ -0,0 +1,167 @@
1
+ # 常见功能配方 · 五种接入方式
2
+
3
+ 先从 Skill 入口选一种模式。本页只回答经常被问到的接法;复制代码时使用**已安装包**
4
+ 的对应 import 和已部署的 iframe 版本。媒体 URL、封面 URL、`origin` 与业务服务函数都由宿主提供。
5
+ 若需求不在本页,用[共用 API 索引](api-index.md)定位所选包 README 的章节和导出类型。
6
+
7
+ ## 隐藏画中画按钮
8
+
9
+ | 模式 | 创建播放器时的配置 |
10
+ | --- | --- |
11
+ | React inline / React iframe | `controlVisibility={ { pip: false } }` |
12
+ | Vue inline / Vue iframe | `:control-visibility="{ pip: false }"` |
13
+ | 静态 iframe URL | `controlPip=0` |
14
+
15
+ 只隐藏内置控制栏入口,其他控件照常显示;`controls=false` 会隐藏整个控制栏。
16
+ `controlVisibility` 是初始化配置,修改时重建组件;静态模式重新导航 iframe。
17
+ iframe 模式还须确认部署的播放器应用支持此字段。它不负责禁止浏览器或宿主自己的画中画入口。
18
+
19
+ ## 封面与 Loading
20
+
21
+ | 模式 | 内置封面 | 自定义封面 | 自定义首次与运行时 Loading |
22
+ | --- | --- | --- | --- |
23
+ | React inline | `poster="…"` | `renderPoster={() => <BrandPoster />}` | `renderLoading={({ phase, reason, text }) => <BrandLoading phase={phase} reason={reason} text={text} />}` |
24
+ | Vue inline | `poster="…"` | `<template #poster>…</template>` | `<template #loading="{ phase, reason, text }">…</template>` |
25
+ | React iframe | 同 React inline;自定义内容在宿主 DOM | `renderPoster` | `renderLoading`,覆盖 iframe 创建与握手空档 |
26
+ | Vue iframe | 同 Vue inline;插槽在宿主 DOM | `#poster` | `#loading`,覆盖 iframe 创建与握手空档 |
27
+ | 静态 iframe URL | `poster=<编码后的图片 URL>` | 不支持 renderer / slot | 只支持内置层;有脚本的宿主可在 iframe 外自己渲染 |
28
+
29
+ 四个组件由 SDK 控制 Poster 到首帧、Loading 的首次加载与可恢复等待;renderer / slot
30
+ 只负责视觉。提供自定义 Loading 后,默认 Loading 自动让位,不必再设置
31
+ `showDefaultLoadingOverlay=false`。只有把覆盖层完全放到组件外,才关闭默认视觉并消费
32
+ `playablechange`。自定义暂停画面同理使用 React `renderPause` 或 Vue `#pause`,静态模式只能给
33
+ `pauseImage` 图片;明确的人工暂停才应显示暂停画面。Frame 需搭配支持宿主展示桥的 iframe
34
+ 版本验收;旧版可能仍显示 iframe 内媒体层。完整可复制的组件写法见所选模式参考页。
35
+
36
+ ## 403 与签名过期
37
+
38
+ 四个框架组件把 `resolveSource` 交给**宿主**。SDK 仅在 `E_AUTH_EXPIRED` 时调用它;401 / 403
39
+ 可能映射到此错误,但宿主不能把所有 403 都猜成“还能续签”。业务服务判断权益并返回**完整新
40
+ `MediaSource`**;确定没有替代源时返回 `null`。`signal` 用于取消过期请求,不要重连旧签名 URL。
41
+
42
+ ```ts
43
+ import type { MediaSource, ResolveSource } from '@video-lab/react'
44
+
45
+ // 宿主实现:向业务服务获取完整新源;无替代源时返回 null。
46
+ declare function requestAuthorizedSource(signal: AbortSignal): Promise<MediaSource | null>
47
+
48
+ const resolveSource: ResolveSource = ({ signal }) => requestAuthorizedSource(signal)
49
+ ```
50
+
51
+ 上例的 import 改成所选的 `@video-lab/react-frame`、`@video-lab/vue` 或
52
+ `@video-lab/vue-frame` 即可;四个包都重导出 `MediaSource` 和 `ResolveSource`。
53
+ React 传 `resolveSource={resolveSource}`,Vue 传 `:resolve-source="resolveSource"`。
54
+ `onError` / `@error` 用于诊断,`onPlayerEvent` / `@player-event` 中的 `sourceswitch` 用于观测;
55
+ 不要再在错误回调里并行刷新同一个旧源。
56
+
57
+ 静态 iframe URL 不能传函数。只有宿主允许脚本时,才能用
58
+ `createEmbedListener({ origin, iframe })` 在设置 iframe `src` 前订阅 `error`;收到
59
+ `E_AUTH_EXPIRED` 后由宿主向业务服务取得新 URL,并**替换整个 iframe URL**。仅能嵌入 iframe
60
+ 标签的页面无法自动续签,也无法取得可信错误事件。
61
+
62
+ 有 npm 构建的静态宿主可按下面接线;`requestAuthorizedUrl()` 与 `showUnavailable()` 是宿主业务函数。
63
+ 无构建工具但允许脚本时,使用同一部署根的 `helper.js` 和全局
64
+ `SentinelEmbed.createEmbedListener`,事件及换 URL 的顺序相同。
65
+
66
+ ```bash
67
+ pnpm add @video-lab/embed-helper
68
+ ```
69
+
70
+ ```ts
71
+ import { createEmbedListener } from '@video-lab/embed-helper'
72
+
73
+ declare const initialSource: string
74
+ declare function requestAuthorizedUrl(): Promise<string | null>
75
+ declare function showUnavailable(): void
76
+
77
+ const iframe = document.querySelector('#article-player')
78
+ if (!(iframe instanceof HTMLIFrameElement)) throw new Error('iframe 未找到')
79
+ const embedBase = 'https://player.example.internal/' // 换成部署方提供的完整根路径,含版本目录
80
+ const origin = new URL(embedBase).origin
81
+ const listener = createEmbedListener({ origin, iframe })
82
+ let refreshing = false
83
+ let lastFailedIframeUrl = ''
84
+ const offError = listener.on('error', async ({ code }) => {
85
+ if (code !== 'E_AUTH_EXPIRED' || refreshing || iframe.src === lastFailedIframeUrl) return
86
+ lastFailedIframeUrl = iframe.src
87
+ refreshing = true
88
+ try {
89
+ const replacement = await requestAuthorizedUrl()
90
+ if (replacement === null) return showUnavailable()
91
+ const next = new URL(iframe.src)
92
+ next.searchParams.set('src', replacement)
93
+ iframe.src = next.href
94
+ } catch {
95
+ showUnavailable()
96
+ } finally {
97
+ refreshing = false
98
+ }
99
+ })
100
+
101
+ const embedUrl = new URL(embedBase)
102
+ embedUrl.searchParams.set('src', initialSource)
103
+ embedUrl.searchParams.set('controlPip', '0')
104
+ iframe.src = embedUrl.href // 先订阅,后导航;事件不会回放
105
+ window.addEventListener('pagehide', () => {
106
+ offError()
107
+ listener.destroy()
108
+ }, { once: true })
109
+ ```
110
+
111
+ ## QoE 与行为上报
112
+
113
+ 需要会话 QoE 时另装 `@video-lab/telemetry`。它只把完整 `PlayerEvent` 转成记录和摘要,
114
+ **不发送 HTTP**。`sink` 是宿主的有界队列入口;批量、并发、有限重试和上传失败监控由宿主实现。
115
+
116
+ ```bash
117
+ pnpm add @video-lab/telemetry
118
+ ```
119
+
120
+ ```ts
121
+ import { createTelemetrySession } from '@video-lab/telemetry'
122
+
123
+ // 宿主实现:构建版本与有界队列;不能把这三个声明直接当成传输实现。
124
+ declare const hostRelease: string
125
+ declare function enqueueRecord(input: unknown): void
126
+ declare function enqueueSummary(input: unknown): void
127
+
128
+ const telemetry = createTelemetrySession({
129
+ application: { name: 'business-web', release: hostRelease },
130
+ sink: { onRecord: enqueueRecord, onSummary: enqueueSummary },
131
+ })
132
+
133
+ // 在宿主自定义按钮的真实点击处理器中调用;内置控件动作已在完整事件中。
134
+ function onRetryClick() {
135
+ telemetry.recordIntent({ type: 'retry', origin: 'user' })
136
+ // 接着调用所选组件句柄的 retry(),并处理 Promise rejection。
137
+ }
138
+ // 组件卸载时调用 telemetry.finish({ reason: 'destroyed' });
139
+ // 页面退出时调用 telemetry.finish({ reason: 'page_unload' })。
140
+ ```
141
+
142
+ | 模式 | 完整事件接线 |
143
+ | --- | --- |
144
+ | React inline / React iframe | `onPlayerEvent={(event) => telemetry.record(event)}` |
145
+ | Vue inline / Vue iframe | `@player-event="telemetry.record"` |
146
+ | 静态 iframe URL,有 npm 构建与页面脚本 | `const off = listener.onAny((event) => telemetry.record(event))`;卸载时 `off()`、`listener.destroy()` |
147
+ | 静态 iframe URL,只有 `helper.js` 脚本 | 可订阅完整事件;`@video-lab/telemetry` 没有独立 IIFE,需宿主构建链或自有上报实现 |
148
+ | 只有 iframe 标签 | 无宿主事件出口,不能做这一套会话上报 |
149
+
150
+ 静态模式要先绑定具体 iframe 与精确 `origin`、订阅 `onAny()`,再设置 `src`;事件不回放。
151
+ 不要把 `timeupdate` 等完整事件逐条 `fetch()`,也不要把完整媒体 URL、签名、token、身份或
152
+ 弹幕正文放进上报。精确记录类型、频率与会话完成时机见已安装的
153
+ `@video-lab/telemetry/README.md`“最小接入”“高频与网络安全”。
154
+
155
+ ## 其他常见问题
156
+
157
+ | 想做的事 | 接入入口与边界 |
158
+ | --- | --- |
159
+ | 只隐藏 CSS 全屏 | 四个组件 `controlVisibility.cssFullscreen=false`;静态 URL `controlCssFullscreen=0`。 |
160
+ | 自动播放被拒 | React `onAutoplayBlocked`;Vue `@autoplay-blocked`;向用户显示可点击播放按钮并在手势中调用 `play()`。静态模式只有允许脚本时才能收事件。 |
161
+ | 运行时换源 | 四个组件更新 `source` prop;静态模式重建 URL。不要调用内部 `load()`。 |
162
+ | 清晰度 / 字幕菜单 | 从 `ready.quality` / `ready.subtitles ?? []` 生成菜单,再调用 `setQuality` / `setSubtitle`;静态模式没有命令。 |
163
+ | 终态错误 / 重试 | UI 读取 `playablechange`;四个组件的自定义 CTA 可调用 `performErrorAction()`;Promise 完成不代表已恢复,等待 `recovery` / 播放证据。 |
164
+ | 暂停画面与网络缓冲 | React `renderPause`、Vue `#pause` 或五种模式共用 `pauseImage`;只有明确人工暂停显示 Pause,缓冲显示 Loading。 |
165
+
166
+ 所有初始化参数、事件 payload、命令返回值与平台限制都以所选版本的**包 README 与导出类型**
167
+ 为准。iframe 的 `origin` 和可用版本由部署方提供,不从示例地址推断生产配置。
@@ -0,0 +1,46 @@
1
+ # 离线 API 参考 · 五种接入方式
2
+
3
+ 本页随五个直接接入包一起发布,供无法访问文档站的宿主按功能查入口。先读所选[模式参考](../SKILL.md)和[功能配方](feature-recipes.md),再用**已安装版本**的包 README 与导出 `.d.ts` 核对精确类型;这里不替代类型声明。iframe 的可用能力还取决于实际部署版本。
4
+
5
+ ## 配置入口与生效时机
6
+
7
+ | 功能 | React / Vue inline 与 iframe 组件 | 静态 iframe URL |
8
+ | --- | --- | --- |
9
+ | 媒体与起播 | 必填 `source`;可选 `resolveSource`、`autoplay`、`muted`、`loop`、`playsinline`、`preload`、`startTime`、`volume`、`playbackRate` | 必填 `src`;可选 `type`、`live`、`lowLatency` 及同名起播参数;无函数型续签 |
10
+ | 内置画面 | `poster`、`pauseImage`、`controls`、`controlVisibility`、`interactive` | `poster`、`posterFit`、`posterLoading`、`pauseImage`、`pauseImageFit`、`controls`、八个 `control*`、`interactive` |
11
+ | 覆盖层 | `showErrorOverlay`、`showDefaultLoadingOverlay`、`showLoadingText`、`errorButtonColors`;React `renderPoster/Pause/Loading`;Vue `#poster/#pause/#loading` | 同名布尔 URL 键;`errorButtonBackground/Foreground`;没有 renderer / slot |
12
+ | 语言与扩展 | `locale`、`preset`、`danmaku`;组件还可传 `pageFullscreen` | `locale`、`preset`、`title`、`description`;无字幕、弹幕输入或宿主函数 |
13
+ | iframe 专属 | `origin`、`iframeVersion`、`debug` | 部署方给完整 URL;`debug=1` 只用于通信排障 |
14
+
15
+ `source` 改变可换源;`muted`、`locale`、`interactive` 与 inline 默认覆盖层视觉可原地更新。`controlVisibility`、renderer/slot 是否存在、`preload`、初始音量/倍速/时间属于创建策略;切换这些策略时显式重挂。Frame 内层覆盖层配置由 iframe 创建时读取,宿主显示层的动态行为须用实际部署版本验收。`controls=false` 优先于单个 `controlVisibility` 开关。`debug` 在 inline 没有通信层,因此不产生日志。
16
+
17
+ ### 媒体源、字幕与弹幕
18
+
19
+ 四个组件的 `source` 接受 URL 字符串、`{ url, type?, live?, hls?, subtitles?, metadata? }`,或 `{ sources: [{ url, type }, …], live?, hls?, subtitles?, metadata? }`。多源候选是**同一内容的不同封装**,不是清晰度档位;签名 URL 应显式写 `type`。直播务必设置 `live: true`;更换直播/点播模式需要重建播放器。`hls.lowLatencyMode: true` 才启用低延迟 HLS。字幕轨可用 `{ mode: 'url', url, locale, label }` 或 `{ mode: 'content', content, locale, label }`,内联正文须是 WebVTT;`ready.subtitles` 给轨道 id,交给 `setSubtitle(id)`,关闭传 `'off'`。静态 URL 没有字幕轨输入或切换命令。
20
+
21
+ `danmaku` 默认关闭;`{ enabled: true, mode: 'streaming' }` 配合 `pushDanmaku({ id, text, type?, color?, start? })`。点播可用 `{ enabled: true, mode: 'preload', items: [...] }`,`start` 是**毫秒**;`seek` 和 `startTime` 是秒。弹幕内容、审核和业务传输由宿主负责。静态 URL 不接受弹幕输入。
22
+
23
+ `poster` 可传图片 URL 或 `{ url, fit?, loading? }`,`pauseImage` 可传 URL 或 `{ url, fit? }`;组件还可传 `null` 清除 Provider 继承的图片。自定义视觉的最小代码见各[模式参考](../SKILL.md);Loading 的 `phase` 为 `initial` / `runtime`。明确人工暂停显示 Pause,网络缓冲显示 Loading。静态 URL 要在 `URLSearchParams` 中编码图片 URL 和颜色值。
24
+
25
+ ### 应用级默认值与语言
26
+
27
+ 四个框架包都导出 `PlayerProvider` / `usePlayerI18n`。React:`<PlayerProvider defaultLocale="zh-CN" defaults={ { showLoadingText: true } }>…</PlayerProvider>`;受控语言用 `locale` + `onLocaleChange`。Vue:`<PlayerProvider v-model:locale="language" :defaults="defaults">…</PlayerProvider>`。Provider 还接受 `resources`、`messageOverrides`、`fallbackLocales`;`frameDefaults` 可给新建 iframe 统一设置 `origin`、`iframeVersion`、`debug`。单实例显式值优先,`false` / `0` 会覆盖默认;`source`、`resolveSource`、回调与自定义视觉始终逐实例提供。`usePlayerI18n` 只能在 Provider 内调用。默认中/英;越南语按需从 `@video-lab/locales/vi-VN` 引入 `viVN`。静态 URL 无 Provider 或自定义消息表,切换 `locale` 要替换 URL。
28
+
29
+ ### 网页全屏
30
+
31
+ 四个组件的 `pageFullscreen` 接受挂载期间稳定的 `{ setActive(active): void | Promise<void>, dispose(): void }`。宿主在 `setActive` 中调整容器、滚动与焦点,调用句柄 `await setPageFullscreen(true | false)`,监听 React `onPageFullscreenChange` 或 Vue `@page-fullscreen-change`。浏览器原生全屏仍由 `enterFullscreen()` / `exitFullscreen()` 控制。静态 iframe 只有带脚本且安装 `@video-lab/embed-helper` 时可用 `createPageFullscreenController({ iframe, origin, adapter, onChange? })`,先建控制器再设置 iframe `src`,离开时 `destroy()`;纯 iframe 标签无法铺满宿主页面。iframe 侧须部署支持该能力的版本。
32
+
33
+ ## 命令、事件与错误
34
+
35
+ 四个框架组件通过 ref 暴露 `play`、`pause`、`seek`、`setMuted`、`setVolume`、`setPlaybackRate`、`setPageFullscreen`、`enterFullscreen`、`exitFullscreen`、`setQuality`、`setSubtitle`、`setLocale`、`pushDanmaku`、`setDanmakuEnabled`、`clearDanmaku`、`retry`、`performErrorAction`、`destroy`、`getCurrentTime`、`getDuration`、`getPlaybackContext`、`getPlayerHandle`。**iframe 命令除本地 getter 均异步**;inline 只有可失败的 `play`、全屏、`retry`、`performErrorAction` 等返回 Promise,精确签名看包的导出类型。静态 URL 无播放命令通道;helper 的 `on/onAny/off/destroy` 用于订阅,换源须生成新 URL。
36
+
37
+ 完整事件:React `onPlayerEvent(event)`,Vue `@player-event`,静态脚本 `createEmbedListener({ origin, iframe }).onAny(handler)`;仅 iframe 标签没有事件出口。`PlayerEvent` 以 `event` 区分、`payload` 承载数据、`delivery` 提供排序/去重证据。常用事实:`ready` 给时长/清晰度/字幕清单;`firstframe` 表示本次播放会话画面出现;`playablechange` 的 `playable/recoverable/action` 决定页面级 Loading 与终态;`error` 给错误诊断;`recovery` 给恢复结果;`contextchange`、`stalled`、`kernelhealth`、`bufferhealth`、`audiohealth`、`framefreeze`、`useraction` 供观测。React 对应 `onReady`、`onFirstFrame`、`onPlayableChange` 等,Vue 对应 `@ready`、`@first-frame`、`@playable-change` 等;全部事件名和 payload 以导出的 `PlayerEvent` 为准。
38
+
39
+ 播放器错误码包括 `E_AUTH_EXPIRED`、`E_NETWORK`、`E_NETWORK_TIMEOUT`、`E_MANIFEST_PARSE`、`E_MEDIA_DECODE`、`E_MEDIA_NOT_SUPPORTED`、`E_AUTOPLAY_BLOCKED`、`E_SUBTITLE_LOAD_FAILED`、`E_ENV_CSP_BLOCKED` 等;iframe 组件另可能出现握手/加载/崩溃错误。**不要只凭错误码自己拼接最终 UI 状态**:`error` 用于诊断,`playablechange` 用于呈现。`E_AUTH_EXPIRED` 的续签由宿主提供完整新源;`retry()` 完成仅表示请求已接受,恢复成功仍看后续事件。命令 rejection 和播放期 `error` 需分别处理。
40
+
41
+ ## 包内资料查找顺序
42
+
43
+ 1. [Skill 入口](../SKILL.md)选模式;所选模式参考给最小示例与差异。
44
+ 2. [功能配方](feature-recipes.md)回答画中画、封面、Loading、403、QoE;本页回答其余公开能力。
45
+ 3. 同版本包的 `README.md` 给完整接入叙述;安装 `@video-lab/telemetry` 时另看**该包自己的 README**。
46
+ 4. 需要精确字段/返回类型,读安装包 `dist/*.d.ts` 及依赖包 `@video-lab/protocol` 的导出类型。不要读取 `src/`、猜私有 `postMessage` 或把仓库维护者文档复制到宿主。
@@ -0,0 +1,62 @@
1
+ # React iframe
2
+
3
+ 适用 React 19+ 页面,需要样式、脚本或故障隔离,同时仍需公开命令与事件时。安装 `@video-lab/react-frame`;播放器样式在 iframe 内,无需引入 inline 包的 CSS。
4
+
5
+ ## 最小接入
6
+
7
+ ```tsx
8
+ import { VideoPlayerFrame } from '@video-lab/react-frame'
9
+
10
+ export function LessonPlayer() {
11
+ return (
12
+ <VideoPlayerFrame
13
+ origin="https://player.example.internal"
14
+ source="https://media.example.com/lesson.m3u8"
15
+ muted
16
+ onReady={({ duration }) => console.info('ready', duration)}
17
+ onError={({ code }) => console.error(code)}
18
+ />
19
+ )
20
+ }
21
+ ```
22
+
23
+ `origin` 和可用 `iframeVersion` 必须来自部署方;示例地址只是占位符。需要锁定应用构建物时才传 `iframeVersion`,并确认该目录已部署。句柄命令跨 iframe,全部异步,宿主主动调用时都处理 rejection。
24
+
25
+ ## 常见视觉配置
26
+
27
+ 下例的 renderer 在**宿主 DOM**,可覆盖 iframe 创建和握手空档;`origin` 要换成部署方给出的地址。
28
+
29
+ ```tsx
30
+ import { VideoPlayerFrame } from '@video-lab/react-frame'
31
+
32
+ export function BrandedPlayer() {
33
+ return (
34
+ <VideoPlayerFrame
35
+ origin="https://player.example.internal"
36
+ source="https://media.example.com/lesson.m3u8"
37
+ poster="https://media.example.com/lesson-poster.jpg"
38
+ controlVisibility={{ pip: false }}
39
+ renderPoster={() => (
40
+ <img src="/brand-poster.jpg" alt="课程视频封面" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
41
+ )}
42
+ renderLoading={({ phase, text }) => (
43
+ <div role="status">{text ?? (phase === 'initial' ? '正在加载' : '正在恢复')}</div>
44
+ )}
45
+ />
46
+ )
47
+ }
48
+ ```
49
+
50
+ 配套 iframe 版本中自定义 Loading 替换默认层,不必再设 `showDefaultLoadingOverlay={false}`;
51
+ 旧版 iframe 可能保留内部媒体层,须在目标部署验收。
52
+ renderer 是否存在与 `controlVisibility` 属构造期策略;切换时重新挂载,并确认部署版 iframe 支持配置。
53
+ 403 换源与 QoE 接线见[常见功能配方](feature-recipes.md)。
54
+
55
+ ## 按需读取的 API
56
+
57
+ - 配置、命令、错误与恢复:已安装 `@video-lab/react-frame/README.md` 的“用法”“配置 iframe 地址”“完整事件与命令失败”“统一恢复”;props 与句柄准确签名见 `VideoPlayerFrameProps`、`VideoPlayerFrameHandle`。
58
+ - 完整事件使用 `onPlayerEvent`;用户可见状态用 `onPlayableChange`,错误码用 `onError`。命令 resolve 不证明画面已恢复;等待 `recovery`、`playablechange` 或首帧证据。
59
+ - 宿主视觉用 `renderLoading` / `renderPoster` / `renderPause`;iframe 内覆盖层不能直接接受宿主 DOM、renderer 或 CSS。初次握手阶段需要宿主 Loading,失联后不能继续在旧句柄上循环下命令。
60
+ - `source` prop 更新可换源;签名过期由宿主 `resolveSource` 提供新完整源。React 组件卸载会释放连接,宿主仍需结束自己的遥测会话。
61
+
62
+ 按需读[共用索引](api-index.md);不要将 `origin` 当媒体源 URL,也不要手工向 iframe 发 `postMessage`。
@@ -0,0 +1,62 @@
1
+ # React inline
2
+
3
+ 适用 React 19+ 页面,需要最小首帧开销或与页面直接组合时。安装 `@video-lab/react`,并引入一次 `@video-lab/react/style.css`;无需 iframe origin。
4
+
5
+ ## 最小接入
6
+
7
+ ```tsx
8
+ import { VideoPlayer } from '@video-lab/react'
9
+ import '@video-lab/react/style.css'
10
+
11
+ export function LessonPlayer() {
12
+ return (
13
+ <VideoPlayer
14
+ source="https://media.example.com/lesson.m3u8"
15
+ muted
16
+ onReady={({ duration }) => console.info('ready', duration)}
17
+ onError={({ code }) => console.error(code)}
18
+ />
19
+ )
20
+ }
21
+ ```
22
+
23
+ 真实媒体 URL 由宿主服务签发;示例域名不能直接用于生产。要控制播放,给组件 `VideoPlayerHandle` ref,调用 `play()` 时 `await` / `catch`;`seek()` 等同步命令以事件确认实际状态。换源改 `source` prop,不调用内部 `load()`。
24
+
25
+ ## 常见视觉配置
26
+
27
+ 以下例子只隐藏内置画中画按钮,并把封面与首次/恢复 Loading 交给宿主渲染;
28
+ renderer 的显隐由 SDK 管理。
29
+
30
+ ```tsx
31
+ import { VideoPlayer } from '@video-lab/react'
32
+ import '@video-lab/react/style.css'
33
+
34
+ export function BrandedPlayer() {
35
+ return (
36
+ <VideoPlayer
37
+ source="https://media.example.com/lesson.m3u8"
38
+ poster="https://media.example.com/lesson-poster.jpg"
39
+ controlVisibility={{ pip: false }}
40
+ renderPoster={() => (
41
+ <img src="/brand-poster.jpg" alt="课程视频封面" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
42
+ )}
43
+ renderLoading={({ phase, text }) => (
44
+ <div role="status">{text ?? (phase === 'initial' ? '正在加载' : '正在恢复')}</div>
45
+ )}
46
+ />
47
+ )
48
+ }
49
+ ```
50
+
51
+ 自定义 Loading 已替换默认层,不必再传 `showDefaultLoadingOverlay={false}`。
52
+ `controlVisibility` 与 renderer 是否存在是构造期策略;要切换时重新挂载组件。
53
+ 403 换源与 QoE 接线见[常见功能配方](feature-recipes.md)。
54
+
55
+ ## 按需读取的 API
56
+
57
+ - 配置与默认值:已安装 `@video-lab/react/README.md` 的“安装”“用法”“错误 UI 接管”“内置控件按需隐藏”“统一恢复”;所有 props、renderer 与句柄签名见包导出的 `VideoPlayerProps`、`VideoPlayerHandle`。
58
+ - 完整事件:`onPlayerEvent`;UI 使用 `onPlayableChange`,诊断使用 `onError`。`onWaiting` / `onPlaying` 是瞬时信号,不应直接作为整个错误层的状态。`PlayerEvent.event` 才是事件辨别字段。
59
+ - Poster、Pause、Loading:`poster` / `pauseImage` 是内置图片;`renderPoster` / `renderPause` / `renderLoading` 是宿主自定义视觉。明确暂停才显示 Pause,缓冲或被动暂停不应显示 Pause;`renderLoading` 自动替换默认 Loading。
60
+ - 恢复与销毁:`retry()` 的 Promise 只表示请求已接收;等待 `recovery` / `playablechange` 判断结果。组件卸载自动清理;宿主遥测另行收口。
61
+
62
+ 需要精确字段、参数取值、事件 payload 或错误码时读已安装包 README 与导出类型,再读[共用索引](api-index.md)对应项;不要根据 iframe 包的全 Promise 句柄推断 inline 签名。
@@ -0,0 +1,35 @@
1
+ # 静态 iframe URL
2
+
3
+ 适用只能嵌 URL 的 CMS、Markdown 或纯 HTML。宿主无命令 RPC;如允许页面脚本,可用部署根的 `helper.js` 或 `@video-lab/embed-helper` 订阅事件。仅允许 iframe 标签时,不能取得宿主侧事件。
4
+
5
+ ## 最小接入
6
+
7
+ ```html
8
+ <iframe
9
+ id="article-player"
10
+ title="文章视频播放器"
11
+ src="https://player.example.internal/?src=https%3A%2F%2Fmedia.example.com%2Flesson.m3u8&amp;muted=1"
12
+ allow="autoplay; fullscreen; picture-in-picture; encrypted-media; screen-wake-lock"
13
+ allowfullscreen
14
+ ></iframe>
15
+ ```
16
+
17
+ 实际 iframe origin、路径和应用版本必须由部署方提供。宿主用 `URL` / `URLSearchParams` 对媒体 URL 编码;示例地址不能直接生产使用。只允许简单配置:`src`、`type`、`live`、`lowLatency`、起播和视觉参数;不能把 JSON、token 或业务身份塞入 query。
18
+
19
+ 只隐藏内置画中画按钮时,在已部署 URL 上加 `controlPip=0`,例如
20
+ `?src=https%3A%2F%2Fmedia.example.com%2Flesson.m3u8&controlPip=0`。
21
+ 自定义封面 renderer / Vue slot 不能放进 URL;可用 `poster=<编码后的图片 URL>` 指定内置图。
22
+ 首次与恢复 Loading 只支持内置层,或由有脚本的宿主在 iframe 外接事件自行渲染。
23
+ 403 换源与 QoE 的脚本边界见[常见功能配方](feature-recipes.md)。
24
+
25
+ ## URL 与事件
26
+
27
+ - `src` 是必需的媒体 URL;`type` / `live` / `lowLatency` 描述流,`title` / `description` 用于媒体会话元信息。`poster` / `posterFit` / `posterLoading` 和 `pauseImage` / `pauseImageFit` 控制内置图片。
28
+ - `autoplay`、`muted`、`loop`、`controls`、`playsinline`、`interactive`、`showErrorOverlay`、`showDefaultLoadingOverlay`、`showLoadingText` 是布尔参数;使用 `1` / `0`,不要传任意字符串。`volume` 范围 0–1,`playbackRate` 范围 0.25–4;`startTime`、`preload`、`preset`、`locale` 的可用值按部署版本校验。
29
+ - 单独控件参数为 `controlPlay`、`controlProgress`、`controlTime`、`controlVolume`、`controlPlaybackRate`、`controlFullscreen`、`controlCssFullscreen`、`controlPip`;默认错误按钮颜色可用 `errorButtonBackground` / `errorButtonForeground`。取值与边界见[离线 API 参考](offline-api.md)和本包 README,部署版本仍须核对。
30
+ - `debug=1` 只用于 iframe 通信排障;旧的 `pauseImageClosable` 已拒绝,不能继续传。
31
+ - 需要事件时,先用 `createEmbedListener({ origin, iframe })` 创建**绑定具体 iframe 和精确 origin**的监听器,先 `on('ready', ...)` / `on('error', ...)` 或 `onAny()` 订阅,再设置 iframe `src`。离开页面时退订并 `destroy()`;订阅之前的事件不会回放。
32
+
33
+ 上面的最小 `<iframe src="…">` 例子只适合不收事件的页面。要收事件,先渲染没有 `src` 的 iframe,完成 helper 订阅后再设置 `src`;完整脚本见 `@video-lab/embed-helper/README.md`。
34
+
35
+ 静态模式不能下发播放、暂停、字幕、弹幕或重试命令;换源和切语言均需替换整个 iframe URL。需要可信的现态或命令时改用 React/Vue iframe 组件。`iframe.onload` 不等于播放器 `ready`。详见已安装 `@video-lab/embed-helper/README.md` 与[共用索引](api-index.md);纯 URL 用户须取得部署方提供的同版本离线资料。
@@ -0,0 +1,63 @@
1
+ # Vue iframe
2
+
3
+ 适用 Vue 3.4+ 页面,需要 iframe 隔离并保留完整命令与事件时。安装 `@video-lab/vue-frame`;无需安装 inline 包或引入播放器 CSS。
4
+
5
+ ## 最小接入
6
+
7
+ ```vue
8
+ <script setup lang="ts">
9
+ import { VideoPlayerFrame } from '@video-lab/vue-frame'
10
+ </script>
11
+
12
+ <template>
13
+ <VideoPlayerFrame
14
+ origin="https://player.example.internal"
15
+ source="https://media.example.com/lesson.m3u8"
16
+ muted
17
+ @ready="({ duration }) => console.info('ready', duration)"
18
+ @error="({ code }) => console.error(code)"
19
+ />
20
+ </template>
21
+ ```
22
+
23
+ `origin`、可用 `iframeVersion` 和媒体源由部署方与业务服务提供;示例地址只作占位。iframe 句柄方法全部跨通信通道,宿主主动调用时均需 `await` / `catch`,并等待事件确认播放结果。
24
+
25
+ ## 常见视觉配置
26
+
27
+ 插槽在宿主 DOM 渲染,可覆盖 iframe 创建和握手空档;`origin` 要换成部署方给出的地址。
28
+
29
+ ```vue
30
+ <script setup lang="ts">
31
+ import { VideoPlayerFrame } from '@video-lab/vue-frame'
32
+ </script>
33
+
34
+ <template>
35
+ <VideoPlayerFrame
36
+ origin="https://player.example.internal"
37
+ source="https://media.example.com/lesson.m3u8"
38
+ poster="https://media.example.com/lesson-poster.jpg"
39
+ :control-visibility="{ pip: false }"
40
+ >
41
+ <template #poster>
42
+ <img src="/brand-poster.jpg" alt="课程视频封面" style="width: 100%; height: 100%; object-fit: cover" />
43
+ </template>
44
+ <template #loading="{ phase, text }">
45
+ <div role="status">{{ text ?? (phase === 'initial' ? '正在加载' : '正在恢复') }}</div>
46
+ </template>
47
+ </VideoPlayerFrame>
48
+ </template>
49
+ ```
50
+
51
+ 配套 iframe 版本中 `#loading` 替换默认层;旧版 iframe 可能保留内部媒体层,须在目标部署验收。
52
+ 插槽是否存在与 `controlVisibility` 属构造期策略。
53
+ 切换时重新挂载,并确认部署版 iframe 支持配置。403 换源与 QoE 接线见
54
+ [常见功能配方](feature-recipes.md)。
55
+
56
+ ## 按需读取的 API
57
+
58
+ - 参数、事件和命令:已安装 `@video-lab/vue-frame/README.md` 的“Props”“Events”“Template ref”;准确类型为 `VideoPlayerFrameProps`、`VideoPlayerFrameEmits`、`VideoPlayerFrameExpose`。
59
+ - 完整事实流为 `@player-event`;`@playable-change` 管 Loading 与终态 CTA,`@error` 提供诊断。事件模板名用 kebab-case。
60
+ - 品牌 Loading / Poster / Pause 分别使用 `#loading` / `#poster` / `#pause`;slot 渲染在宿主侧,须覆盖 iframe 初始化空档。明确暂停与缓冲时的被动暂停不同。
61
+ - 失联时读取 README 的“iframe 特有生命周期”;使用公开 `performErrorAction()` 或重建入口,不循环调用旧句柄。换源改 `source` prop;签名过期由宿主 `resolveSource` 获取新完整源。卸载自动清理连接。
62
+
63
+ 按需读[共用索引](api-index.md)。`iframe.onload` 只表示文档导航完成,不能当作播放器 `ready`。
@@ -0,0 +1,60 @@
1
+ # Vue inline
2
+
3
+ 适用 Vue 3.4+ 页面,需要直连播放核心时。安装 `@video-lab/vue`,并引入一次 `@video-lab/vue/style.css`;不需要在 Vite 配置 `isCustomElement`。
4
+
5
+ ## 最小接入
6
+
7
+ ```vue
8
+ <script setup lang="ts">
9
+ import { VideoPlayer } from '@video-lab/vue'
10
+ import '@video-lab/vue/style.css'
11
+ </script>
12
+
13
+ <template>
14
+ <VideoPlayer
15
+ source="https://media.example.com/lesson.m3u8"
16
+ muted
17
+ @ready="({ duration }) => console.info('ready', duration)"
18
+ @error="({ code }) => console.error(code)"
19
+ />
20
+ </template>
21
+ ```
22
+
23
+ 真实媒体 URL 由宿主服务签发。需要命令时使用 `VideoPlayerExpose` 的 template ref:`play()`、全屏与 `retry()` 要处理 Promise rejection,其余多数命令同步。换源改 `source` prop。
24
+
25
+ ## 常见视觉配置
26
+
27
+ ```vue
28
+ <script setup lang="ts">
29
+ import { VideoPlayer } from '@video-lab/vue'
30
+ import '@video-lab/vue/style.css'
31
+ </script>
32
+
33
+ <template>
34
+ <VideoPlayer
35
+ source="https://media.example.com/lesson.m3u8"
36
+ poster="https://media.example.com/lesson-poster.jpg"
37
+ :control-visibility="{ pip: false }"
38
+ >
39
+ <template #poster>
40
+ <img src="/brand-poster.jpg" alt="课程视频封面" style="width: 100%; height: 100%; object-fit: cover" />
41
+ </template>
42
+ <template #loading="{ phase, text }">
43
+ <div role="status">{{ text ?? (phase === 'initial' ? '正在加载' : '正在恢复') }}</div>
44
+ </template>
45
+ </VideoPlayer>
46
+ </template>
47
+ ```
48
+
49
+ `#poster` / `#loading` 由 SDK 管显隐;`#loading` 已替换默认层。
50
+ 插槽是否存在与 `controlVisibility` 属于构造期策略,切换时重新挂载。
51
+ 403 换源与 QoE 接线见[常见功能配方](feature-recipes.md)。
52
+
53
+ ## 按需读取的 API
54
+
55
+ - 配置、默认值、完整事件、句柄:已安装 `@video-lab/vue/README.md` 的“Props”“Events”“Template ref”三节;准确类型为包导出的 `VideoPlayerProps`、`VideoPlayerEmits`、`VideoPlayerExpose`。
56
+ - 事件在模板中使用 kebab-case,例如 `@playable-change`、`@first-frame`、`@player-event`;完整事实流只走 `@player-event`。UI 以 `playable`、`recoverable`、`action` 决策,不把 `waiting` 当成不可恢复错误。
57
+ - Poster、Pause、Loading:内置图片用 `poster` / `pauseImage`;宿主视觉用 `#poster` / `#pause` / `#loading`。`#loading` 负责首次和运行时恢复视觉;明确暂停与被动暂停语义不同。
58
+ - 签名失效时 `resolveSource` 由宿主获取完整新源;`retry()` 接受请求不等于媒体恢复。普通组件卸载自动清理;宿主遥测独立收口。
59
+
60
+ 需要精确参数值或复杂业务示例时读该包 README 的“生产场景示例”与[共用索引](api-index.md)。不要把 React custom element 事件 prop 写法复制到 Vue 模板。