@video-lab/react 1.0.0
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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/index.cjs +447 -0
- package/dist/index.d.cts +310 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +310 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +448 -0
- package/dist/index.mjs.map +1 -0
- package/dist/style.css +124 -0
- package/package.json +62 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { PlayerCoreHandle } from "@video-lab/player-core";
|
|
2
|
+
import { AudioHealthPayload, BufferHealthPayload, DanmakuConfig, DanmakuItem, FirstFramePayload, FrameFreezePayload, KernelHealthPayload, LocaleConfig, MediaSource, PauseImageConfig, PlayableReason, PlaybackContext, PlaybackContextPayload, PlayerError, PlayerEvent, PosterConfig, PresetName, QualityLevel, SourceRouteEventPayload, SubtitleTrackInfo, UserActionPayload } from "@video-lab/protocol";
|
|
3
|
+
import { CSSProperties, ReactNode } from "react";
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* 消费方拿到的底层句柄(逃生舱)。
|
|
7
|
+
*
|
|
8
|
+
* inline 模式直连 player-core,所以就是 `PlayerCoreHandle`——不像 iframe 系隔着 frame-core。
|
|
9
|
+
* `getPlayerHandle()` 返回它,给需要绕过 React 直接下命令的高级场景。
|
|
10
|
+
*/
|
|
11
|
+
type PlayerHandle = PlayerCoreHandle;
|
|
12
|
+
/**
|
|
13
|
+
* `<VideoPlayer />` 的 props(inline 高性能模式)。
|
|
14
|
+
*
|
|
15
|
+
* 刻意和 react-frame 的 `VideoPlayerFrameProps` 对齐(去掉 iframe 专属的 origin / version)——
|
|
16
|
+
* 团队封装层在 inline / iframe 两种模式间切换时,props 面尽量一致(cross-mode-parity)。
|
|
17
|
+
* 播放字段最终拼进 player-core 的 `PlayerConfig`;事件走 React 惯例 `onCamelCase` 回调,
|
|
18
|
+
* payload 形状对齐 protocol 的 PlayerEvent。
|
|
19
|
+
*/
|
|
20
|
+
interface VideoPlayerProps {
|
|
21
|
+
/** 视频源。字符串按 URL 处理,对象可带 type / live / hls 等 */
|
|
22
|
+
source: MediaSource | string;
|
|
23
|
+
autoplay?: boolean;
|
|
24
|
+
muted?: boolean;
|
|
25
|
+
loop?: boolean;
|
|
26
|
+
controls?: boolean;
|
|
27
|
+
/** false 时播放器不接收指针事件(点击穿透到下层,如首页预览卡片) */
|
|
28
|
+
interactive?: boolean;
|
|
29
|
+
playsinline?: boolean;
|
|
30
|
+
/** 初始倍速(0.25–4)。运行时改倍速走句柄的 `setPlaybackRate`(ADR-037) */
|
|
31
|
+
playbackRate?: number;
|
|
32
|
+
/**
|
|
33
|
+
* 初始音量(0–1)。运行时改音量走句柄的 `setVolume`。
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ **`setVolume()` 替代不了它**:句柄要 mount 之后才有,中间有一个窗口停在默认音量 1。
|
|
36
|
+
*/
|
|
37
|
+
volume?: number;
|
|
38
|
+
/**
|
|
39
|
+
* 起播位置(秒,≥ 0)。CMS 场景「从 1:30 开始」写 `startTime={90}`。
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ **`seek()` 替代不了它**:那是**播起来之后**才跳,观众先看到第 0 帧再被拽走。
|
|
42
|
+
*/
|
|
43
|
+
startTime?: number;
|
|
44
|
+
/**
|
|
45
|
+
* 预加载策略。`none` / `metadata`(默认)/ `auto`。
|
|
46
|
+
*
|
|
47
|
+
* ⚠️ **运行时没有任何替代路径** —— 它是 `<video>` 创建期属性,播放器建好之后改它没有意义。
|
|
48
|
+
*/
|
|
49
|
+
preload?: 'none' | 'metadata' | 'auto';
|
|
50
|
+
/** 封面。字符串按 URL 处理,对象形态可带 `fit` / `loading` */
|
|
51
|
+
poster?: string | PosterConfig;
|
|
52
|
+
/**
|
|
53
|
+
* 暂停时盖在画面中央的静态图(ADR-043)。字符串按 URL 处理,对象形态可带
|
|
54
|
+
* `fit`(默认 `contain`,与封面的 `cover` 不同 —— 暂停图通常是完整构图)/ `closable`。
|
|
55
|
+
*
|
|
56
|
+
* **带倒计时 / 跳转 / 推荐列表的不在这里做** —— 那是业务调度,归团队封装层(ADR-025)。
|
|
57
|
+
*/
|
|
58
|
+
pauseImage?: string | PauseImageConfig;
|
|
59
|
+
preset?: PresetName;
|
|
60
|
+
/** 传给 xgplayer 的 i18n 配置(覆盖层文案) */
|
|
61
|
+
locale?: LocaleConfig;
|
|
62
|
+
/** 弹幕配置(ADR-028)。enabled 时注册渲染引擎;数据经 pushDanmaku 推入 */
|
|
63
|
+
danmaku?: DanmakuConfig;
|
|
64
|
+
/**
|
|
65
|
+
* ⚠️ **inline 模式无作用。**
|
|
66
|
+
*
|
|
67
|
+
* `debug` 的语义是「打开 iframe 通信层(Penpal)日志」,而 inline 直连 player-core,
|
|
68
|
+
* 没有 iframe、没有 Penpal。xgplayer 3.0.26 也没有可映射的 debug / logLevel 字段,
|
|
69
|
+
* 按 ADR-029「无真实需求驱动,不投机造能力」不为 inline 硬造一套日志。
|
|
70
|
+
*
|
|
71
|
+
* 保留该 prop 只为与 react-frame / vue-frame 的 props 面对齐(cross-mode-parity)。
|
|
72
|
+
* 需要 iframe 通信日志请用 `@video-lab/react-frame`。
|
|
73
|
+
*/
|
|
74
|
+
debug?: boolean;
|
|
75
|
+
/** 追加到容器根节点的 className(内置 `sentinel-video` 保留) */
|
|
76
|
+
className?: string;
|
|
77
|
+
/** 追加到容器根节点的内联样式(覆盖内置布局样式) */
|
|
78
|
+
style?: CSSProperties;
|
|
79
|
+
/**
|
|
80
|
+
* SSR / 挂载前的占位内容(player 要 DOM 容器才能建)。
|
|
81
|
+
*
|
|
82
|
+
* ⚠️ **inline 面没有默认占位** —— 不传 children 时那一帧是透明的(外层 div 无背景色)。
|
|
83
|
+
* 这里曾照抄 react-frame 的 JSDoc 写「默认黑底占位」,但**那个默认只有 iframe 面有**:
|
|
84
|
+
* 那边要等异步握手 + iframe 里 336KB JS,占位窗口是几百毫秒;inline 的
|
|
85
|
+
* `createPlayer` 是同步的,窗口只有一帧,给默认反而是 inline 两面的行为差异
|
|
86
|
+
*(`vue/src/VideoPlayer.vue` 里把这个决定登记对了)。
|
|
87
|
+
*/
|
|
88
|
+
children?: ReactNode;
|
|
89
|
+
/** 完整的契约事件流,供遥测适配器使用;不裁剪 payload。 */
|
|
90
|
+
onPlayerEvent?: (event: PlayerEvent) => void;
|
|
91
|
+
/**
|
|
92
|
+
* metadata 就绪。`quality` 是可切档位清单(单码率源为空数组),团队层据此渲染清晰度菜单;
|
|
93
|
+
* `subtitles` 是可切字幕轨清单(无字幕源为空/缺省,契约 1.0.0 起可选,ADR-027),团队层据此渲染字幕菜单。
|
|
94
|
+
*/
|
|
95
|
+
onReady?: (payload: {
|
|
96
|
+
duration: number;
|
|
97
|
+
quality: QualityLevel[];
|
|
98
|
+
subtitles?: SubtitleTrackInfo[];
|
|
99
|
+
}) => void;
|
|
100
|
+
onPlay?: () => void;
|
|
101
|
+
onPause?: () => void;
|
|
102
|
+
onEnded?: () => void;
|
|
103
|
+
onTimeUpdate?: (payload: {
|
|
104
|
+
time: number;
|
|
105
|
+
duration: number;
|
|
106
|
+
}) => void;
|
|
107
|
+
onVolumeChange?: (payload: {
|
|
108
|
+
volume: number;
|
|
109
|
+
muted: boolean;
|
|
110
|
+
}) => void;
|
|
111
|
+
/** 开始跳转(拖进度条)。`time` = 目标位置,团队层据此显示"跳转中" */
|
|
112
|
+
onSeeking?: (payload: {
|
|
113
|
+
time: number;
|
|
114
|
+
}) => void;
|
|
115
|
+
/** 跳转完成。`time` = 实际落点(可能被钳制到有效范围内) */
|
|
116
|
+
onSeeked?: (payload: {
|
|
117
|
+
time: number;
|
|
118
|
+
}) => void;
|
|
119
|
+
/**
|
|
120
|
+
* 缓冲开始(HTML5 `waiting` 透传)。
|
|
121
|
+
*
|
|
122
|
+
* 和 {@link onStalled} 区分:这个是**瞬间信号**,给 UI 立刻转圈用;`stalled` 是
|
|
123
|
+
* HealthMonitor **测量**后的结果,带时长、能抓到冻帧型隐性卡顿(那种情况下
|
|
124
|
+
* xgplayer 根本不发 `waiting`)。做 UI 用这个,做埋点用 `stalled`。
|
|
125
|
+
*/
|
|
126
|
+
onWaiting?: () => void;
|
|
127
|
+
/** 缓冲结束、继续播放(HTML5 `playing` 透传)。和 {@link onWaiting} 配对收起转圈 */
|
|
128
|
+
onPlaying?: () => void;
|
|
129
|
+
/** 清晰度切换回报。`auto` = true 是 ABR 自适应切的,false 是用户手动切的 */
|
|
130
|
+
onQualityChange?: (payload: {
|
|
131
|
+
level: number;
|
|
132
|
+
auto: boolean;
|
|
133
|
+
}) => void;
|
|
134
|
+
/** 字幕轨切换回报(ADR-027)。`id` = 当前轨 id,`null` = 已关闭字幕 */
|
|
135
|
+
onSubtitleChange?: (payload: {
|
|
136
|
+
id: number | null;
|
|
137
|
+
}) => void;
|
|
138
|
+
onError?: (payload: PlayerError) => void;
|
|
139
|
+
onAutoplayBlocked?: () => void;
|
|
140
|
+
onReconnectStart?: (payload: {
|
|
141
|
+
attempt: number;
|
|
142
|
+
maxAttempts: number;
|
|
143
|
+
}) => void;
|
|
144
|
+
onReconnectSuccess?: () => void;
|
|
145
|
+
onReconnectFailed?: () => void;
|
|
146
|
+
onCompatWarning?: (payload: {
|
|
147
|
+
code: string;
|
|
148
|
+
message: string;
|
|
149
|
+
ua: string;
|
|
150
|
+
}) => void;
|
|
151
|
+
/**
|
|
152
|
+
* 测量过的卡顿(HealthMonitor,契约 1.1.0 / ADR-026)。`phase='end'` 带 `durationMs`,
|
|
153
|
+
* 团队层据此算卡顿率上报埋点。和裸 waiting/playing 不同——这是测量后的结果。
|
|
154
|
+
*/
|
|
155
|
+
onStalled?: (payload: {
|
|
156
|
+
phase: 'start' | 'end';
|
|
157
|
+
position: number;
|
|
158
|
+
durationMs?: number;
|
|
159
|
+
}) => void;
|
|
160
|
+
/**
|
|
161
|
+
* 聚合播放状态变化(契约 1.9.0 / ADR-043)。
|
|
162
|
+
*
|
|
163
|
+
* **它不是又一个原始信号,是上面那一堆的唯一结论。** 团队层写
|
|
164
|
+
* `if (!playable) 盖上自己的 loading` 就覆盖了全部「播不了」的成因,不用自己拼装
|
|
165
|
+
* `ready`/`waiting`/`stalled`/`reconnect*`/`autoplayBlocked`/`error` 再兜一个超时。
|
|
166
|
+
* `recoverable` 决定盖 loading(等)还是露重试入口(不等);`reason='degraded'`
|
|
167
|
+
* 是唯一 `playable` 仍为 `true` 的值(掉帧严重但画面还在动,可以忽略)。
|
|
168
|
+
*/
|
|
169
|
+
onPlayableChange?: (payload: {
|
|
170
|
+
playable: boolean;
|
|
171
|
+
reason: PlayableReason;
|
|
172
|
+
recoverable: boolean;
|
|
173
|
+
}) => void;
|
|
174
|
+
/**
|
|
175
|
+
* 内核健康 —— **非致命内核诊断的聚合**(契约 4.1.0 / ADR-062 / issue #391)。
|
|
176
|
+
*
|
|
177
|
+
* 它补的是「**为什么**卡住」那一格:`stalled` 有时长没成因,`playableChange`
|
|
178
|
+
* 有结论没成因,而最典型的直播故障(断流)下内核**根本不升 fatal**、`error` 一条不发。
|
|
179
|
+
* 实测:注入立即断流后 60 秒内 hls.js 报了 10,137 条非致命诊断,消费方零 `error`。
|
|
180
|
+
*
|
|
181
|
+
* 固定 5 秒窗口聚合,窗口内不论内核报多少条最多产出一条。`degraded: false`
|
|
182
|
+
* 是**收尾**那条(安静了一整个窗口),团队层据此结束一段故障记录,不用自己兜超时。
|
|
183
|
+
*
|
|
184
|
+
* ⚠️ **拿它做上报和排障,不要拿它驱动 UI** —— 首条最迟 5 秒后到。UI 归 `playableChange`。
|
|
185
|
+
*
|
|
186
|
+
* ⚠️ **FLV 源永远不发**:flv.js 没有非致命档,它的每条错误都已经走了 `error`。
|
|
187
|
+
* 这是已知边界,不是漏实现(ADR-062 ④)。
|
|
188
|
+
*/
|
|
189
|
+
onKernelHealth?: (payload: KernelHealthPayload) => void;
|
|
190
|
+
/**
|
|
191
|
+
* FLV 音频数据轨连续性(ADR-078 / #520)。
|
|
192
|
+
*
|
|
193
|
+
* 仅表示音频数据没有继续进入播放器,不是内容静音、用户静音或设备音量。
|
|
194
|
+
* `degraded: false` 是同一段缺口结束时的收尾信号。
|
|
195
|
+
*/
|
|
196
|
+
onAudioHealth?: (payload: AudioHealthPayload) => void;
|
|
197
|
+
/** 缓冲余量在净流失 / 已恢复(ADR-071)。**契约里唯一一条「事情还没坏」的信号** */
|
|
198
|
+
onBufferHealth?: (payload: BufferHealthPayload) => void;
|
|
199
|
+
/**
|
|
200
|
+
* 播放上下文变了(ADR-074)。**上报适配器的初值与增量。**
|
|
201
|
+
*
|
|
202
|
+
* ⚠️ payload 里的 `position` 是**发出这一刻**的值,不参与触发判据。
|
|
203
|
+
* 要「出错那一刻」的位置,请在错误回调里同步取上下文,别读缓存下来的这一份。
|
|
204
|
+
*/
|
|
205
|
+
onContextChange?: (payload: PlaybackContextPayload) => void;
|
|
206
|
+
/** 选源结论;用于支持矩阵,不表示首帧或 QoE 成功。 */
|
|
207
|
+
onSourceRoute?: (payload: SourceRouteEventPayload) => void;
|
|
208
|
+
/**
|
|
209
|
+
* 首帧可见(ADR-075)。来自 xgplayer 一直在算、而 SDK 从没读过的 `XGLogger`。
|
|
210
|
+
*
|
|
211
|
+
* ⚠️ 和 `pnpm test:e2e:kpi`(#148)量的不是一件事:那条含页面加载,这条是播放器内部口径。
|
|
212
|
+
*/
|
|
213
|
+
onFirstFrame?: (payload: FirstFramePayload) => void;
|
|
214
|
+
/**
|
|
215
|
+
* 画面冻结(ADR-075):**缓冲是够的,但解码器出不了新帧**。**仅 PC** ——
|
|
216
|
+
* 上游 `FpsDetect` 按 `sniffer.device` 分档装载,手机上结构性不触发(已登记上行缺口)。
|
|
217
|
+
*
|
|
218
|
+
* ⚠️ 它不是掉帧率,也不与 `stalled` 重复(后者是缓冲驱动的等待)。
|
|
219
|
+
*/
|
|
220
|
+
onFrameFreeze?: (payload: FrameFreezePayload) => void;
|
|
221
|
+
/**
|
|
222
|
+
* 用户动作(ADR-075),经白名单过滤,只含**发生在 SDK 内部**的那些。
|
|
223
|
+
*
|
|
224
|
+
* 契约里已有 `play` / `volumechange` / `seeking` / `qualitychange`,而它们回答不了
|
|
225
|
+
* **「是谁发起的」** —— 用户点的还是代码调的。本事件补的就是这一格。
|
|
226
|
+
*
|
|
227
|
+
* ⚠️ 传 `controls: false` 时绝大多数动作不再发出(发出方是播放器自己的控件)。
|
|
228
|
+
*/
|
|
229
|
+
onUserAction?: (payload: UserActionPayload) => void;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 通过 `ref` 暴露的命令方法(useImperativeHandle)。
|
|
233
|
+
*
|
|
234
|
+
* 直连 player-core,方法签名沿用 `PlayerCoreHandle` 的真实同步 / 异步形态
|
|
235
|
+
* (只有 `play` 是异步,其余命令同步生效)——不像 iframe 系因为跨 postMessage 而全异步。
|
|
236
|
+
* 方法名和 react-frame 保持一致,团队封装层的调用点尽量复用。
|
|
237
|
+
*/
|
|
238
|
+
interface VideoPlayerHandle {
|
|
239
|
+
play(): Promise<void>;
|
|
240
|
+
pause(): void;
|
|
241
|
+
seek(time: number): void;
|
|
242
|
+
setMuted(muted: boolean): void;
|
|
243
|
+
setVolume(volume: number): void;
|
|
244
|
+
setPlaybackRate(rate: number): void;
|
|
245
|
+
/** 进入全屏(容器托管,不碰 video 原生入口,iOS 微信崩溃入口由 FullscreenGuard 中和) */
|
|
246
|
+
enterFullscreen(): Promise<void>;
|
|
247
|
+
/** 退出全屏 */
|
|
248
|
+
exitFullscreen(): Promise<void>;
|
|
249
|
+
setQuality(level: number | 'auto'): void;
|
|
250
|
+
/** 切字幕轨(ADR-027)。`id` = `onReady` 的 `subtitles[].id`;`'off'` 关闭字幕。直连 player-core,同步生效 */
|
|
251
|
+
setSubtitle(id: number | 'off'): void;
|
|
252
|
+
/** 运行时切换语言,不重建播放器、不丢进度。通常不必手动调 —— 改 `locale` prop 即自动下发 */
|
|
253
|
+
setLocale(locale: LocaleConfig): void;
|
|
254
|
+
/** 逐条推弹幕(直播,ADR-028)。渲染归 SDK,数据源/发送框归团队层 */
|
|
255
|
+
pushDanmaku(item: DanmakuItem): void;
|
|
256
|
+
/** 开关弹幕渲染(ADR-028) */
|
|
257
|
+
setDanmakuEnabled(enabled: boolean): void;
|
|
258
|
+
/** 清空屏上弹幕(ADR-028) */
|
|
259
|
+
clearDanmaku(): void;
|
|
260
|
+
reconnect(options?: {
|
|
261
|
+
resetCounter?: boolean;
|
|
262
|
+
}): void;
|
|
263
|
+
destroy(): void;
|
|
264
|
+
getCurrentTime(): number;
|
|
265
|
+
getDuration(): number;
|
|
266
|
+
/**
|
|
267
|
+
* 取**这一刻**的播放上下文(ADR-074):这一次播放是谁、用的哪个内核、
|
|
268
|
+
* 播的哪个候选源、当前哪一档、播到了哪。**消费方在 SDK 外面拿不到这几格。**
|
|
269
|
+
*
|
|
270
|
+
* **和 `onContextChange` 是互补的,不是二选一**:事件给初值与增量,本方法给
|
|
271
|
+
* 「出错那一刻」的精确值 —— 在 `onError` 里**同步**调它,拿到的 `position` 就是出错位置。
|
|
272
|
+
*
|
|
273
|
+
* 未挂载 / 已销毁时返回 `null`。
|
|
274
|
+
*
|
|
275
|
+
* ⚠️ **`null` 在这里是对的降级值,而 ADR-066 拒绝把 `destroyed` 摆上句柄时说「没有对的降级值」——
|
|
276
|
+
* 两者不矛盾**:`destroyed` 的候选降级值是 `false`,那会让「活着」和「还没活」共用一个读数;
|
|
277
|
+
* 而 `null` 的含义是「还没有上下文」,不与任何真实上下文冲突。
|
|
278
|
+
*/
|
|
279
|
+
getPlaybackContext(): PlaybackContext | null;
|
|
280
|
+
/** 高级场景逃生舱:拿到底层 player-core 句柄,未挂载时为 null */
|
|
281
|
+
getPlayerHandle(): PlayerHandle | null;
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/VideoPlayer.d.ts
|
|
285
|
+
/**
|
|
286
|
+
* VideoPlayer · React inline 高性能模式(SDK 消费面之一)
|
|
287
|
+
*
|
|
288
|
+
* 直连 player-core + player-ui,不走 iframe——首帧最快(~200ms),数据路径和 iframe 系不同。
|
|
289
|
+
*
|
|
290
|
+
* **覆盖层用 player-ui 的 `<sentinel-*>` custom elements**(#120 · R1 · PR B),不再是 React 组件。
|
|
291
|
+
* 消费方 API 一个都没变 —— props / 事件 / 句柄全部原样。变的只有两件内部事:
|
|
292
|
+
* ① i18n 解析上移到这一层(元素只收解析好的字符串,player-ui 不再需要状态注入机制);
|
|
293
|
+
* ② 覆盖层的样式钩子从全局 class 变成 shadow DOM 的 `::part()`(见 USER-GUIDE)。
|
|
294
|
+
* 只做技术:组装播放器、把 props 桥接成命令、把事件桥接成 `onXxx` 回调、渲染 SDK 自带的
|
|
295
|
+
* Poster / Loading / Error 覆盖层。不含任何团队品牌 UI(ADR-021)——那些在业务封装层。
|
|
296
|
+
* props / 事件面和 react-frame 对齐(cross-mode-parity)。
|
|
297
|
+
*
|
|
298
|
+
* 【关于默认值】刻意不给 autoplay / muted / loop / controls / playsinline / interactive
|
|
299
|
+
* 设默认值:这些字段同时受 preset 控制,一旦在这里强塞就变成"显式值"把 preset 覆盖掉
|
|
300
|
+
*(resolvePreset 的语义是 undefined 才回落到 preset)。没传的 prop 保持 undefined。
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* const ref = useRef<VideoPlayerHandle>(null)
|
|
304
|
+
* <VideoPlayer ref={ref} source="https://cdn/a.m3u8" muted onReady={...} />
|
|
305
|
+
* ref.current?.play()
|
|
306
|
+
*/
|
|
307
|
+
declare const VideoPlayer: import("react").ForwardRefExoticComponent<VideoPlayerProps & import("react").RefAttributes<VideoPlayerHandle>>;
|
|
308
|
+
//#endregion
|
|
309
|
+
export { type DanmakuConfig, type DanmakuItem, type MediaSource, type PlayerError, type PlayerHandle, type QualityLevel, type SubtitleTrackInfo, VideoPlayer, type VideoPlayerHandle, type VideoPlayerProps };
|
|
310
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/VideoPlayer.tsx"],"mappings":";;;;;;;;;;KAgCY,eAAe;;;;;;;;;UAoBV;;EAEf,QAAQ;EACR;EACA;EACA;EACA;;EAEA;EACA;;EAEA;;;;;;EAMA;;;;;;EAMA;;;;;;EAMA;;EAEA,kBAAkB;;;;;;;EAOlB,sBAAsB;EACtB,SAAS;;EAET,SAAS;;EAET,UAAU;;;;;;;;;;;EAWV;;EAGA;;EAEA,QAAQ;;;;;;;;;;EAUR,WAAW;;EAIX,iBAAiB,OAAO;;;;;EAKxB,WAAW;IACT;IACA,SAAS;IACT,YAAY;;EAEd;EACA;EACA;EACA,gBAAgB;IAAW;IAAc;;EACzC,kBAAkB;IAAW;IAAgB;;;EAE7C,aAAa;IAAW;;;EAExB,YAAY;IAAW;;;;;;;;;EAQvB;;EAEA;;EAEA,mBAAmB;IAAW;IAAe;;;EAE7C,oBAAoB;IAAW;;EAC/B,WAAW,SAAS;EACpB;EACA,oBAAoB;IAAW;IAAiB;;EAChD;EACA;EACA,mBAAmB;IAAW;IAAc;IAAiB;;;;;;EAK7D,aAAa;IAAW;IAAwB;IAAkB;;;;;;;;;;;EAUlE,oBAAoB;IAClB;IACA,QAAQ;IACR;;;;;;;;;;;;;;;;;EAiBF,kBAAkB,SAAS;;;;;;;EAO3B,iBAAiB,SAAS;;EAE1B,kBAAkB,SAAS;;;;;;;EAQ3B,mBAAmB,SAAS;;EAE5B,iBAAiB,SAAS;;;;;;EAM1B,gBAAgB,SAAS;;;;;;;EAOzB,iBAAiB,SAAS;;;;;;;;;EAS1B,gBAAgB,SAAS;;;;;;;;;UAUV;EACf,QAAQ;EACR;EACA,KAAK;EACL,SAAS;EACT,UAAU;EACV,gBAAgB;;EAEhB,mBAAmB;;EAEnB,kBAAkB;EAClB,WAAW;;EAEX,YAAY;;EAGZ,UAAU,QAAQ;;EAElB,YAAY,MAAM;;EAElB,kBAAkB;;EAElB;EACA,UAAU;IAAY;;EACtB;EACA;EACA;;;;;;;;;;;;;;EAcA,sBAAsB;;EAEtB,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;cCvQR,6BAAW,0BAAA,mCAAA,cAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { CSSProperties, ReactNode } from "react";
|
|
2
|
+
import { AudioHealthPayload, BufferHealthPayload, DanmakuConfig, DanmakuItem, FirstFramePayload, FrameFreezePayload, KernelHealthPayload, LocaleConfig, MediaSource, PauseImageConfig, PlayableReason, PlaybackContext, PlaybackContextPayload, PlayerError, PlayerEvent, PosterConfig, PresetName, QualityLevel, SourceRouteEventPayload, SubtitleTrackInfo, UserActionPayload } from "@video-lab/protocol";
|
|
3
|
+
import { PlayerCoreHandle } from "@video-lab/player-core";
|
|
4
|
+
//#region src/types.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* 消费方拿到的底层句柄(逃生舱)。
|
|
7
|
+
*
|
|
8
|
+
* inline 模式直连 player-core,所以就是 `PlayerCoreHandle`——不像 iframe 系隔着 frame-core。
|
|
9
|
+
* `getPlayerHandle()` 返回它,给需要绕过 React 直接下命令的高级场景。
|
|
10
|
+
*/
|
|
11
|
+
type PlayerHandle = PlayerCoreHandle;
|
|
12
|
+
/**
|
|
13
|
+
* `<VideoPlayer />` 的 props(inline 高性能模式)。
|
|
14
|
+
*
|
|
15
|
+
* 刻意和 react-frame 的 `VideoPlayerFrameProps` 对齐(去掉 iframe 专属的 origin / version)——
|
|
16
|
+
* 团队封装层在 inline / iframe 两种模式间切换时,props 面尽量一致(cross-mode-parity)。
|
|
17
|
+
* 播放字段最终拼进 player-core 的 `PlayerConfig`;事件走 React 惯例 `onCamelCase` 回调,
|
|
18
|
+
* payload 形状对齐 protocol 的 PlayerEvent。
|
|
19
|
+
*/
|
|
20
|
+
interface VideoPlayerProps {
|
|
21
|
+
/** 视频源。字符串按 URL 处理,对象可带 type / live / hls 等 */
|
|
22
|
+
source: MediaSource | string;
|
|
23
|
+
autoplay?: boolean;
|
|
24
|
+
muted?: boolean;
|
|
25
|
+
loop?: boolean;
|
|
26
|
+
controls?: boolean;
|
|
27
|
+
/** false 时播放器不接收指针事件(点击穿透到下层,如首页预览卡片) */
|
|
28
|
+
interactive?: boolean;
|
|
29
|
+
playsinline?: boolean;
|
|
30
|
+
/** 初始倍速(0.25–4)。运行时改倍速走句柄的 `setPlaybackRate`(ADR-037) */
|
|
31
|
+
playbackRate?: number;
|
|
32
|
+
/**
|
|
33
|
+
* 初始音量(0–1)。运行时改音量走句柄的 `setVolume`。
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ **`setVolume()` 替代不了它**:句柄要 mount 之后才有,中间有一个窗口停在默认音量 1。
|
|
36
|
+
*/
|
|
37
|
+
volume?: number;
|
|
38
|
+
/**
|
|
39
|
+
* 起播位置(秒,≥ 0)。CMS 场景「从 1:30 开始」写 `startTime={90}`。
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ **`seek()` 替代不了它**:那是**播起来之后**才跳,观众先看到第 0 帧再被拽走。
|
|
42
|
+
*/
|
|
43
|
+
startTime?: number;
|
|
44
|
+
/**
|
|
45
|
+
* 预加载策略。`none` / `metadata`(默认)/ `auto`。
|
|
46
|
+
*
|
|
47
|
+
* ⚠️ **运行时没有任何替代路径** —— 它是 `<video>` 创建期属性,播放器建好之后改它没有意义。
|
|
48
|
+
*/
|
|
49
|
+
preload?: 'none' | 'metadata' | 'auto';
|
|
50
|
+
/** 封面。字符串按 URL 处理,对象形态可带 `fit` / `loading` */
|
|
51
|
+
poster?: string | PosterConfig;
|
|
52
|
+
/**
|
|
53
|
+
* 暂停时盖在画面中央的静态图(ADR-043)。字符串按 URL 处理,对象形态可带
|
|
54
|
+
* `fit`(默认 `contain`,与封面的 `cover` 不同 —— 暂停图通常是完整构图)/ `closable`。
|
|
55
|
+
*
|
|
56
|
+
* **带倒计时 / 跳转 / 推荐列表的不在这里做** —— 那是业务调度,归团队封装层(ADR-025)。
|
|
57
|
+
*/
|
|
58
|
+
pauseImage?: string | PauseImageConfig;
|
|
59
|
+
preset?: PresetName;
|
|
60
|
+
/** 传给 xgplayer 的 i18n 配置(覆盖层文案) */
|
|
61
|
+
locale?: LocaleConfig;
|
|
62
|
+
/** 弹幕配置(ADR-028)。enabled 时注册渲染引擎;数据经 pushDanmaku 推入 */
|
|
63
|
+
danmaku?: DanmakuConfig;
|
|
64
|
+
/**
|
|
65
|
+
* ⚠️ **inline 模式无作用。**
|
|
66
|
+
*
|
|
67
|
+
* `debug` 的语义是「打开 iframe 通信层(Penpal)日志」,而 inline 直连 player-core,
|
|
68
|
+
* 没有 iframe、没有 Penpal。xgplayer 3.0.26 也没有可映射的 debug / logLevel 字段,
|
|
69
|
+
* 按 ADR-029「无真实需求驱动,不投机造能力」不为 inline 硬造一套日志。
|
|
70
|
+
*
|
|
71
|
+
* 保留该 prop 只为与 react-frame / vue-frame 的 props 面对齐(cross-mode-parity)。
|
|
72
|
+
* 需要 iframe 通信日志请用 `@video-lab/react-frame`。
|
|
73
|
+
*/
|
|
74
|
+
debug?: boolean;
|
|
75
|
+
/** 追加到容器根节点的 className(内置 `sentinel-video` 保留) */
|
|
76
|
+
className?: string;
|
|
77
|
+
/** 追加到容器根节点的内联样式(覆盖内置布局样式) */
|
|
78
|
+
style?: CSSProperties;
|
|
79
|
+
/**
|
|
80
|
+
* SSR / 挂载前的占位内容(player 要 DOM 容器才能建)。
|
|
81
|
+
*
|
|
82
|
+
* ⚠️ **inline 面没有默认占位** —— 不传 children 时那一帧是透明的(外层 div 无背景色)。
|
|
83
|
+
* 这里曾照抄 react-frame 的 JSDoc 写「默认黑底占位」,但**那个默认只有 iframe 面有**:
|
|
84
|
+
* 那边要等异步握手 + iframe 里 336KB JS,占位窗口是几百毫秒;inline 的
|
|
85
|
+
* `createPlayer` 是同步的,窗口只有一帧,给默认反而是 inline 两面的行为差异
|
|
86
|
+
*(`vue/src/VideoPlayer.vue` 里把这个决定登记对了)。
|
|
87
|
+
*/
|
|
88
|
+
children?: ReactNode;
|
|
89
|
+
/** 完整的契约事件流,供遥测适配器使用;不裁剪 payload。 */
|
|
90
|
+
onPlayerEvent?: (event: PlayerEvent) => void;
|
|
91
|
+
/**
|
|
92
|
+
* metadata 就绪。`quality` 是可切档位清单(单码率源为空数组),团队层据此渲染清晰度菜单;
|
|
93
|
+
* `subtitles` 是可切字幕轨清单(无字幕源为空/缺省,契约 1.0.0 起可选,ADR-027),团队层据此渲染字幕菜单。
|
|
94
|
+
*/
|
|
95
|
+
onReady?: (payload: {
|
|
96
|
+
duration: number;
|
|
97
|
+
quality: QualityLevel[];
|
|
98
|
+
subtitles?: SubtitleTrackInfo[];
|
|
99
|
+
}) => void;
|
|
100
|
+
onPlay?: () => void;
|
|
101
|
+
onPause?: () => void;
|
|
102
|
+
onEnded?: () => void;
|
|
103
|
+
onTimeUpdate?: (payload: {
|
|
104
|
+
time: number;
|
|
105
|
+
duration: number;
|
|
106
|
+
}) => void;
|
|
107
|
+
onVolumeChange?: (payload: {
|
|
108
|
+
volume: number;
|
|
109
|
+
muted: boolean;
|
|
110
|
+
}) => void;
|
|
111
|
+
/** 开始跳转(拖进度条)。`time` = 目标位置,团队层据此显示"跳转中" */
|
|
112
|
+
onSeeking?: (payload: {
|
|
113
|
+
time: number;
|
|
114
|
+
}) => void;
|
|
115
|
+
/** 跳转完成。`time` = 实际落点(可能被钳制到有效范围内) */
|
|
116
|
+
onSeeked?: (payload: {
|
|
117
|
+
time: number;
|
|
118
|
+
}) => void;
|
|
119
|
+
/**
|
|
120
|
+
* 缓冲开始(HTML5 `waiting` 透传)。
|
|
121
|
+
*
|
|
122
|
+
* 和 {@link onStalled} 区分:这个是**瞬间信号**,给 UI 立刻转圈用;`stalled` 是
|
|
123
|
+
* HealthMonitor **测量**后的结果,带时长、能抓到冻帧型隐性卡顿(那种情况下
|
|
124
|
+
* xgplayer 根本不发 `waiting`)。做 UI 用这个,做埋点用 `stalled`。
|
|
125
|
+
*/
|
|
126
|
+
onWaiting?: () => void;
|
|
127
|
+
/** 缓冲结束、继续播放(HTML5 `playing` 透传)。和 {@link onWaiting} 配对收起转圈 */
|
|
128
|
+
onPlaying?: () => void;
|
|
129
|
+
/** 清晰度切换回报。`auto` = true 是 ABR 自适应切的,false 是用户手动切的 */
|
|
130
|
+
onQualityChange?: (payload: {
|
|
131
|
+
level: number;
|
|
132
|
+
auto: boolean;
|
|
133
|
+
}) => void;
|
|
134
|
+
/** 字幕轨切换回报(ADR-027)。`id` = 当前轨 id,`null` = 已关闭字幕 */
|
|
135
|
+
onSubtitleChange?: (payload: {
|
|
136
|
+
id: number | null;
|
|
137
|
+
}) => void;
|
|
138
|
+
onError?: (payload: PlayerError) => void;
|
|
139
|
+
onAutoplayBlocked?: () => void;
|
|
140
|
+
onReconnectStart?: (payload: {
|
|
141
|
+
attempt: number;
|
|
142
|
+
maxAttempts: number;
|
|
143
|
+
}) => void;
|
|
144
|
+
onReconnectSuccess?: () => void;
|
|
145
|
+
onReconnectFailed?: () => void;
|
|
146
|
+
onCompatWarning?: (payload: {
|
|
147
|
+
code: string;
|
|
148
|
+
message: string;
|
|
149
|
+
ua: string;
|
|
150
|
+
}) => void;
|
|
151
|
+
/**
|
|
152
|
+
* 测量过的卡顿(HealthMonitor,契约 1.1.0 / ADR-026)。`phase='end'` 带 `durationMs`,
|
|
153
|
+
* 团队层据此算卡顿率上报埋点。和裸 waiting/playing 不同——这是测量后的结果。
|
|
154
|
+
*/
|
|
155
|
+
onStalled?: (payload: {
|
|
156
|
+
phase: 'start' | 'end';
|
|
157
|
+
position: number;
|
|
158
|
+
durationMs?: number;
|
|
159
|
+
}) => void;
|
|
160
|
+
/**
|
|
161
|
+
* 聚合播放状态变化(契约 1.9.0 / ADR-043)。
|
|
162
|
+
*
|
|
163
|
+
* **它不是又一个原始信号,是上面那一堆的唯一结论。** 团队层写
|
|
164
|
+
* `if (!playable) 盖上自己的 loading` 就覆盖了全部「播不了」的成因,不用自己拼装
|
|
165
|
+
* `ready`/`waiting`/`stalled`/`reconnect*`/`autoplayBlocked`/`error` 再兜一个超时。
|
|
166
|
+
* `recoverable` 决定盖 loading(等)还是露重试入口(不等);`reason='degraded'`
|
|
167
|
+
* 是唯一 `playable` 仍为 `true` 的值(掉帧严重但画面还在动,可以忽略)。
|
|
168
|
+
*/
|
|
169
|
+
onPlayableChange?: (payload: {
|
|
170
|
+
playable: boolean;
|
|
171
|
+
reason: PlayableReason;
|
|
172
|
+
recoverable: boolean;
|
|
173
|
+
}) => void;
|
|
174
|
+
/**
|
|
175
|
+
* 内核健康 —— **非致命内核诊断的聚合**(契约 4.1.0 / ADR-062 / issue #391)。
|
|
176
|
+
*
|
|
177
|
+
* 它补的是「**为什么**卡住」那一格:`stalled` 有时长没成因,`playableChange`
|
|
178
|
+
* 有结论没成因,而最典型的直播故障(断流)下内核**根本不升 fatal**、`error` 一条不发。
|
|
179
|
+
* 实测:注入立即断流后 60 秒内 hls.js 报了 10,137 条非致命诊断,消费方零 `error`。
|
|
180
|
+
*
|
|
181
|
+
* 固定 5 秒窗口聚合,窗口内不论内核报多少条最多产出一条。`degraded: false`
|
|
182
|
+
* 是**收尾**那条(安静了一整个窗口),团队层据此结束一段故障记录,不用自己兜超时。
|
|
183
|
+
*
|
|
184
|
+
* ⚠️ **拿它做上报和排障,不要拿它驱动 UI** —— 首条最迟 5 秒后到。UI 归 `playableChange`。
|
|
185
|
+
*
|
|
186
|
+
* ⚠️ **FLV 源永远不发**:flv.js 没有非致命档,它的每条错误都已经走了 `error`。
|
|
187
|
+
* 这是已知边界,不是漏实现(ADR-062 ④)。
|
|
188
|
+
*/
|
|
189
|
+
onKernelHealth?: (payload: KernelHealthPayload) => void;
|
|
190
|
+
/**
|
|
191
|
+
* FLV 音频数据轨连续性(ADR-078 / #520)。
|
|
192
|
+
*
|
|
193
|
+
* 仅表示音频数据没有继续进入播放器,不是内容静音、用户静音或设备音量。
|
|
194
|
+
* `degraded: false` 是同一段缺口结束时的收尾信号。
|
|
195
|
+
*/
|
|
196
|
+
onAudioHealth?: (payload: AudioHealthPayload) => void;
|
|
197
|
+
/** 缓冲余量在净流失 / 已恢复(ADR-071)。**契约里唯一一条「事情还没坏」的信号** */
|
|
198
|
+
onBufferHealth?: (payload: BufferHealthPayload) => void;
|
|
199
|
+
/**
|
|
200
|
+
* 播放上下文变了(ADR-074)。**上报适配器的初值与增量。**
|
|
201
|
+
*
|
|
202
|
+
* ⚠️ payload 里的 `position` 是**发出这一刻**的值,不参与触发判据。
|
|
203
|
+
* 要「出错那一刻」的位置,请在错误回调里同步取上下文,别读缓存下来的这一份。
|
|
204
|
+
*/
|
|
205
|
+
onContextChange?: (payload: PlaybackContextPayload) => void;
|
|
206
|
+
/** 选源结论;用于支持矩阵,不表示首帧或 QoE 成功。 */
|
|
207
|
+
onSourceRoute?: (payload: SourceRouteEventPayload) => void;
|
|
208
|
+
/**
|
|
209
|
+
* 首帧可见(ADR-075)。来自 xgplayer 一直在算、而 SDK 从没读过的 `XGLogger`。
|
|
210
|
+
*
|
|
211
|
+
* ⚠️ 和 `pnpm test:e2e:kpi`(#148)量的不是一件事:那条含页面加载,这条是播放器内部口径。
|
|
212
|
+
*/
|
|
213
|
+
onFirstFrame?: (payload: FirstFramePayload) => void;
|
|
214
|
+
/**
|
|
215
|
+
* 画面冻结(ADR-075):**缓冲是够的,但解码器出不了新帧**。**仅 PC** ——
|
|
216
|
+
* 上游 `FpsDetect` 按 `sniffer.device` 分档装载,手机上结构性不触发(已登记上行缺口)。
|
|
217
|
+
*
|
|
218
|
+
* ⚠️ 它不是掉帧率,也不与 `stalled` 重复(后者是缓冲驱动的等待)。
|
|
219
|
+
*/
|
|
220
|
+
onFrameFreeze?: (payload: FrameFreezePayload) => void;
|
|
221
|
+
/**
|
|
222
|
+
* 用户动作(ADR-075),经白名单过滤,只含**发生在 SDK 内部**的那些。
|
|
223
|
+
*
|
|
224
|
+
* 契约里已有 `play` / `volumechange` / `seeking` / `qualitychange`,而它们回答不了
|
|
225
|
+
* **「是谁发起的」** —— 用户点的还是代码调的。本事件补的就是这一格。
|
|
226
|
+
*
|
|
227
|
+
* ⚠️ 传 `controls: false` 时绝大多数动作不再发出(发出方是播放器自己的控件)。
|
|
228
|
+
*/
|
|
229
|
+
onUserAction?: (payload: UserActionPayload) => void;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 通过 `ref` 暴露的命令方法(useImperativeHandle)。
|
|
233
|
+
*
|
|
234
|
+
* 直连 player-core,方法签名沿用 `PlayerCoreHandle` 的真实同步 / 异步形态
|
|
235
|
+
* (只有 `play` 是异步,其余命令同步生效)——不像 iframe 系因为跨 postMessage 而全异步。
|
|
236
|
+
* 方法名和 react-frame 保持一致,团队封装层的调用点尽量复用。
|
|
237
|
+
*/
|
|
238
|
+
interface VideoPlayerHandle {
|
|
239
|
+
play(): Promise<void>;
|
|
240
|
+
pause(): void;
|
|
241
|
+
seek(time: number): void;
|
|
242
|
+
setMuted(muted: boolean): void;
|
|
243
|
+
setVolume(volume: number): void;
|
|
244
|
+
setPlaybackRate(rate: number): void;
|
|
245
|
+
/** 进入全屏(容器托管,不碰 video 原生入口,iOS 微信崩溃入口由 FullscreenGuard 中和) */
|
|
246
|
+
enterFullscreen(): Promise<void>;
|
|
247
|
+
/** 退出全屏 */
|
|
248
|
+
exitFullscreen(): Promise<void>;
|
|
249
|
+
setQuality(level: number | 'auto'): void;
|
|
250
|
+
/** 切字幕轨(ADR-027)。`id` = `onReady` 的 `subtitles[].id`;`'off'` 关闭字幕。直连 player-core,同步生效 */
|
|
251
|
+
setSubtitle(id: number | 'off'): void;
|
|
252
|
+
/** 运行时切换语言,不重建播放器、不丢进度。通常不必手动调 —— 改 `locale` prop 即自动下发 */
|
|
253
|
+
setLocale(locale: LocaleConfig): void;
|
|
254
|
+
/** 逐条推弹幕(直播,ADR-028)。渲染归 SDK,数据源/发送框归团队层 */
|
|
255
|
+
pushDanmaku(item: DanmakuItem): void;
|
|
256
|
+
/** 开关弹幕渲染(ADR-028) */
|
|
257
|
+
setDanmakuEnabled(enabled: boolean): void;
|
|
258
|
+
/** 清空屏上弹幕(ADR-028) */
|
|
259
|
+
clearDanmaku(): void;
|
|
260
|
+
reconnect(options?: {
|
|
261
|
+
resetCounter?: boolean;
|
|
262
|
+
}): void;
|
|
263
|
+
destroy(): void;
|
|
264
|
+
getCurrentTime(): number;
|
|
265
|
+
getDuration(): number;
|
|
266
|
+
/**
|
|
267
|
+
* 取**这一刻**的播放上下文(ADR-074):这一次播放是谁、用的哪个内核、
|
|
268
|
+
* 播的哪个候选源、当前哪一档、播到了哪。**消费方在 SDK 外面拿不到这几格。**
|
|
269
|
+
*
|
|
270
|
+
* **和 `onContextChange` 是互补的,不是二选一**:事件给初值与增量,本方法给
|
|
271
|
+
* 「出错那一刻」的精确值 —— 在 `onError` 里**同步**调它,拿到的 `position` 就是出错位置。
|
|
272
|
+
*
|
|
273
|
+
* 未挂载 / 已销毁时返回 `null`。
|
|
274
|
+
*
|
|
275
|
+
* ⚠️ **`null` 在这里是对的降级值,而 ADR-066 拒绝把 `destroyed` 摆上句柄时说「没有对的降级值」——
|
|
276
|
+
* 两者不矛盾**:`destroyed` 的候选降级值是 `false`,那会让「活着」和「还没活」共用一个读数;
|
|
277
|
+
* 而 `null` 的含义是「还没有上下文」,不与任何真实上下文冲突。
|
|
278
|
+
*/
|
|
279
|
+
getPlaybackContext(): PlaybackContext | null;
|
|
280
|
+
/** 高级场景逃生舱:拿到底层 player-core 句柄,未挂载时为 null */
|
|
281
|
+
getPlayerHandle(): PlayerHandle | null;
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
//#region src/VideoPlayer.d.ts
|
|
285
|
+
/**
|
|
286
|
+
* VideoPlayer · React inline 高性能模式(SDK 消费面之一)
|
|
287
|
+
*
|
|
288
|
+
* 直连 player-core + player-ui,不走 iframe——首帧最快(~200ms),数据路径和 iframe 系不同。
|
|
289
|
+
*
|
|
290
|
+
* **覆盖层用 player-ui 的 `<sentinel-*>` custom elements**(#120 · R1 · PR B),不再是 React 组件。
|
|
291
|
+
* 消费方 API 一个都没变 —— props / 事件 / 句柄全部原样。变的只有两件内部事:
|
|
292
|
+
* ① i18n 解析上移到这一层(元素只收解析好的字符串,player-ui 不再需要状态注入机制);
|
|
293
|
+
* ② 覆盖层的样式钩子从全局 class 变成 shadow DOM 的 `::part()`(见 USER-GUIDE)。
|
|
294
|
+
* 只做技术:组装播放器、把 props 桥接成命令、把事件桥接成 `onXxx` 回调、渲染 SDK 自带的
|
|
295
|
+
* Poster / Loading / Error 覆盖层。不含任何团队品牌 UI(ADR-021)——那些在业务封装层。
|
|
296
|
+
* props / 事件面和 react-frame 对齐(cross-mode-parity)。
|
|
297
|
+
*
|
|
298
|
+
* 【关于默认值】刻意不给 autoplay / muted / loop / controls / playsinline / interactive
|
|
299
|
+
* 设默认值:这些字段同时受 preset 控制,一旦在这里强塞就变成"显式值"把 preset 覆盖掉
|
|
300
|
+
*(resolvePreset 的语义是 undefined 才回落到 preset)。没传的 prop 保持 undefined。
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* const ref = useRef<VideoPlayerHandle>(null)
|
|
304
|
+
* <VideoPlayer ref={ref} source="https://cdn/a.m3u8" muted onReady={...} />
|
|
305
|
+
* ref.current?.play()
|
|
306
|
+
*/
|
|
307
|
+
declare const VideoPlayer: import("react").ForwardRefExoticComponent<VideoPlayerProps & import("react").RefAttributes<VideoPlayerHandle>>;
|
|
308
|
+
//#endregion
|
|
309
|
+
export { type DanmakuConfig, type DanmakuItem, type MediaSource, type PlayerError, type PlayerHandle, type QualityLevel, type SubtitleTrackInfo, VideoPlayer, type VideoPlayerHandle, type VideoPlayerProps };
|
|
310
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/VideoPlayer.tsx"],"mappings":";;;;;;;;;;KAgCY,eAAe;;;;;;;;;UAoBV;;EAEf,QAAQ;EACR;EACA;EACA;EACA;;EAEA;EACA;;EAEA;;;;;;EAMA;;;;;;EAMA;;;;;;EAMA;;EAEA,kBAAkB;;;;;;;EAOlB,sBAAsB;EACtB,SAAS;;EAET,SAAS;;EAET,UAAU;;;;;;;;;;;EAWV;;EAGA;;EAEA,QAAQ;;;;;;;;;;EAUR,WAAW;;EAIX,iBAAiB,OAAO;;;;;EAKxB,WAAW;IACT;IACA,SAAS;IACT,YAAY;;EAEd;EACA;EACA;EACA,gBAAgB;IAAW;IAAc;;EACzC,kBAAkB;IAAW;IAAgB;;;EAE7C,aAAa;IAAW;;;EAExB,YAAY;IAAW;;;;;;;;;EAQvB;;EAEA;;EAEA,mBAAmB;IAAW;IAAe;;;EAE7C,oBAAoB;IAAW;;EAC/B,WAAW,SAAS;EACpB;EACA,oBAAoB;IAAW;IAAiB;;EAChD;EACA;EACA,mBAAmB;IAAW;IAAc;IAAiB;;;;;;EAK7D,aAAa;IAAW;IAAwB;IAAkB;;;;;;;;;;;EAUlE,oBAAoB;IAClB;IACA,QAAQ;IACR;;;;;;;;;;;;;;;;;EAiBF,kBAAkB,SAAS;;;;;;;EAO3B,iBAAiB,SAAS;;EAE1B,kBAAkB,SAAS;;;;;;;EAQ3B,mBAAmB,SAAS;;EAE5B,iBAAiB,SAAS;;;;;;EAM1B,gBAAgB,SAAS;;;;;;;EAOzB,iBAAiB,SAAS;;;;;;;;;EAS1B,gBAAgB,SAAS;;;;;;;;;UAUV;EACf,QAAQ;EACR;EACA,KAAK;EACL,SAAS;EACT,UAAU;EACV,gBAAgB;;EAEhB,mBAAmB;;EAEnB,kBAAkB;EAClB,WAAW;;EAEX,YAAY;;EAGZ,UAAU,QAAQ;;EAElB,YAAY,MAAM;;EAElB,kBAAkB;;EAElB;EACA,UAAU;IAAY;;EACtB;EACA;EACA;;;;;;;;;;;;;;EAcA,sBAAsB;;EAEtB,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;cCvQR,6BAAW,0BAAA,mCAAA,cAAA"}
|