@scarlett-player/hls 1.7.0 → 1.8.1
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 +10 -10
- package/dist/{chunk-NU6WD6KW.js → chunk-VD46AQJB.js} +22 -5
- package/dist/index.cjs +22 -5
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/light.cjs +22 -5
- package/dist/light.js +1 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -11,16 +11,14 @@ npm install @scarlett-player/core @scarlett-player/hls
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {
|
|
14
|
+
import { createPlayer } from '@scarlett-player/core';
|
|
15
15
|
import { createHLSPlugin } from '@scarlett-player/hls';
|
|
16
16
|
|
|
17
|
-
const player =
|
|
17
|
+
const player = await createPlayer({
|
|
18
18
|
container: document.getElementById('player'),
|
|
19
|
+
src: 'https://example.com/video.m3u8',
|
|
19
20
|
plugins: [createHLSPlugin()],
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
-
await player.init();
|
|
23
|
-
await player.load('https://example.com/video.m3u8');
|
|
24
22
|
```
|
|
25
23
|
|
|
26
24
|
## Features
|
|
@@ -44,6 +42,8 @@ All options are optional; defaults shown.
|
|
|
44
42
|
|
|
45
43
|
```typescript
|
|
46
44
|
createHLSPlugin({
|
|
45
|
+
debug: false, // hls.js debug logging
|
|
46
|
+
|
|
47
47
|
// Buffering
|
|
48
48
|
maxBufferLength: 30, // Forward buffer target (seconds)
|
|
49
49
|
maxMaxBufferLength: 600, // Hard forward buffer cap (seconds)
|
|
@@ -96,15 +96,15 @@ import { createHLSPlugin } from '@scarlett-player/hls/light';
|
|
|
96
96
|
## Quality Selection
|
|
97
97
|
|
|
98
98
|
```typescript
|
|
99
|
-
// Get available qualities
|
|
99
|
+
// Get available qualities (HLSQualityLevel[], empty on native Safari HLS)
|
|
100
100
|
const qualities = player.getQualities();
|
|
101
|
-
// [{ index: 0, height: 1080, bitrate: 5000000 }, ...]
|
|
101
|
+
// [{ index: 0, width: 1920, height: 1080, bitrate: 5000000, label: '1080p', codec: 'avc1,mp4a' }, ...]
|
|
102
102
|
|
|
103
|
-
// Set quality (use -1 for auto)
|
|
104
|
-
player.setQuality(0);
|
|
103
|
+
// Set quality by index (use -1 for auto)
|
|
104
|
+
player.setQuality(0);
|
|
105
105
|
player.setQuality(-1); // Auto/ABR
|
|
106
106
|
|
|
107
|
-
// Get current quality
|
|
107
|
+
// Get current quality index (-1 while auto)
|
|
108
108
|
const current = player.getCurrentQuality();
|
|
109
109
|
```
|
|
110
110
|
|
|
@@ -222,8 +222,14 @@ function setupVideoEventHandlers(video, api) {
|
|
|
222
222
|
video.addEventListener(event, handler);
|
|
223
223
|
handlers.push({ event, handler });
|
|
224
224
|
};
|
|
225
|
+
const syncEndedFromElement = () => {
|
|
226
|
+
if (video.ended || !api.getState("ended")) return;
|
|
227
|
+
api.setState("ended", false);
|
|
228
|
+
api.setState("playbackState", video.paused ? "paused" : "playing");
|
|
229
|
+
};
|
|
225
230
|
addHandler("play", () => {
|
|
226
231
|
api.setState("paused", false);
|
|
232
|
+
syncEndedFromElement();
|
|
227
233
|
});
|
|
228
234
|
addHandler("playing", () => {
|
|
229
235
|
api.setState("playing", true);
|
|
@@ -231,6 +237,7 @@ function setupVideoEventHandlers(video, api) {
|
|
|
231
237
|
api.setState("waiting", false);
|
|
232
238
|
api.setState("buffering", false);
|
|
233
239
|
api.setState("playbackState", "playing");
|
|
240
|
+
syncEndedFromElement();
|
|
234
241
|
});
|
|
235
242
|
addHandler("pause", () => {
|
|
236
243
|
api.setState("playing", false);
|
|
@@ -285,6 +292,7 @@ function setupVideoEventHandlers(video, api) {
|
|
|
285
292
|
});
|
|
286
293
|
addHandler("seeking", () => {
|
|
287
294
|
api.setState("seeking", true);
|
|
295
|
+
syncEndedFromElement();
|
|
288
296
|
});
|
|
289
297
|
addHandler("seeked", () => {
|
|
290
298
|
api.setState("seeking", false);
|
|
@@ -392,6 +400,9 @@ function createValidatingPlaylistLoader(Hls) {
|
|
|
392
400
|
};
|
|
393
401
|
}
|
|
394
402
|
|
|
403
|
+
// src/version.ts
|
|
404
|
+
var PKG_VERSION = true ? "1.8.1" : "0.0.0-dev";
|
|
405
|
+
|
|
395
406
|
// src/create-hls-plugin.ts
|
|
396
407
|
var DEFAULT_CONFIG = {
|
|
397
408
|
debug: false,
|
|
@@ -450,6 +461,10 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
450
461
|
let onlineListener = null;
|
|
451
462
|
let reconnectTriggerError = null;
|
|
452
463
|
let reconnectExhausted = false;
|
|
464
|
+
const applyPoster = () => {
|
|
465
|
+
if (!video) return;
|
|
466
|
+
video.poster = api?.getState("poster") || "";
|
|
467
|
+
};
|
|
453
468
|
const getOrCreateVideo = () => {
|
|
454
469
|
if (video) return video;
|
|
455
470
|
const existing = api?.container.querySelector("video");
|
|
@@ -462,10 +477,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
462
477
|
video.preload = "metadata";
|
|
463
478
|
video.controls = false;
|
|
464
479
|
video.playsInline = true;
|
|
465
|
-
|
|
466
|
-
if (poster) {
|
|
467
|
-
video.poster = poster;
|
|
468
|
-
}
|
|
480
|
+
applyPoster();
|
|
469
481
|
api?.container.appendChild(video);
|
|
470
482
|
return video;
|
|
471
483
|
};
|
|
@@ -1008,7 +1020,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1008
1020
|
const plugin = {
|
|
1009
1021
|
id: "hls-provider",
|
|
1010
1022
|
name: variant.name,
|
|
1011
|
-
version:
|
|
1023
|
+
version: PKG_VERSION,
|
|
1012
1024
|
type: "provider",
|
|
1013
1025
|
description: variant.description,
|
|
1014
1026
|
canPlay(src) {
|
|
@@ -1099,6 +1111,9 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1099
1111
|
};
|
|
1100
1112
|
window.addEventListener("online", onlineListener);
|
|
1101
1113
|
}
|
|
1114
|
+
const unsubPoster = api.subscribeToState((event) => {
|
|
1115
|
+
if (event.key === "poster") applyPoster();
|
|
1116
|
+
});
|
|
1102
1117
|
api.onDestroy(() => {
|
|
1103
1118
|
unsubPlay();
|
|
1104
1119
|
unsubPause();
|
|
@@ -1107,6 +1122,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1107
1122
|
unsubMute();
|
|
1108
1123
|
unsubRate();
|
|
1109
1124
|
unsubQuality();
|
|
1125
|
+
unsubPoster();
|
|
1110
1126
|
});
|
|
1111
1127
|
},
|
|
1112
1128
|
async destroy() {
|
|
@@ -1132,6 +1148,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1132
1148
|
hasPlayedContent = false;
|
|
1133
1149
|
cleanup(new Error("HLS load cancelled: superseded by a new load"));
|
|
1134
1150
|
currentSrc = src;
|
|
1151
|
+
applyPoster();
|
|
1135
1152
|
api.setState("playbackState", "loading");
|
|
1136
1153
|
api.setState("buffering", true);
|
|
1137
1154
|
if (api.getState("airplayActive") && loader.supportsNativeHLS()) {
|
package/dist/index.cjs
CHANGED
|
@@ -317,8 +317,14 @@ function setupVideoEventHandlers(video, api) {
|
|
|
317
317
|
video.addEventListener(event, handler);
|
|
318
318
|
handlers.push({ event, handler });
|
|
319
319
|
};
|
|
320
|
+
const syncEndedFromElement = () => {
|
|
321
|
+
if (video.ended || !api.getState("ended")) return;
|
|
322
|
+
api.setState("ended", false);
|
|
323
|
+
api.setState("playbackState", video.paused ? "paused" : "playing");
|
|
324
|
+
};
|
|
320
325
|
addHandler("play", () => {
|
|
321
326
|
api.setState("paused", false);
|
|
327
|
+
syncEndedFromElement();
|
|
322
328
|
});
|
|
323
329
|
addHandler("playing", () => {
|
|
324
330
|
api.setState("playing", true);
|
|
@@ -326,6 +332,7 @@ function setupVideoEventHandlers(video, api) {
|
|
|
326
332
|
api.setState("waiting", false);
|
|
327
333
|
api.setState("buffering", false);
|
|
328
334
|
api.setState("playbackState", "playing");
|
|
335
|
+
syncEndedFromElement();
|
|
329
336
|
});
|
|
330
337
|
addHandler("pause", () => {
|
|
331
338
|
api.setState("playing", false);
|
|
@@ -380,6 +387,7 @@ function setupVideoEventHandlers(video, api) {
|
|
|
380
387
|
});
|
|
381
388
|
addHandler("seeking", () => {
|
|
382
389
|
api.setState("seeking", true);
|
|
390
|
+
syncEndedFromElement();
|
|
383
391
|
});
|
|
384
392
|
addHandler("seeked", () => {
|
|
385
393
|
api.setState("seeking", false);
|
|
@@ -498,6 +506,9 @@ function sanitizeUrl(url) {
|
|
|
498
506
|
}
|
|
499
507
|
}
|
|
500
508
|
|
|
509
|
+
// src/version.ts
|
|
510
|
+
var PKG_VERSION = true ? "1.8.1" : "0.0.0-dev";
|
|
511
|
+
|
|
501
512
|
// src/create-hls-plugin.ts
|
|
502
513
|
var DEFAULT_CONFIG = {
|
|
503
514
|
debug: false,
|
|
@@ -556,6 +567,10 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
556
567
|
let onlineListener = null;
|
|
557
568
|
let reconnectTriggerError = null;
|
|
558
569
|
let reconnectExhausted = false;
|
|
570
|
+
const applyPoster = () => {
|
|
571
|
+
if (!video) return;
|
|
572
|
+
video.poster = api?.getState("poster") || "";
|
|
573
|
+
};
|
|
559
574
|
const getOrCreateVideo = () => {
|
|
560
575
|
if (video) return video;
|
|
561
576
|
const existing = api?.container.querySelector("video");
|
|
@@ -568,10 +583,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
568
583
|
video.preload = "metadata";
|
|
569
584
|
video.controls = false;
|
|
570
585
|
video.playsInline = true;
|
|
571
|
-
|
|
572
|
-
if (poster) {
|
|
573
|
-
video.poster = poster;
|
|
574
|
-
}
|
|
586
|
+
applyPoster();
|
|
575
587
|
api?.container.appendChild(video);
|
|
576
588
|
return video;
|
|
577
589
|
};
|
|
@@ -1114,7 +1126,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1114
1126
|
const plugin = {
|
|
1115
1127
|
id: "hls-provider",
|
|
1116
1128
|
name: variant.name,
|
|
1117
|
-
version:
|
|
1129
|
+
version: PKG_VERSION,
|
|
1118
1130
|
type: "provider",
|
|
1119
1131
|
description: variant.description,
|
|
1120
1132
|
canPlay(src) {
|
|
@@ -1205,6 +1217,9 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1205
1217
|
};
|
|
1206
1218
|
window.addEventListener("online", onlineListener);
|
|
1207
1219
|
}
|
|
1220
|
+
const unsubPoster = api.subscribeToState((event) => {
|
|
1221
|
+
if (event.key === "poster") applyPoster();
|
|
1222
|
+
});
|
|
1208
1223
|
api.onDestroy(() => {
|
|
1209
1224
|
unsubPlay();
|
|
1210
1225
|
unsubPause();
|
|
@@ -1213,6 +1228,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1213
1228
|
unsubMute();
|
|
1214
1229
|
unsubRate();
|
|
1215
1230
|
unsubQuality();
|
|
1231
|
+
unsubPoster();
|
|
1216
1232
|
});
|
|
1217
1233
|
},
|
|
1218
1234
|
async destroy() {
|
|
@@ -1238,6 +1254,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1238
1254
|
hasPlayedContent = false;
|
|
1239
1255
|
cleanup(new Error("HLS load cancelled: superseded by a new load"));
|
|
1240
1256
|
currentSrc = src;
|
|
1257
|
+
applyPoster();
|
|
1241
1258
|
api.setState("playbackState", "loading");
|
|
1242
1259
|
api.setState("buffering", true);
|
|
1243
1260
|
if (api.getState("airplayActive") && loader.supportsNativeHLS()) {
|
package/dist/index.d.cts
CHANGED
|
@@ -20,9 +20,10 @@ import '@scarlett-player/core';
|
|
|
20
20
|
*
|
|
21
21
|
* @example
|
|
22
22
|
* ```ts
|
|
23
|
+
* import { createPlayer } from '@scarlett-player/core';
|
|
23
24
|
* import { createHLSPlugin } from '@scarlett-player/hls';
|
|
24
25
|
*
|
|
25
|
-
* const player =
|
|
26
|
+
* const player = await createPlayer({
|
|
26
27
|
* container: document.getElementById('player'),
|
|
27
28
|
* plugins: [createHLSPlugin()],
|
|
28
29
|
* });
|
package/dist/index.d.ts
CHANGED
|
@@ -20,9 +20,10 @@ import '@scarlett-player/core';
|
|
|
20
20
|
*
|
|
21
21
|
* @example
|
|
22
22
|
* ```ts
|
|
23
|
+
* import { createPlayer } from '@scarlett-player/core';
|
|
23
24
|
* import { createHLSPlugin } from '@scarlett-player/hls';
|
|
24
25
|
*
|
|
25
|
-
* const player =
|
|
26
|
+
* const player = await createPlayer({
|
|
26
27
|
* container: document.getElementById('player'),
|
|
27
28
|
* plugins: [createHLSPlugin()],
|
|
28
29
|
* });
|
package/dist/index.js
CHANGED
package/dist/light.cjs
CHANGED
|
@@ -317,8 +317,14 @@ function setupVideoEventHandlers(video, api) {
|
|
|
317
317
|
video.addEventListener(event, handler);
|
|
318
318
|
handlers.push({ event, handler });
|
|
319
319
|
};
|
|
320
|
+
const syncEndedFromElement = () => {
|
|
321
|
+
if (video.ended || !api.getState("ended")) return;
|
|
322
|
+
api.setState("ended", false);
|
|
323
|
+
api.setState("playbackState", video.paused ? "paused" : "playing");
|
|
324
|
+
};
|
|
320
325
|
addHandler("play", () => {
|
|
321
326
|
api.setState("paused", false);
|
|
327
|
+
syncEndedFromElement();
|
|
322
328
|
});
|
|
323
329
|
addHandler("playing", () => {
|
|
324
330
|
api.setState("playing", true);
|
|
@@ -326,6 +332,7 @@ function setupVideoEventHandlers(video, api) {
|
|
|
326
332
|
api.setState("waiting", false);
|
|
327
333
|
api.setState("buffering", false);
|
|
328
334
|
api.setState("playbackState", "playing");
|
|
335
|
+
syncEndedFromElement();
|
|
329
336
|
});
|
|
330
337
|
addHandler("pause", () => {
|
|
331
338
|
api.setState("playing", false);
|
|
@@ -380,6 +387,7 @@ function setupVideoEventHandlers(video, api) {
|
|
|
380
387
|
});
|
|
381
388
|
addHandler("seeking", () => {
|
|
382
389
|
api.setState("seeking", true);
|
|
390
|
+
syncEndedFromElement();
|
|
383
391
|
});
|
|
384
392
|
addHandler("seeked", () => {
|
|
385
393
|
api.setState("seeking", false);
|
|
@@ -498,6 +506,9 @@ function sanitizeUrl(url) {
|
|
|
498
506
|
}
|
|
499
507
|
}
|
|
500
508
|
|
|
509
|
+
// src/version.ts
|
|
510
|
+
var PKG_VERSION = true ? "1.8.1" : "0.0.0-dev";
|
|
511
|
+
|
|
501
512
|
// src/create-hls-plugin.ts
|
|
502
513
|
var DEFAULT_CONFIG = {
|
|
503
514
|
debug: false,
|
|
@@ -556,6 +567,10 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
556
567
|
let onlineListener = null;
|
|
557
568
|
let reconnectTriggerError = null;
|
|
558
569
|
let reconnectExhausted = false;
|
|
570
|
+
const applyPoster = () => {
|
|
571
|
+
if (!video) return;
|
|
572
|
+
video.poster = api?.getState("poster") || "";
|
|
573
|
+
};
|
|
559
574
|
const getOrCreateVideo = () => {
|
|
560
575
|
if (video) return video;
|
|
561
576
|
const existing = api?.container.querySelector("video");
|
|
@@ -568,10 +583,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
568
583
|
video.preload = "metadata";
|
|
569
584
|
video.controls = false;
|
|
570
585
|
video.playsInline = true;
|
|
571
|
-
|
|
572
|
-
if (poster) {
|
|
573
|
-
video.poster = poster;
|
|
574
|
-
}
|
|
586
|
+
applyPoster();
|
|
575
587
|
api?.container.appendChild(video);
|
|
576
588
|
return video;
|
|
577
589
|
};
|
|
@@ -1114,7 +1126,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1114
1126
|
const plugin = {
|
|
1115
1127
|
id: "hls-provider",
|
|
1116
1128
|
name: variant.name,
|
|
1117
|
-
version:
|
|
1129
|
+
version: PKG_VERSION,
|
|
1118
1130
|
type: "provider",
|
|
1119
1131
|
description: variant.description,
|
|
1120
1132
|
canPlay(src) {
|
|
@@ -1205,6 +1217,9 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1205
1217
|
};
|
|
1206
1218
|
window.addEventListener("online", onlineListener);
|
|
1207
1219
|
}
|
|
1220
|
+
const unsubPoster = api.subscribeToState((event) => {
|
|
1221
|
+
if (event.key === "poster") applyPoster();
|
|
1222
|
+
});
|
|
1208
1223
|
api.onDestroy(() => {
|
|
1209
1224
|
unsubPlay();
|
|
1210
1225
|
unsubPause();
|
|
@@ -1213,6 +1228,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1213
1228
|
unsubMute();
|
|
1214
1229
|
unsubRate();
|
|
1215
1230
|
unsubQuality();
|
|
1231
|
+
unsubPoster();
|
|
1216
1232
|
});
|
|
1217
1233
|
},
|
|
1218
1234
|
async destroy() {
|
|
@@ -1238,6 +1254,7 @@ function createHLSPluginWith(loader, variant, config) {
|
|
|
1238
1254
|
hasPlayedContent = false;
|
|
1239
1255
|
cleanup(new Error("HLS load cancelled: superseded by a new load"));
|
|
1240
1256
|
currentSrc = src;
|
|
1257
|
+
applyPoster();
|
|
1241
1258
|
api.setState("playbackState", "loading");
|
|
1242
1259
|
api.setState("buffering", true);
|
|
1243
1260
|
if (api.getState("airplayActive") && loader.supportsNativeHLS()) {
|
package/dist/light.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scarlett-player/hls",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"description": "HLS Provider Plugin for Scarlett Player",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@scarlett-player/core": "^1.0
|
|
26
|
-
"hls.js": "^1.
|
|
25
|
+
"@scarlett-player/core": "^1.8.0",
|
|
26
|
+
"hls.js": "^1.6.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependenciesMeta": {
|
|
29
29
|
"hls.js": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"vitest": "^1.6.0",
|
|
38
38
|
"@vitest/coverage-v8": "^1.6.0",
|
|
39
39
|
"jsdom": "^24.0.0",
|
|
40
|
-
"@scarlett-player/core": "1.
|
|
40
|
+
"@scarlett-player/core": "1.8.1"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"video",
|
|
@@ -58,10 +58,11 @@
|
|
|
58
58
|
},
|
|
59
59
|
"homepage": "https://scarlettplayer.com",
|
|
60
60
|
"scripts": {
|
|
61
|
-
"build": "tsup
|
|
62
|
-
"dev": "tsup
|
|
61
|
+
"build": "tsup",
|
|
62
|
+
"dev": "tsup --watch",
|
|
63
63
|
"test": "vitest --run",
|
|
64
64
|
"test:watch": "vitest",
|
|
65
|
-
"test:coverage": "vitest --coverage"
|
|
65
|
+
"test:coverage": "vitest --coverage",
|
|
66
|
+
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
|
|
66
67
|
}
|
|
67
68
|
}
|