@scarlett-player/embed 0.5.2 → 0.5.3
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/dist/embed.audio.js +77 -4
- package/dist/embed.audio.js.map +1 -1
- package/dist/embed.audio.umd.cjs +1 -1
- package/dist/embed.audio.umd.cjs.map +1 -1
- package/dist/embed.js +83 -4
- package/dist/embed.js.map +1 -1
- package/dist/embed.umd.cjs +1 -1
- package/dist/embed.umd.cjs.map +1 -1
- package/dist/embed.video.js +77 -4
- package/dist/embed.video.js.map +1 -1
- package/dist/embed.video.umd.cjs +1 -1
- package/dist/embed.video.umd.cjs.map +1 -1
- package/package.json +9 -9
package/dist/embed.js
CHANGED
|
@@ -123,12 +123,44 @@ function setupHlsEventHandlers(hls, api, callbacks) {
|
|
|
123
123
|
addHandler("hlsLevelLoaded", (_event, data) => {
|
|
124
124
|
if (data.details?.live !== void 0) {
|
|
125
125
|
api.setState("live", data.details.live);
|
|
126
|
+
if (data.details.live) {
|
|
127
|
+
const video = hls.media;
|
|
128
|
+
if (video && video.seekable && video.seekable.length > 0) {
|
|
129
|
+
const start = video.seekable.start(0);
|
|
130
|
+
const end = video.seekable.end(video.seekable.length - 1);
|
|
131
|
+
api.setState("seekableRange", { start, end });
|
|
132
|
+
const threshold = (data.details.targetduration ?? 3) * 3;
|
|
133
|
+
const isAtLiveEdge = end - video.currentTime < threshold;
|
|
134
|
+
api.setState("liveEdge", isAtLiveEdge);
|
|
135
|
+
const latency = end - video.currentTime;
|
|
136
|
+
api.setState("liveLatency", Math.max(0, latency));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
126
139
|
callbacks.onLiveUpdate?.();
|
|
127
140
|
}
|
|
128
141
|
});
|
|
129
142
|
addHandler("hlsError", (_event, data) => {
|
|
130
143
|
const error = parseHlsError(data);
|
|
131
|
-
|
|
144
|
+
const isBufferHoleSeek = !error.fatal && (error.details?.includes("bufferStalledError") || data.reason?.includes("buffer holes"));
|
|
145
|
+
if (isBufferHoleSeek) {
|
|
146
|
+
api.logger.debug(`HLS buffer recovery: ${error.reason || error.details}`, {
|
|
147
|
+
details: error.details,
|
|
148
|
+
reason: error.reason
|
|
149
|
+
});
|
|
150
|
+
} else if (error.fatal) {
|
|
151
|
+
api.logger.error(`HLS fatal error: ${error.details} (type=${error.type})`, {
|
|
152
|
+
type: error.type,
|
|
153
|
+
details: error.details,
|
|
154
|
+
url: error.url
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
api.logger.warn(`HLS error: ${error.details} (type=${error.type}, fatal=${error.fatal})`, {
|
|
158
|
+
type: error.type,
|
|
159
|
+
details: error.details,
|
|
160
|
+
fatal: error.fatal,
|
|
161
|
+
url: error.url
|
|
162
|
+
});
|
|
163
|
+
}
|
|
132
164
|
callbacks.onError?.(error);
|
|
133
165
|
});
|
|
134
166
|
return () => {
|
|
@@ -150,6 +182,8 @@ function setupVideoEventHandlers(video, api) {
|
|
|
150
182
|
addHandler("playing", () => {
|
|
151
183
|
api.setState("playing", true);
|
|
152
184
|
api.setState("paused", false);
|
|
185
|
+
api.setState("waiting", false);
|
|
186
|
+
api.setState("buffering", false);
|
|
153
187
|
api.setState("playbackState", "playing");
|
|
154
188
|
});
|
|
155
189
|
addHandler("pause", () => {
|
|
@@ -166,6 +200,15 @@ function setupVideoEventHandlers(video, api) {
|
|
|
166
200
|
addHandler("timeupdate", () => {
|
|
167
201
|
api.setState("currentTime", video.currentTime);
|
|
168
202
|
api.emit("playback:timeupdate", { currentTime: video.currentTime });
|
|
203
|
+
const isLive = api.getState("live");
|
|
204
|
+
if (isLive && video.seekable && video.seekable.length > 0) {
|
|
205
|
+
const start = video.seekable.start(0);
|
|
206
|
+
const end = video.seekable.end(video.seekable.length - 1);
|
|
207
|
+
api.setState("seekableRange", { start, end });
|
|
208
|
+
const isAtLiveEdge = end - video.currentTime < 10;
|
|
209
|
+
api.setState("liveEdge", isAtLiveEdge);
|
|
210
|
+
api.setState("liveLatency", Math.max(0, end - video.currentTime));
|
|
211
|
+
}
|
|
169
212
|
});
|
|
170
213
|
addHandler("durationchange", () => {
|
|
171
214
|
api.setState("duration", video.duration || 0);
|
|
@@ -4694,6 +4737,9 @@ function createAnalyticsPlugin(config) {
|
|
|
4694
4737
|
fatal: error.fatal ?? false
|
|
4695
4738
|
};
|
|
4696
4739
|
session.errors.push(errorEvent);
|
|
4740
|
+
if (session.errors.length > 100) {
|
|
4741
|
+
session.errors = session.errors.slice(-100);
|
|
4742
|
+
}
|
|
4697
4743
|
sendBeacon("error", {
|
|
4698
4744
|
errorType: errorEvent.type,
|
|
4699
4745
|
errorMessage: errorEvent.message,
|
|
@@ -4720,6 +4766,9 @@ function createAnalyticsPlugin(config) {
|
|
|
4720
4766
|
height: currentQuality.height
|
|
4721
4767
|
};
|
|
4722
4768
|
session.bitrateHistory.push(bitrateChange);
|
|
4769
|
+
if (session.bitrateHistory.length > 500) {
|
|
4770
|
+
session.bitrateHistory = session.bitrateHistory.slice(-500);
|
|
4771
|
+
}
|
|
4723
4772
|
if (currentQuality.bitrate > session.maxBitrate) {
|
|
4724
4773
|
session.maxBitrate = currentQuality.bitrate;
|
|
4725
4774
|
}
|
|
@@ -7188,8 +7237,9 @@ class ScarlettPlayer {
|
|
|
7188
7237
|
*/
|
|
7189
7238
|
setPlaybackRate(rate) {
|
|
7190
7239
|
this.checkDestroyed();
|
|
7191
|
-
|
|
7192
|
-
this.
|
|
7240
|
+
const clampedRate = Math.max(0.0625, Math.min(16, rate));
|
|
7241
|
+
this.stateManager.set("playbackRate", clampedRate);
|
|
7242
|
+
this.eventBus.emit("playback:ratechange", { rate: clampedRate });
|
|
7193
7243
|
}
|
|
7194
7244
|
/**
|
|
7195
7245
|
* Set autoplay state.
|
|
@@ -7318,6 +7368,13 @@ class ScarlettPlayer {
|
|
|
7318
7368
|
}
|
|
7319
7369
|
const provider = this._currentProvider;
|
|
7320
7370
|
if (typeof provider.setLevel === "function") {
|
|
7371
|
+
if (index !== -1) {
|
|
7372
|
+
const levels = this.getQualities();
|
|
7373
|
+
if (levels.length > 0 && (index < 0 || index >= levels.length)) {
|
|
7374
|
+
this.logger.warn(`Invalid quality index: ${index} (available: ${levels.length})`);
|
|
7375
|
+
return;
|
|
7376
|
+
}
|
|
7377
|
+
}
|
|
7321
7378
|
provider.setLevel(index);
|
|
7322
7379
|
this.eventBus.emit("quality:change", {
|
|
7323
7380
|
quality: index === -1 ? "auto" : `level-${index}`,
|
|
@@ -7558,18 +7615,40 @@ class ScarlettPlayer {
|
|
|
7558
7615
|
* @private
|
|
7559
7616
|
*/
|
|
7560
7617
|
detectMimeType(source) {
|
|
7561
|
-
|
|
7618
|
+
let path = source;
|
|
7619
|
+
try {
|
|
7620
|
+
path = new URL(source).pathname;
|
|
7621
|
+
} catch {
|
|
7622
|
+
const noQuery = source.split("?")[0] ?? source;
|
|
7623
|
+
path = noQuery.split("#")[0] ?? noQuery;
|
|
7624
|
+
}
|
|
7625
|
+
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
|
7562
7626
|
switch (ext) {
|
|
7563
7627
|
case "m3u8":
|
|
7564
7628
|
return "application/x-mpegURL";
|
|
7565
7629
|
case "mpd":
|
|
7566
7630
|
return "application/dash+xml";
|
|
7567
7631
|
case "mp4":
|
|
7632
|
+
case "m4v":
|
|
7568
7633
|
return "video/mp4";
|
|
7569
7634
|
case "webm":
|
|
7570
7635
|
return "video/webm";
|
|
7571
7636
|
case "ogg":
|
|
7637
|
+
case "ogv":
|
|
7572
7638
|
return "video/ogg";
|
|
7639
|
+
case "mov":
|
|
7640
|
+
return "video/quicktime";
|
|
7641
|
+
case "mkv":
|
|
7642
|
+
return "video/x-matroska";
|
|
7643
|
+
case "mp3":
|
|
7644
|
+
return "audio/mpeg";
|
|
7645
|
+
case "wav":
|
|
7646
|
+
return "audio/wav";
|
|
7647
|
+
case "flac":
|
|
7648
|
+
return "audio/flac";
|
|
7649
|
+
case "aac":
|
|
7650
|
+
case "m4a":
|
|
7651
|
+
return "audio/mp4";
|
|
7573
7652
|
default:
|
|
7574
7653
|
return "video/mp4";
|
|
7575
7654
|
}
|