@video-lab/player-core 4.1.1 → 4.1.2

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
@@ -103,6 +103,8 @@ window.addEventListener('pagehide', () => player.destroy(), { once: true })
103
103
 
104
104
  **换内核会明确报错。** hls.js / flv.js 的内核插件在构造时注册,运行时换不了。`load()` 一个需要不同内核的源时抛 `E_METHOD_NOT_SUPPORTED`,而不是悄悄播不出来。消费方需要销毁 player 用新 source 重建。
105
105
 
106
+ **新源加载失败只经 `error` 事件报告一次。** `load()` 在这次换源有结论时 resolve:新源起播、源故障已通过 `error` 报出、被更新的 `load()` 取代或播放器销毁。它只在换源前的校验失败(非法 source、换内核、改构造期配置、选不出可播放源)时 reject(ADR-136)。
107
+
106
108
  **直播的 `duration` 是 0,不是 Infinity。** Infinity 过不了 JSON 序列化(变成 null),而事件要跨 iframe 传,所以在源头就归一成 0。
107
109
 
108
110
  ## 环境探测与测试
package/dist/index.cjs CHANGED
@@ -3175,8 +3175,15 @@ function createPlayer(options) {
3175
3175
  let recovery = null;
3176
3176
  let eventStreamReady = false;
3177
3177
  const deferredEvents = [];
3178
+ /**
3179
+ * 挂起中的 `load()` 的结束口(ADR-136)。换源后的源故障只经 `error` 事件报告一次;
3180
+ * 由它结束 `load()`,不再等 xgplayer `switchURL` —— 那个 Promise 只认 xgplayer 自己的
3181
+ * `error` / `canplay`,而 HLS 致命错误走 `HLS_ERROR`,不会触发它(#145)。
3182
+ */
3183
+ let settlePendingLoad = null;
3184
+ const isSourceFault = (event, eventSession) => event.event === "error" && !destroyed && eventSession === sessionId && event.payload.code !== "E_SUBTITLE_LOAD_FAILED" && event.payload.code !== "E_DANMAKU_SEND_FAILED" && event.payload.code !== "E_METHOD_NOT_SUPPORTED";
3178
3185
  const reportRecoveryFault = (event, eventSession) => {
3179
- if (event.event === "error" && !destroyed && eventSession === sessionId && event.payload.code !== "E_SUBTITLE_LOAD_FAILED" && event.payload.code !== "E_DANMAKU_SEND_FAILED" && event.payload.code !== "E_METHOD_NOT_SUPPORTED") recovery?.fault(event.payload, eventSession);
3186
+ if (event.event === "error" && isSourceFault(event, eventSession)) recovery?.fault(event.payload, eventSession);
3180
3187
  };
3181
3188
  const emit = (event) => {
3182
3189
  if (destroyed) return;
@@ -3187,6 +3194,7 @@ function createPlayer(options) {
3187
3194
  }
3188
3195
  const eventSession = sessionId;
3189
3196
  notify(delivered);
3197
+ if (isSourceFault(event, eventSession)) settlePendingLoad?.();
3190
3198
  reportRecoveryFault(event, eventSession);
3191
3199
  };
3192
3200
  let playerRef = null;
@@ -4096,7 +4104,15 @@ function createPlayer(options) {
4096
4104
  if (destroyed || revision !== loadRevision) return;
4097
4105
  emitContextChange();
4098
4106
  if (destroyed || revision !== loadRevision) return;
4099
- await player.switchURL(nextRouted.chosen.url);
4107
+ settlePendingLoad?.();
4108
+ await new Promise((resolve) => {
4109
+ const settle = () => {
4110
+ if (settlePendingLoad === settle) settlePendingLoad = null;
4111
+ resolve();
4112
+ };
4113
+ settlePendingLoad = settle;
4114
+ Promise.resolve(player.switchURL(nextRouted.chosen.url)).then(settle, settle);
4115
+ });
4100
4116
  if (destroyed || revision !== loadRevision) return;
4101
4117
  getMediaSession(player)?.setMetadata(nextNormalized.metadata);
4102
4118
  },
@@ -4120,6 +4136,7 @@ function createPlayer(options) {
4120
4136
  */
4121
4137
  destroy() {
4122
4138
  if (destroyed) return;
4139
+ settlePendingLoad?.();
4123
4140
  recovery?.destroy();
4124
4141
  recoveryCleanup?.();
4125
4142
  destroyed = true;
package/dist/index.d.cts CHANGED
@@ -159,6 +159,10 @@ interface PlayerCoreHandle {
159
159
  * 安全热切媒体路由。改变内核、`live`、`hls.lowLatencyMode` 或字幕配置会抛
160
160
  * `E_METHOD_NOT_SUPPORTED`,因为它们在播放器构造期确定;宿主须销毁后以新 source 重建。
161
161
  * 组合层的常规换源入口是 `source` prop,直接调用只适合 player-core / 高级句柄场景。
162
+ *
163
+ * 返回的 Promise 在这次换源有结论时 resolve(ADR-136):新源起播、当前会话的源故障已经通过
164
+ * `error` 事件报出、被更新的 `load()` 取代或播放器销毁。resolve 不代表新源可播放;
165
+ * 新源加载失败只经 `error` 事件报告一次,不会让 `load()` reject。只有上面的校验失败才 reject。
162
166
  */
163
167
  load(source: MediaSource): Promise<void>;
164
168
  /**
package/dist/index.d.mts CHANGED
@@ -159,6 +159,10 @@ interface PlayerCoreHandle {
159
159
  * 安全热切媒体路由。改变内核、`live`、`hls.lowLatencyMode` 或字幕配置会抛
160
160
  * `E_METHOD_NOT_SUPPORTED`,因为它们在播放器构造期确定;宿主须销毁后以新 source 重建。
161
161
  * 组合层的常规换源入口是 `source` prop,直接调用只适合 player-core / 高级句柄场景。
162
+ *
163
+ * 返回的 Promise 在这次换源有结论时 resolve(ADR-136):新源起播、当前会话的源故障已经通过
164
+ * `error` 事件报出、被更新的 `load()` 取代或播放器销毁。resolve 不代表新源可播放;
165
+ * 新源加载失败只经 `error` 事件报告一次,不会让 `load()` reject。只有上面的校验失败才 reject。
162
166
  */
163
167
  load(source: MediaSource): Promise<void>;
164
168
  /**
package/dist/index.mjs CHANGED
@@ -3149,8 +3149,15 @@ function createPlayer(options) {
3149
3149
  let recovery = null;
3150
3150
  let eventStreamReady = false;
3151
3151
  const deferredEvents = [];
3152
+ /**
3153
+ * 挂起中的 `load()` 的结束口(ADR-136)。换源后的源故障只经 `error` 事件报告一次;
3154
+ * 由它结束 `load()`,不再等 xgplayer `switchURL` —— 那个 Promise 只认 xgplayer 自己的
3155
+ * `error` / `canplay`,而 HLS 致命错误走 `HLS_ERROR`,不会触发它(#145)。
3156
+ */
3157
+ let settlePendingLoad = null;
3158
+ const isSourceFault = (event, eventSession) => event.event === "error" && !destroyed && eventSession === sessionId && event.payload.code !== "E_SUBTITLE_LOAD_FAILED" && event.payload.code !== "E_DANMAKU_SEND_FAILED" && event.payload.code !== "E_METHOD_NOT_SUPPORTED";
3152
3159
  const reportRecoveryFault = (event, eventSession) => {
3153
- if (event.event === "error" && !destroyed && eventSession === sessionId && event.payload.code !== "E_SUBTITLE_LOAD_FAILED" && event.payload.code !== "E_DANMAKU_SEND_FAILED" && event.payload.code !== "E_METHOD_NOT_SUPPORTED") recovery?.fault(event.payload, eventSession);
3160
+ if (event.event === "error" && isSourceFault(event, eventSession)) recovery?.fault(event.payload, eventSession);
3154
3161
  };
3155
3162
  const emit = (event) => {
3156
3163
  if (destroyed) return;
@@ -3161,6 +3168,7 @@ function createPlayer(options) {
3161
3168
  }
3162
3169
  const eventSession = sessionId;
3163
3170
  notify(delivered);
3171
+ if (isSourceFault(event, eventSession)) settlePendingLoad?.();
3164
3172
  reportRecoveryFault(event, eventSession);
3165
3173
  };
3166
3174
  let playerRef = null;
@@ -4070,7 +4078,15 @@ function createPlayer(options) {
4070
4078
  if (destroyed || revision !== loadRevision) return;
4071
4079
  emitContextChange();
4072
4080
  if (destroyed || revision !== loadRevision) return;
4073
- await player.switchURL(nextRouted.chosen.url);
4081
+ settlePendingLoad?.();
4082
+ await new Promise((resolve) => {
4083
+ const settle = () => {
4084
+ if (settlePendingLoad === settle) settlePendingLoad = null;
4085
+ resolve();
4086
+ };
4087
+ settlePendingLoad = settle;
4088
+ Promise.resolve(player.switchURL(nextRouted.chosen.url)).then(settle, settle);
4089
+ });
4074
4090
  if (destroyed || revision !== loadRevision) return;
4075
4091
  getMediaSession(player)?.setMetadata(nextNormalized.metadata);
4076
4092
  },
@@ -4094,6 +4110,7 @@ function createPlayer(options) {
4094
4110
  */
4095
4111
  destroy() {
4096
4112
  if (destroyed) return;
4113
+ settlePendingLoad?.();
4097
4114
  recovery?.destroy();
4098
4115
  recoveryCleanup?.();
4099
4116
  destroyed = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@video-lab/player-core",
3
- "version": "4.1.1",
3
+ "version": "4.1.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -33,7 +33,7 @@
33
33
  "xgplayer": "3.0.26",
34
34
  "hls.js": "1.6.19",
35
35
  "xgplayer-flv.js": "3.0.26",
36
- "@video-lab/protocol": "4.1.1"
36
+ "@video-lab/protocol": "4.1.2"
37
37
  },
38
38
  "devDependencies": {
39
39
  "jsdom": "^24.0.0",