artplayer-plugin-p2p 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.
@@ -0,0 +1,279 @@
1
+ var __typeError = (msg) => {
2
+ throw TypeError(msg);
3
+ };
4
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
5
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
8
+ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
9
+
10
+ // src/bandwidth.ts
11
+ var _windowMs, _samples, _BandwidthCalculator_instances, removeExpired_fn;
12
+ var BandwidthCalculator = class {
13
+ /**
14
+ * @param windowMs - 滑动时间窗长度(毫秒),窗口越长速率越平滑
15
+ */
16
+ constructor(windowMs) {
17
+ __privateAdd(this, _BandwidthCalculator_instances);
18
+ __privateAdd(this, _windowMs);
19
+ __privateAdd(this, _samples, []);
20
+ __privateSet(this, _windowMs, windowMs);
21
+ }
22
+ /**
23
+ * 记录一笔字节
24
+ *
25
+ * @param bytes - 字节数(非正值直接忽略,防御脏数据)
26
+ * @param timestampMs - 样本时间戳;缺省取 performance.now(),测试可注入固定时间
27
+ */
28
+ record(bytes, timestampMs = performance.now()) {
29
+ if (bytes <= 0) return;
30
+ __privateGet(this, _samples).push({ timestampMs, bytes });
31
+ }
32
+ /**
33
+ * 计算窗口内平均速率
34
+ *
35
+ * 读取时惰性清理过期样本;速率按整个窗口长度折算而非
36
+ * 首尾样本间隔,保证窗口未填满时(刚启动)读数平滑趋近真实值
37
+ *
38
+ * @param nowMs - 当前时间戳;缺省取 performance.now(),测试可注入固定时间
39
+ * @returns 速率 B/s
40
+ */
41
+ getSpeed(nowMs = performance.now()) {
42
+ __privateMethod(this, _BandwidthCalculator_instances, removeExpired_fn).call(this, nowMs);
43
+ let totalBytes = 0;
44
+ for (const sample of __privateGet(this, _samples)) {
45
+ totalBytes += sample.bytes;
46
+ }
47
+ return totalBytes / (__privateGet(this, _windowMs) / 1e3);
48
+ }
49
+ /** 清空全部样本 */
50
+ reset() {
51
+ __privateGet(this, _samples).length = 0;
52
+ }
53
+ };
54
+ _windowMs = new WeakMap();
55
+ _samples = new WeakMap();
56
+ _BandwidthCalculator_instances = new WeakSet();
57
+ /**
58
+ * 移除窗口外的过期样本
59
+ *
60
+ * 样本时间戳单调递增(缺省时间源 performance.now 单调;
61
+ * 注入时间戳同样须单调),从头扫描至首个窗口内样本即可。
62
+ * 边界样本(age 恰等于窗口长)保留在窗口内,与 p2pml
63
+ * 官方带宽计算的过期判定语义保持一致
64
+ */
65
+ removeExpired_fn = function(nowMs) {
66
+ const cutoff = nowMs - __privateGet(this, _windowMs);
67
+ let firstValid = __privateGet(this, _samples).length;
68
+ for (let i = 0; i < __privateGet(this, _samples).length; i++) {
69
+ if (__privateGet(this, _samples)[i].timestampMs >= cutoff) {
70
+ firstValid = i;
71
+ break;
72
+ }
73
+ }
74
+ __privateGet(this, _samples).splice(0, firstValid);
75
+ };
76
+
77
+ // src/constants.ts
78
+ var DEFAULT_TYPE = "m3u8";
79
+ var DEFAULT_FATAL_RETRY_MAX = 2;
80
+ var BANDWIDTH_WINDOW_MS = 1e4;
81
+ var STATS_POLLING_MS = 1e3;
82
+ var P2P_EVENT_BRIDGE_MAP = {
83
+ onStreamAdded: "p2p:streamAdded",
84
+ onStreamRegistrationError: "p2p:streamRegistrationError",
85
+ onSegmentLoaded: "p2p:segmentLoaded",
86
+ onSegmentError: "p2p:segmentError",
87
+ onSegmentAbort: "p2p:segmentAbort",
88
+ onSegmentStart: "p2p:segmentStart",
89
+ onPeerConnect: "p2p:peerConnect",
90
+ onPeerConnectError: "p2p:peerConnectError",
91
+ onPeerClose: "p2p:peerClose",
92
+ onPeerError: "p2p:peerError",
93
+ onPeerWarning: "p2p:peerWarning",
94
+ onChunkDownloaded: "p2p:chunkDownloaded",
95
+ onChunkUploaded: "p2p:chunkUploaded",
96
+ onTrackerError: "p2p:trackerError",
97
+ onTrackerWarning: "p2p:trackerWarning"
98
+ };
99
+
100
+ // src/stats.ts
101
+ var _peers, _peakPeers, _p2pDownloadedBytes, _httpDownloadedBytes, _uploadedBytes, _downloadBandwidths, _uploadBandwidth;
102
+ var P2PStatsEngine = class {
103
+ constructor() {
104
+ __privateAdd(this, _peers, 0);
105
+ __privateAdd(this, _peakPeers, 0);
106
+ __privateAdd(this, _p2pDownloadedBytes, 0);
107
+ __privateAdd(this, _httpDownloadedBytes, 0);
108
+ __privateAdd(this, _uploadedBytes, 0);
109
+ __privateAdd(this, _downloadBandwidths, {
110
+ p2p: new BandwidthCalculator(BANDWIDTH_WINDOW_MS),
111
+ http: new BandwidthCalculator(BANDWIDTH_WINDOW_MS)
112
+ });
113
+ __privateAdd(this, _uploadBandwidth, new BandwidthCalculator(BANDWIDTH_WINDOW_MS));
114
+ }
115
+ /** 记录一个 peer 连接建立(同时维护峰值) */
116
+ recordPeerConnect() {
117
+ __privateSet(this, _peers, __privateGet(this, _peers) + 1);
118
+ if (__privateGet(this, _peers) > __privateGet(this, _peakPeers)) {
119
+ __privateSet(this, _peakPeers, __privateGet(this, _peers));
120
+ }
121
+ }
122
+ /** 记录一个 peer 连接断开(计数不小于零) */
123
+ recordPeerClose() {
124
+ if (__privateGet(this, _peers) > 0) {
125
+ __privateSet(this, _peers, __privateGet(this, _peers) - 1);
126
+ }
127
+ }
128
+ /**
129
+ * 记录一笔下行字节
130
+ *
131
+ * @param bytes - 字节数
132
+ * @param channel - 下载通道(http / p2p)
133
+ * @param timestampMs - 样本时间戳;缺省取 performance.now(),测试可注入固定时间
134
+ */
135
+ recordDownload(bytes, channel, timestampMs) {
136
+ if (bytes <= 0) return;
137
+ if (channel === "p2p") {
138
+ __privateSet(this, _p2pDownloadedBytes, __privateGet(this, _p2pDownloadedBytes) + bytes);
139
+ } else {
140
+ __privateSet(this, _httpDownloadedBytes, __privateGet(this, _httpDownloadedBytes) + bytes);
141
+ }
142
+ __privateGet(this, _downloadBandwidths)[channel].record(bytes, timestampMs);
143
+ }
144
+ /**
145
+ * 记录一笔 P2P 上行字节
146
+ *
147
+ * @param bytes - 字节数
148
+ * @param timestampMs - 样本时间戳;缺省取 performance.now(),测试可注入固定时间
149
+ */
150
+ recordUpload(bytes, timestampMs) {
151
+ if (bytes <= 0) return;
152
+ __privateSet(this, _uploadedBytes, __privateGet(this, _uploadedBytes) + bytes);
153
+ __privateGet(this, _uploadBandwidth).record(bytes, timestampMs);
154
+ }
155
+ /**
156
+ * 输出当前统计快照(累计量 + 派生指标一次算清)
157
+ *
158
+ * 带宽基准时间只取一次、每通道只计算一次,
159
+ * 保证快照内部指标自洽(downloadSpeed 恒等于两通道之和)
160
+ *
161
+ * @param nowMs - 带宽计算基准时间戳;缺省取 performance.now(),测试可注入固定时间
162
+ */
163
+ snapshot(nowMs) {
164
+ const totalDownloadedBytes = __privateGet(this, _p2pDownloadedBytes) + __privateGet(this, _httpDownloadedBytes);
165
+ const now = nowMs ?? performance.now();
166
+ const p2pDownloadSpeed = __privateGet(this, _downloadBandwidths).p2p.getSpeed(now);
167
+ const httpDownloadSpeed = __privateGet(this, _downloadBandwidths).http.getSpeed(now);
168
+ return {
169
+ peers: __privateGet(this, _peers),
170
+ peakPeers: __privateGet(this, _peakPeers),
171
+ p2pDownloadedBytes: __privateGet(this, _p2pDownloadedBytes),
172
+ httpDownloadedBytes: __privateGet(this, _httpDownloadedBytes),
173
+ uploadedBytes: __privateGet(this, _uploadedBytes),
174
+ totalDownloadedBytes,
175
+ p2pDownloadRatio: totalDownloadedBytes > 0 ? __privateGet(this, _p2pDownloadedBytes) / totalDownloadedBytes : 0,
176
+ downloadSpeed: p2pDownloadSpeed + httpDownloadSpeed,
177
+ p2pDownloadSpeed,
178
+ uploadSpeed: __privateGet(this, _uploadBandwidth).getSpeed(now)
179
+ };
180
+ }
181
+ /**
182
+ * 清零当前 peer 计数
183
+ *
184
+ * 动态关闭 P2P 或销毁播放实例后连接已全部断开(引擎销毁不保证
185
+ * 逐个发出 onPeerClose),主动清零防止残留计数污染下一次快照;
186
+ * 峰值与字节累计保留
187
+ */
188
+ resetPeers() {
189
+ __privateSet(this, _peers, 0);
190
+ }
191
+ /** 重置全部计数与样本(换源时调用) */
192
+ reset() {
193
+ __privateSet(this, _peers, 0);
194
+ __privateSet(this, _peakPeers, 0);
195
+ __privateSet(this, _p2pDownloadedBytes, 0);
196
+ __privateSet(this, _httpDownloadedBytes, 0);
197
+ __privateSet(this, _uploadedBytes, 0);
198
+ __privateGet(this, _downloadBandwidths).p2p.reset();
199
+ __privateGet(this, _downloadBandwidths).http.reset();
200
+ __privateGet(this, _uploadBandwidth).reset();
201
+ }
202
+ };
203
+ _peers = new WeakMap();
204
+ _peakPeers = new WeakMap();
205
+ _p2pDownloadedBytes = new WeakMap();
206
+ _httpDownloadedBytes = new WeakMap();
207
+ _uploadedBytes = new WeakMap();
208
+ _downloadBandwidths = new WeakMap();
209
+ _uploadBandwidth = new WeakMap();
210
+
211
+ // src/config.ts
212
+ function resolveUIOptions(ui) {
213
+ return ui.setting !== false;
214
+ }
215
+ function resolveSettingItems(setting) {
216
+ if (setting === void 0 || setting === true) {
217
+ return { p2pEnabled: true, uploadOnly: true, stats: true };
218
+ }
219
+ if (setting === false) {
220
+ return { p2pEnabled: false, uploadOnly: false, stats: false };
221
+ }
222
+ return {
223
+ p2pEnabled: setting.p2pEnabled ?? true,
224
+ uploadOnly: setting.uploadOnly ?? true,
225
+ stats: setting.stats ?? true
226
+ };
227
+ }
228
+ function resolveOptions(options) {
229
+ const uiEnabled = options.ui !== false;
230
+ let settingEnabled;
231
+ let settingItems;
232
+ if (typeof options.ui === "object" && options.ui !== null) {
233
+ settingEnabled = resolveUIOptions(options.ui);
234
+ settingItems = resolveSettingItems(options.ui.setting);
235
+ } else {
236
+ settingEnabled = uiEnabled;
237
+ settingItems = resolveSettingItems(uiEnabled ? void 0 : false);
238
+ }
239
+ return {
240
+ typeName: options.type ?? DEFAULT_TYPE,
241
+ fatalRetryMax: options.fatalRetryMax ?? DEFAULT_FATAL_RETRY_MAX,
242
+ p2pEnabled: options.enabled ?? true,
243
+ uploadEnabled: options.uploadEnabled ?? true,
244
+ uiEnabled,
245
+ statsEnabled: uiEnabled && options.stats !== false,
246
+ settingEnabled,
247
+ settingItems,
248
+ core: options.core,
249
+ tracker: options.tracker,
250
+ hls: options.hls
251
+ };
252
+ }
253
+ function mergeCoreConfig(options) {
254
+ return { ...options.core, ...options.tracker };
255
+ }
256
+ function applyRuntimeToggle(config, p2pEnabled, uploadEnabled) {
257
+ return {
258
+ ...config,
259
+ isP2PDisabled: !p2pEnabled,
260
+ isP2PUploadDisabled: !uploadEnabled
261
+ };
262
+ }
263
+
264
+ export {
265
+ __privateGet,
266
+ __privateAdd,
267
+ __privateSet,
268
+ __privateMethod,
269
+ BandwidthCalculator,
270
+ DEFAULT_TYPE,
271
+ DEFAULT_FATAL_RETRY_MAX,
272
+ BANDWIDTH_WINDOW_MS,
273
+ STATS_POLLING_MS,
274
+ P2P_EVENT_BRIDGE_MAP,
275
+ P2PStatsEngine,
276
+ resolveOptions,
277
+ mergeCoreConfig,
278
+ applyRuntimeToggle
279
+ };
@@ -0,0 +1,202 @@
1
+ import Artplayer from 'artplayer';
2
+ import { P as P2POptions, R as ResolvedOptions, a as P2PStatsEngine, b as P2PPluginHandle } from './stats-CFbDXEsi.mjs';
3
+ export { B as BANDWIDTH_WINDOW_MS, D as DEFAULT_FATAL_RETRY_MAX, c as DEFAULT_TYPE, d as DownloadChannel, e as P2PSettingItemsOptions, f as P2PStats, g as P2PTrackerOptions, h as P2PUIOptions, i as P2P_EVENT_BRIDGE_MAP, S as STATS_POLLING_MS, j as StateChangeDetails, k as applyRuntimeToggle, m as mergeCoreConfig, r as resolveOptions } from './stats-CFbDXEsi.mjs';
4
+ import Hls, { ErrorData } from 'hls.js';
5
+ import { HlsJsP2PEngine, HlsWithP2PInstance } from 'p2p-media-loader-hlsjs';
6
+ export { CoreConfig, DynamicCoreConfig } from 'p2p-media-loader-core';
7
+
8
+ /**
9
+ * hls.js fatal 错误分级恢复策略
10
+ *
11
+ * 按 hls.js 官方最佳实践实施三级自愈:
12
+ * 网络级 startLoad() → 媒体级 recoverMediaError()(二次失败自动
13
+ * swapAudioCodec)→ 其他/超限交由上层销毁重建;全部动作有界。
14
+ * 通过回调注入通知上层(不依赖 ArtPlayer),保持模块可移植性
15
+ *
16
+ * @module recovery
17
+ */
18
+
19
+ /** 恢复策略回调集合(由编排层注入并绑定到具体通知渠道) */
20
+ interface FatalRecoveryOptions {
21
+ /** 单实例内软恢复的最大次数上限 */
22
+ retryMax: number;
23
+ /** 每次 fatal 发生时通知(含软恢复与放弃两种情形,附当前重试序号) */
24
+ onFatalError: (data: ErrorData, retryCount: number) => void;
25
+ /** 遇到不可软恢复的 fatal(其他类型错误),交由上层销毁重建 */
26
+ onUnrecoverable: (data: ErrorData, retryCount: number) => void;
27
+ }
28
+ /**
29
+ * fatal 分级恢复策略
30
+ *
31
+ * 挂载在单个 hls.js 实例的 ERROR 事件上,随实例销毁而失效;
32
+ * 软恢复计数为实例级(重建后的新实例从零计数)
33
+ */
34
+ declare class FatalRecoveryPolicy {
35
+ #private;
36
+ /**
37
+ * @param hls - 目标 hls.js 实例
38
+ * @param hlsConstructor - hls.js 构造器(Events/ErrorTypes 枚举来源,
39
+ * 避免本模块引入 hls.js 运行时值以保持可移植性)
40
+ * @param options - 回调集合
41
+ */
42
+ constructor(hls: Hls, hlsConstructor: typeof Hls, options: FatalRecoveryOptions);
43
+ }
44
+
45
+ /**
46
+ * P2P 播放引擎工厂
47
+ *
48
+ * 职责:
49
+ * - 合并 tracker 快捷配置与 core 全量配置,并注入插件运行时开关状态
50
+ * - 缓存 injectMixin 生成的 HlsWithP2P 构造器(每个 hls.js 构造器仅生成一次)
51
+ * - 创建携带 P2P 能力的 hls.js 实例,并挂载 fatal 分级恢复策略
52
+ *
53
+ * P2P / 上传开关统一经运行时动态配置实现(见 controller),
54
+ * 实例创建时的初始注入保证重建后模式不丢失,
55
+ * 因此本模块不再提供"裸 hls.js 实例"工厂
56
+ *
57
+ * @module engine
58
+ */
59
+
60
+ /** 引擎消费的选项结构化子集(ResolvedOptions 结构化兼容) */
61
+ interface EngineOptions extends Pick<P2POptions, 'core' | 'tracker' | 'hls' | 'fatalRetryMax'> {
62
+ /** 当前 P2P 开关状态(注入 isP2PDisabled) */
63
+ p2pEnabled: boolean;
64
+ /** 当前上传开关状态(注入 isP2PUploadDisabled) */
65
+ uploadEnabled: boolean;
66
+ }
67
+ /** hls.js 构造器类型 */
68
+ type HlsConstructor = typeof Hls;
69
+ /**
70
+ * 引擎工厂钩子集合(由编排层注入)
71
+ *
72
+ * onEngineCreated 在引擎就绪时触发,供事件桥挂载;
73
+ * 其余两个钩子用于 fatal 通知与不可恢复错误上报
74
+ */
75
+ interface EngineHooks {
76
+ /** P2P 引擎就绪(onHlsJsCreated 时机),供事件桥挂载 */
77
+ onEngineCreated: (engine: HlsJsP2PEngine) => void;
78
+ /** fatal 发生通知(含软恢复与放弃两种情形) */
79
+ onFatalError: (data: ErrorData, retryCount: number) => void;
80
+ /** 遇到不可软恢复的 fatal,由编排层决定销毁重建 */
81
+ onUnrecoverable: (data: ErrorData, retryCount: number) => void;
82
+ }
83
+ /**
84
+ * 创建携带 P2P 能力的 hls.js 播放实例
85
+ *
86
+ * core 配置经 applyRuntimeToggle 注入当前开关状态;
87
+ * 仅完成实例构造与 fatal 恢复挂载,loadSource / attachMedia
88
+ * 的调用时机由调用方决定
89
+ *
90
+ * @param options - 引擎选项(含透传配置与运行时开关状态)
91
+ * @param hlsConstructor - 宿主提供的 hls.js 构造器(peerDependency 实例)
92
+ * @param hooks - 引擎钩子集合
93
+ * @returns HlsWithP2P 播放实例
94
+ */
95
+ declare function createHlsWithP2P(options: EngineOptions, hlsConstructor: HlsConstructor, hooks: EngineHooks): HlsWithP2PInstance<Hls>;
96
+
97
+ /**
98
+ * P2P 实例生命周期控制器
99
+ *
100
+ * 唯一持有播放实例与跨实例状态(最近 URL / 视频 / 重建计数 /
101
+ * P2P 与上传开关)的状态机;换源、fatal 重建、开关切换、销毁
102
+ * 全部收敛为显式方法。art.hls 槽位的写入与清空只发生在本模块内
103
+ *
104
+ * 开关语义:
105
+ * - P2P / 上传开关经 engine.applyDynamicConfig 无损切换,
106
+ * 不销毁实例、不中断播放
107
+ * - 实例创建(首装 / fatal 重建 / 换源 / 重连)时按当前开关状态
108
+ * 注入初始配置,保证重建后模式不丢失
109
+ * - activate 不接受模式参数:模式一律取控制器运行时状态,
110
+ * 避免 customType 重入路径(video:error 重连)重置用户的开关选择
111
+ *
112
+ * @module controller
113
+ */
114
+
115
+ /** 控制器状态:idle 未创建 / active 播放中 / destroyed 播放器已销毁 */
116
+ type ControllerState = 'idle' | 'active' | 'destroyed';
117
+ /** P2P 实例生命周期控制器 */
118
+ declare class P2PController {
119
+ #private;
120
+ /**
121
+ * @param art - ArtPlayer 实例
122
+ * @param options - 解析后的插件选项(p2pEnabled / uploadEnabled 为初始开关状态)
123
+ * @param stats - 统计引擎实例(由入口层创建并共享给句柄)
124
+ */
125
+ constructor(art: Artplayer, options: ResolvedOptions, stats: P2PStatsEngine);
126
+ /** 当前控制器状态 */
127
+ get state(): ControllerState;
128
+ /** 当前 P2P 开关状态 */
129
+ get p2pEnabled(): boolean;
130
+ /** 当前上传开关状态 */
131
+ get uploadEnabled(): boolean;
132
+ /** 当前播放实例(与 art.hls 槽位同步) */
133
+ get hls(): HlsWithP2PInstance<Hls> | undefined;
134
+ /**
135
+ * 外部显式激活(customType 回调:首次加载 / 换源 / 重连共用)
136
+ *
137
+ * 视为新的播放意图:统计与重建计数全部归零,
138
+ * 以控制器当前开关状态创建实例(不接受模式参数)
139
+ *
140
+ * @param url - 播放地址
141
+ * @param video - 视频元素
142
+ */
143
+ activate(url: string, video: HTMLVideoElement): void;
144
+ /** 销毁当前播放实例(注销监听、清空 art.hls 槽位) */
145
+ deactivate(): void;
146
+ /** 销毁现有实例后按最近地址与当前开关模式重建 */
147
+ reload(): void;
148
+ /**
149
+ * P2P 运行时开关:经 applyDynamicConfig 无损切换
150
+ *
151
+ * 不销毁实例、不中断播放;关闭时 HybridLoader 被引擎销毁、
152
+ * peer 连接全断,主动清零 peer 计数防止残留(迟到关闭事件
153
+ * 由统计引擎的非负保护兜底);字节统计冻结保留供模式对照
154
+ *
155
+ * @param enabled - 目标 P2P 状态
156
+ */
157
+ setP2PEnabled(enabled: boolean): void;
158
+ /**
159
+ * 上行运行时开关:仅信令广播(引擎内部处理),完全无缝
160
+ *
161
+ * @param enabled - 目标上传状态
162
+ */
163
+ setUploadEnabled(enabled: boolean): void;
164
+ /** 播放器销毁:终止一切并进入 destroyed(幂等) */
165
+ destroy(): void;
166
+ }
167
+
168
+ /**
169
+ * ArtPlayer P2P 插件入口
170
+ *
171
+ * 将 p2p-media-loader(hls.js 引擎)封装为 ArtPlayer 插件:
172
+ * 通过 option.plugins 注入即可为 m3u8 播放启用 P2P 分发,
173
+ * 支持自建 tracker 信令服务器配置、运行时开关与统计
174
+ *
175
+ * 组装流程依赖 ArtPlayer 的两个时序事实:
176
+ * 1. 插件工厂在 Player.optionInit 之后执行,首次 URL 加载已经发生,
177
+ * 因此 customType 注册完成后需要对匹配类型的源做二次赋值补救
178
+ * 2. video:error 重连经 art.url 重赋值会重新进入 customType 回调,
179
+ * 回调内控制器的 activate 幂等(先销毁再重建),重入安全
180
+ *
181
+ * 用法:
182
+ * new Artplayer({
183
+ * url: 'https://example.com/stream.m3u8',
184
+ * type: 'm3u8',
185
+ * plugins: [artplayerPluginP2P({ tracker: { announceTrackers: ['wss://tracker.example.com'] } })],
186
+ * })
187
+ *
188
+ * @module index
189
+ */
190
+
191
+ /**
192
+ * ArtPlayer P2P 插件工厂
193
+ *
194
+ * 自动注册 customType 并对首次加载做时序接管(用户零感知);
195
+ * 返回的句柄挂载在 art.plugins.artplayerPluginP2P
196
+ *
197
+ * @param options - 插件选项
198
+ * @returns ArtPlayer 插件函数
199
+ */
200
+ declare function artplayerPluginP2P(options?: P2POptions): (art: Artplayer) => P2PPluginHandle;
201
+
202
+ export { type ControllerState, type EngineHooks, type EngineOptions, FatalRecoveryPolicy, P2PController, P2POptions, P2PPluginHandle, P2PStatsEngine, createHlsWithP2P, artplayerPluginP2P as default };
@@ -0,0 +1,202 @@
1
+ import Artplayer from 'artplayer';
2
+ import { P as P2POptions, R as ResolvedOptions, a as P2PStatsEngine, b as P2PPluginHandle } from './stats-CFbDXEsi.js';
3
+ export { B as BANDWIDTH_WINDOW_MS, D as DEFAULT_FATAL_RETRY_MAX, c as DEFAULT_TYPE, d as DownloadChannel, e as P2PSettingItemsOptions, f as P2PStats, g as P2PTrackerOptions, h as P2PUIOptions, i as P2P_EVENT_BRIDGE_MAP, S as STATS_POLLING_MS, j as StateChangeDetails, k as applyRuntimeToggle, m as mergeCoreConfig, r as resolveOptions } from './stats-CFbDXEsi.js';
4
+ import Hls, { ErrorData } from 'hls.js';
5
+ import { HlsJsP2PEngine, HlsWithP2PInstance } from 'p2p-media-loader-hlsjs';
6
+ export { CoreConfig, DynamicCoreConfig } from 'p2p-media-loader-core';
7
+
8
+ /**
9
+ * hls.js fatal 错误分级恢复策略
10
+ *
11
+ * 按 hls.js 官方最佳实践实施三级自愈:
12
+ * 网络级 startLoad() → 媒体级 recoverMediaError()(二次失败自动
13
+ * swapAudioCodec)→ 其他/超限交由上层销毁重建;全部动作有界。
14
+ * 通过回调注入通知上层(不依赖 ArtPlayer),保持模块可移植性
15
+ *
16
+ * @module recovery
17
+ */
18
+
19
+ /** 恢复策略回调集合(由编排层注入并绑定到具体通知渠道) */
20
+ interface FatalRecoveryOptions {
21
+ /** 单实例内软恢复的最大次数上限 */
22
+ retryMax: number;
23
+ /** 每次 fatal 发生时通知(含软恢复与放弃两种情形,附当前重试序号) */
24
+ onFatalError: (data: ErrorData, retryCount: number) => void;
25
+ /** 遇到不可软恢复的 fatal(其他类型错误),交由上层销毁重建 */
26
+ onUnrecoverable: (data: ErrorData, retryCount: number) => void;
27
+ }
28
+ /**
29
+ * fatal 分级恢复策略
30
+ *
31
+ * 挂载在单个 hls.js 实例的 ERROR 事件上,随实例销毁而失效;
32
+ * 软恢复计数为实例级(重建后的新实例从零计数)
33
+ */
34
+ declare class FatalRecoveryPolicy {
35
+ #private;
36
+ /**
37
+ * @param hls - 目标 hls.js 实例
38
+ * @param hlsConstructor - hls.js 构造器(Events/ErrorTypes 枚举来源,
39
+ * 避免本模块引入 hls.js 运行时值以保持可移植性)
40
+ * @param options - 回调集合
41
+ */
42
+ constructor(hls: Hls, hlsConstructor: typeof Hls, options: FatalRecoveryOptions);
43
+ }
44
+
45
+ /**
46
+ * P2P 播放引擎工厂
47
+ *
48
+ * 职责:
49
+ * - 合并 tracker 快捷配置与 core 全量配置,并注入插件运行时开关状态
50
+ * - 缓存 injectMixin 生成的 HlsWithP2P 构造器(每个 hls.js 构造器仅生成一次)
51
+ * - 创建携带 P2P 能力的 hls.js 实例,并挂载 fatal 分级恢复策略
52
+ *
53
+ * P2P / 上传开关统一经运行时动态配置实现(见 controller),
54
+ * 实例创建时的初始注入保证重建后模式不丢失,
55
+ * 因此本模块不再提供"裸 hls.js 实例"工厂
56
+ *
57
+ * @module engine
58
+ */
59
+
60
+ /** 引擎消费的选项结构化子集(ResolvedOptions 结构化兼容) */
61
+ interface EngineOptions extends Pick<P2POptions, 'core' | 'tracker' | 'hls' | 'fatalRetryMax'> {
62
+ /** 当前 P2P 开关状态(注入 isP2PDisabled) */
63
+ p2pEnabled: boolean;
64
+ /** 当前上传开关状态(注入 isP2PUploadDisabled) */
65
+ uploadEnabled: boolean;
66
+ }
67
+ /** hls.js 构造器类型 */
68
+ type HlsConstructor = typeof Hls;
69
+ /**
70
+ * 引擎工厂钩子集合(由编排层注入)
71
+ *
72
+ * onEngineCreated 在引擎就绪时触发,供事件桥挂载;
73
+ * 其余两个钩子用于 fatal 通知与不可恢复错误上报
74
+ */
75
+ interface EngineHooks {
76
+ /** P2P 引擎就绪(onHlsJsCreated 时机),供事件桥挂载 */
77
+ onEngineCreated: (engine: HlsJsP2PEngine) => void;
78
+ /** fatal 发生通知(含软恢复与放弃两种情形) */
79
+ onFatalError: (data: ErrorData, retryCount: number) => void;
80
+ /** 遇到不可软恢复的 fatal,由编排层决定销毁重建 */
81
+ onUnrecoverable: (data: ErrorData, retryCount: number) => void;
82
+ }
83
+ /**
84
+ * 创建携带 P2P 能力的 hls.js 播放实例
85
+ *
86
+ * core 配置经 applyRuntimeToggle 注入当前开关状态;
87
+ * 仅完成实例构造与 fatal 恢复挂载,loadSource / attachMedia
88
+ * 的调用时机由调用方决定
89
+ *
90
+ * @param options - 引擎选项(含透传配置与运行时开关状态)
91
+ * @param hlsConstructor - 宿主提供的 hls.js 构造器(peerDependency 实例)
92
+ * @param hooks - 引擎钩子集合
93
+ * @returns HlsWithP2P 播放实例
94
+ */
95
+ declare function createHlsWithP2P(options: EngineOptions, hlsConstructor: HlsConstructor, hooks: EngineHooks): HlsWithP2PInstance<Hls>;
96
+
97
+ /**
98
+ * P2P 实例生命周期控制器
99
+ *
100
+ * 唯一持有播放实例与跨实例状态(最近 URL / 视频 / 重建计数 /
101
+ * P2P 与上传开关)的状态机;换源、fatal 重建、开关切换、销毁
102
+ * 全部收敛为显式方法。art.hls 槽位的写入与清空只发生在本模块内
103
+ *
104
+ * 开关语义:
105
+ * - P2P / 上传开关经 engine.applyDynamicConfig 无损切换,
106
+ * 不销毁实例、不中断播放
107
+ * - 实例创建(首装 / fatal 重建 / 换源 / 重连)时按当前开关状态
108
+ * 注入初始配置,保证重建后模式不丢失
109
+ * - activate 不接受模式参数:模式一律取控制器运行时状态,
110
+ * 避免 customType 重入路径(video:error 重连)重置用户的开关选择
111
+ *
112
+ * @module controller
113
+ */
114
+
115
+ /** 控制器状态:idle 未创建 / active 播放中 / destroyed 播放器已销毁 */
116
+ type ControllerState = 'idle' | 'active' | 'destroyed';
117
+ /** P2P 实例生命周期控制器 */
118
+ declare class P2PController {
119
+ #private;
120
+ /**
121
+ * @param art - ArtPlayer 实例
122
+ * @param options - 解析后的插件选项(p2pEnabled / uploadEnabled 为初始开关状态)
123
+ * @param stats - 统计引擎实例(由入口层创建并共享给句柄)
124
+ */
125
+ constructor(art: Artplayer, options: ResolvedOptions, stats: P2PStatsEngine);
126
+ /** 当前控制器状态 */
127
+ get state(): ControllerState;
128
+ /** 当前 P2P 开关状态 */
129
+ get p2pEnabled(): boolean;
130
+ /** 当前上传开关状态 */
131
+ get uploadEnabled(): boolean;
132
+ /** 当前播放实例(与 art.hls 槽位同步) */
133
+ get hls(): HlsWithP2PInstance<Hls> | undefined;
134
+ /**
135
+ * 外部显式激活(customType 回调:首次加载 / 换源 / 重连共用)
136
+ *
137
+ * 视为新的播放意图:统计与重建计数全部归零,
138
+ * 以控制器当前开关状态创建实例(不接受模式参数)
139
+ *
140
+ * @param url - 播放地址
141
+ * @param video - 视频元素
142
+ */
143
+ activate(url: string, video: HTMLVideoElement): void;
144
+ /** 销毁当前播放实例(注销监听、清空 art.hls 槽位) */
145
+ deactivate(): void;
146
+ /** 销毁现有实例后按最近地址与当前开关模式重建 */
147
+ reload(): void;
148
+ /**
149
+ * P2P 运行时开关:经 applyDynamicConfig 无损切换
150
+ *
151
+ * 不销毁实例、不中断播放;关闭时 HybridLoader 被引擎销毁、
152
+ * peer 连接全断,主动清零 peer 计数防止残留(迟到关闭事件
153
+ * 由统计引擎的非负保护兜底);字节统计冻结保留供模式对照
154
+ *
155
+ * @param enabled - 目标 P2P 状态
156
+ */
157
+ setP2PEnabled(enabled: boolean): void;
158
+ /**
159
+ * 上行运行时开关:仅信令广播(引擎内部处理),完全无缝
160
+ *
161
+ * @param enabled - 目标上传状态
162
+ */
163
+ setUploadEnabled(enabled: boolean): void;
164
+ /** 播放器销毁:终止一切并进入 destroyed(幂等) */
165
+ destroy(): void;
166
+ }
167
+
168
+ /**
169
+ * ArtPlayer P2P 插件入口
170
+ *
171
+ * 将 p2p-media-loader(hls.js 引擎)封装为 ArtPlayer 插件:
172
+ * 通过 option.plugins 注入即可为 m3u8 播放启用 P2P 分发,
173
+ * 支持自建 tracker 信令服务器配置、运行时开关与统计
174
+ *
175
+ * 组装流程依赖 ArtPlayer 的两个时序事实:
176
+ * 1. 插件工厂在 Player.optionInit 之后执行,首次 URL 加载已经发生,
177
+ * 因此 customType 注册完成后需要对匹配类型的源做二次赋值补救
178
+ * 2. video:error 重连经 art.url 重赋值会重新进入 customType 回调,
179
+ * 回调内控制器的 activate 幂等(先销毁再重建),重入安全
180
+ *
181
+ * 用法:
182
+ * new Artplayer({
183
+ * url: 'https://example.com/stream.m3u8',
184
+ * type: 'm3u8',
185
+ * plugins: [artplayerPluginP2P({ tracker: { announceTrackers: ['wss://tracker.example.com'] } })],
186
+ * })
187
+ *
188
+ * @module index
189
+ */
190
+
191
+ /**
192
+ * ArtPlayer P2P 插件工厂
193
+ *
194
+ * 自动注册 customType 并对首次加载做时序接管(用户零感知);
195
+ * 返回的句柄挂载在 art.plugins.artplayerPluginP2P
196
+ *
197
+ * @param options - 插件选项
198
+ * @returns ArtPlayer 插件函数
199
+ */
200
+ declare function artplayerPluginP2P(options?: P2POptions): (art: Artplayer) => P2PPluginHandle;
201
+
202
+ export { type ControllerState, type EngineHooks, type EngineOptions, FatalRecoveryPolicy, P2PController, P2POptions, P2PPluginHandle, P2PStatsEngine, createHlsWithP2P, artplayerPluginP2P as default };