@scarlett-player/native 1.6.0 → 1.8.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/README.md +3 -5
- package/dist/index.cjs +35 -15
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +35 -15
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -11,16 +11,14 @@ npm install @scarlett-player/core @scarlett-player/native
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {
|
|
14
|
+
import { createPlayer } from '@scarlett-player/core';
|
|
15
15
|
import { createNativePlugin } from '@scarlett-player/native';
|
|
16
16
|
|
|
17
|
-
const player =
|
|
17
|
+
const player = await createPlayer({
|
|
18
18
|
container: document.getElementById('player'),
|
|
19
|
+
src: 'https://example.com/video.mp4',
|
|
19
20
|
plugins: [createNativePlugin()],
|
|
20
21
|
});
|
|
21
|
-
|
|
22
|
-
await player.init();
|
|
23
|
-
await player.load('https://example.com/video.mp4');
|
|
24
22
|
```
|
|
25
23
|
|
|
26
24
|
## Supported Formats
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,11 @@ __export(index_exports, {
|
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
27
|
var import_core = require("@scarlett-player/core");
|
|
28
|
+
|
|
29
|
+
// src/version.ts
|
|
30
|
+
var PKG_VERSION = true ? "1.7.1" : "0.0.0-dev";
|
|
31
|
+
|
|
32
|
+
// src/index.ts
|
|
28
33
|
var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "mkv", "ogv", "m4v"];
|
|
29
34
|
var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "weba"];
|
|
30
35
|
var SUPPORTED_EXTENSIONS = [...VIDEO_EXTENSIONS, ...AUDIO_EXTENSIONS];
|
|
@@ -53,6 +58,7 @@ function createNativePlugin(config) {
|
|
|
53
58
|
let video = null;
|
|
54
59
|
let cleanupEvents = null;
|
|
55
60
|
let derived_title = null;
|
|
61
|
+
let is_audio_source = false;
|
|
56
62
|
const getExtension = (src) => {
|
|
57
63
|
try {
|
|
58
64
|
const url = new URL(src, window.location.href);
|
|
@@ -76,6 +82,14 @@ function createNativePlugin(config) {
|
|
|
76
82
|
const canPlay = testElement.canPlayType(mimeType);
|
|
77
83
|
return canPlay === "probably" || canPlay === "maybe";
|
|
78
84
|
};
|
|
85
|
+
const applyPoster = () => {
|
|
86
|
+
if (!video) return;
|
|
87
|
+
if (is_audio_source) {
|
|
88
|
+
video.poster = "";
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
video.poster = api?.getState("poster") || "";
|
|
92
|
+
};
|
|
79
93
|
const getOrCreateVideo = () => {
|
|
80
94
|
if (video) return video;
|
|
81
95
|
const existing = api?.container.querySelector("video");
|
|
@@ -88,10 +102,7 @@ function createNativePlugin(config) {
|
|
|
88
102
|
video.preload = preload;
|
|
89
103
|
video.controls = false;
|
|
90
104
|
video.playsInline = true;
|
|
91
|
-
|
|
92
|
-
if (poster) {
|
|
93
|
-
video.poster = poster;
|
|
94
|
-
}
|
|
105
|
+
applyPoster();
|
|
95
106
|
api?.container.appendChild(video);
|
|
96
107
|
return video;
|
|
97
108
|
};
|
|
@@ -101,22 +112,32 @@ function createNativePlugin(config) {
|
|
|
101
112
|
videoEl.addEventListener(event, handler);
|
|
102
113
|
handlers.push([event, handler]);
|
|
103
114
|
};
|
|
115
|
+
const syncEndedFromElement = () => {
|
|
116
|
+
if (videoEl.ended || !api?.getState("ended")) return;
|
|
117
|
+
api?.setState("ended", false);
|
|
118
|
+
api?.setState("playbackState", videoEl.paused ? "paused" : "playing");
|
|
119
|
+
};
|
|
104
120
|
on("play", () => {
|
|
105
121
|
api?.setState("paused", false);
|
|
122
|
+
syncEndedFromElement();
|
|
106
123
|
});
|
|
107
124
|
on("playing", () => {
|
|
108
125
|
api?.setState("playing", true);
|
|
109
126
|
api?.setState("paused", false);
|
|
127
|
+
api?.setState("playbackState", "playing");
|
|
110
128
|
api?.emit("playback:play", void 0);
|
|
129
|
+
syncEndedFromElement();
|
|
111
130
|
});
|
|
112
131
|
on("pause", () => {
|
|
113
132
|
api?.setState("playing", false);
|
|
114
133
|
api?.setState("paused", true);
|
|
134
|
+
api?.setState("playbackState", "paused");
|
|
115
135
|
api?.emit("playback:pause", void 0);
|
|
116
136
|
});
|
|
117
137
|
on("ended", () => {
|
|
118
138
|
api?.setState("playing", false);
|
|
119
139
|
api?.setState("ended", true);
|
|
140
|
+
api?.setState("playbackState", "ended");
|
|
120
141
|
api?.emit("playback:ended", void 0);
|
|
121
142
|
});
|
|
122
143
|
on("timeupdate", () => {
|
|
@@ -152,6 +173,7 @@ function createNativePlugin(config) {
|
|
|
152
173
|
});
|
|
153
174
|
on("seeking", () => {
|
|
154
175
|
api?.setState("seeking", true);
|
|
176
|
+
syncEndedFromElement();
|
|
155
177
|
});
|
|
156
178
|
on("seeked", () => {
|
|
157
179
|
api?.setState("seeking", false);
|
|
@@ -246,7 +268,7 @@ function createNativePlugin(config) {
|
|
|
246
268
|
const plugin = {
|
|
247
269
|
id: "native-provider",
|
|
248
270
|
name: "Native Media Provider",
|
|
249
|
-
version:
|
|
271
|
+
version: PKG_VERSION,
|
|
250
272
|
type: "provider",
|
|
251
273
|
description: "Native HTML5 playback for video (MP4, WebM, MOV) and audio (MP3, WAV, FLAC, AAC)",
|
|
252
274
|
canPlay(src) {
|
|
@@ -288,6 +310,9 @@ function createNativePlugin(config) {
|
|
|
288
310
|
const unsubRate = api.on("playback:ratechange", ({ rate }) => {
|
|
289
311
|
if (video) video.playbackRate = rate;
|
|
290
312
|
});
|
|
313
|
+
const unsubPoster = api.subscribeToState((event) => {
|
|
314
|
+
if (event.key === "poster") applyPoster();
|
|
315
|
+
});
|
|
291
316
|
api.onDestroy(() => {
|
|
292
317
|
unsubPlay();
|
|
293
318
|
unsubPause();
|
|
@@ -295,6 +320,7 @@ function createNativePlugin(config) {
|
|
|
295
320
|
unsubVolume();
|
|
296
321
|
unsubMute();
|
|
297
322
|
unsubRate();
|
|
323
|
+
unsubPoster();
|
|
298
324
|
});
|
|
299
325
|
},
|
|
300
326
|
async destroy() {
|
|
@@ -306,12 +332,14 @@ function createNativePlugin(config) {
|
|
|
306
332
|
video = null;
|
|
307
333
|
api = null;
|
|
308
334
|
derived_title = null;
|
|
335
|
+
is_audio_source = false;
|
|
309
336
|
},
|
|
310
337
|
async loadSource(src) {
|
|
311
338
|
if (!api) throw new Error("Plugin not initialized");
|
|
312
339
|
const ext = getExtension(src);
|
|
313
340
|
const mimeType = getMimeType(ext);
|
|
314
341
|
const isAudio = isAudioExtension(ext);
|
|
342
|
+
is_audio_source = isAudio;
|
|
315
343
|
api.logger.info("Loading native media source", { src, mimeType, isAudio });
|
|
316
344
|
cleanup();
|
|
317
345
|
api.setState("playbackState", "loading");
|
|
@@ -335,16 +363,8 @@ function createNativePlugin(config) {
|
|
|
335
363
|
api.setState("qualities", []);
|
|
336
364
|
api.setState("currentQuality", null);
|
|
337
365
|
const videoEl = getOrCreateVideo();
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
videoEl.poster = "";
|
|
341
|
-
} else {
|
|
342
|
-
videoEl.style.display = "block";
|
|
343
|
-
const poster = api.getState("poster");
|
|
344
|
-
if (poster) {
|
|
345
|
-
videoEl.poster = poster;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
366
|
+
videoEl.style.display = isAudio ? "none" : "block";
|
|
367
|
+
applyPoster();
|
|
348
368
|
cleanupEvents = setupEventListeners(videoEl);
|
|
349
369
|
return new Promise((resolve, reject) => {
|
|
350
370
|
let watchdog = null;
|
package/dist/index.d.cts
CHANGED
|
@@ -50,9 +50,10 @@ interface INativePlugin {
|
|
|
50
50
|
*
|
|
51
51
|
* @example
|
|
52
52
|
* ```ts
|
|
53
|
+
* import { createPlayer } from '@scarlett-player/core';
|
|
53
54
|
* import { createNativePlugin } from '@scarlett-player/native';
|
|
54
55
|
*
|
|
55
|
-
* const player =
|
|
56
|
+
* const player = await createPlayer({
|
|
56
57
|
* container: document.getElementById('player'),
|
|
57
58
|
* plugins: [createNativePlugin()],
|
|
58
59
|
* });
|
package/dist/index.d.ts
CHANGED
|
@@ -50,9 +50,10 @@ interface INativePlugin {
|
|
|
50
50
|
*
|
|
51
51
|
* @example
|
|
52
52
|
* ```ts
|
|
53
|
+
* import { createPlayer } from '@scarlett-player/core';
|
|
53
54
|
* import { createNativePlugin } from '@scarlett-player/native';
|
|
54
55
|
*
|
|
55
|
-
* const player =
|
|
56
|
+
* const player = await createPlayer({
|
|
56
57
|
* container: document.getElementById('player'),
|
|
57
58
|
* plugins: [createNativePlugin()],
|
|
58
59
|
* });
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
import { ErrorCode } from "@scarlett-player/core";
|
|
3
|
+
|
|
4
|
+
// src/version.ts
|
|
5
|
+
var PKG_VERSION = true ? "1.7.1" : "0.0.0-dev";
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
3
8
|
var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "mkv", "ogv", "m4v"];
|
|
4
9
|
var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "weba"];
|
|
5
10
|
var SUPPORTED_EXTENSIONS = [...VIDEO_EXTENSIONS, ...AUDIO_EXTENSIONS];
|
|
@@ -28,6 +33,7 @@ function createNativePlugin(config) {
|
|
|
28
33
|
let video = null;
|
|
29
34
|
let cleanupEvents = null;
|
|
30
35
|
let derived_title = null;
|
|
36
|
+
let is_audio_source = false;
|
|
31
37
|
const getExtension = (src) => {
|
|
32
38
|
try {
|
|
33
39
|
const url = new URL(src, window.location.href);
|
|
@@ -51,6 +57,14 @@ function createNativePlugin(config) {
|
|
|
51
57
|
const canPlay = testElement.canPlayType(mimeType);
|
|
52
58
|
return canPlay === "probably" || canPlay === "maybe";
|
|
53
59
|
};
|
|
60
|
+
const applyPoster = () => {
|
|
61
|
+
if (!video) return;
|
|
62
|
+
if (is_audio_source) {
|
|
63
|
+
video.poster = "";
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
video.poster = api?.getState("poster") || "";
|
|
67
|
+
};
|
|
54
68
|
const getOrCreateVideo = () => {
|
|
55
69
|
if (video) return video;
|
|
56
70
|
const existing = api?.container.querySelector("video");
|
|
@@ -63,10 +77,7 @@ function createNativePlugin(config) {
|
|
|
63
77
|
video.preload = preload;
|
|
64
78
|
video.controls = false;
|
|
65
79
|
video.playsInline = true;
|
|
66
|
-
|
|
67
|
-
if (poster) {
|
|
68
|
-
video.poster = poster;
|
|
69
|
-
}
|
|
80
|
+
applyPoster();
|
|
70
81
|
api?.container.appendChild(video);
|
|
71
82
|
return video;
|
|
72
83
|
};
|
|
@@ -76,22 +87,32 @@ function createNativePlugin(config) {
|
|
|
76
87
|
videoEl.addEventListener(event, handler);
|
|
77
88
|
handlers.push([event, handler]);
|
|
78
89
|
};
|
|
90
|
+
const syncEndedFromElement = () => {
|
|
91
|
+
if (videoEl.ended || !api?.getState("ended")) return;
|
|
92
|
+
api?.setState("ended", false);
|
|
93
|
+
api?.setState("playbackState", videoEl.paused ? "paused" : "playing");
|
|
94
|
+
};
|
|
79
95
|
on("play", () => {
|
|
80
96
|
api?.setState("paused", false);
|
|
97
|
+
syncEndedFromElement();
|
|
81
98
|
});
|
|
82
99
|
on("playing", () => {
|
|
83
100
|
api?.setState("playing", true);
|
|
84
101
|
api?.setState("paused", false);
|
|
102
|
+
api?.setState("playbackState", "playing");
|
|
85
103
|
api?.emit("playback:play", void 0);
|
|
104
|
+
syncEndedFromElement();
|
|
86
105
|
});
|
|
87
106
|
on("pause", () => {
|
|
88
107
|
api?.setState("playing", false);
|
|
89
108
|
api?.setState("paused", true);
|
|
109
|
+
api?.setState("playbackState", "paused");
|
|
90
110
|
api?.emit("playback:pause", void 0);
|
|
91
111
|
});
|
|
92
112
|
on("ended", () => {
|
|
93
113
|
api?.setState("playing", false);
|
|
94
114
|
api?.setState("ended", true);
|
|
115
|
+
api?.setState("playbackState", "ended");
|
|
95
116
|
api?.emit("playback:ended", void 0);
|
|
96
117
|
});
|
|
97
118
|
on("timeupdate", () => {
|
|
@@ -127,6 +148,7 @@ function createNativePlugin(config) {
|
|
|
127
148
|
});
|
|
128
149
|
on("seeking", () => {
|
|
129
150
|
api?.setState("seeking", true);
|
|
151
|
+
syncEndedFromElement();
|
|
130
152
|
});
|
|
131
153
|
on("seeked", () => {
|
|
132
154
|
api?.setState("seeking", false);
|
|
@@ -221,7 +243,7 @@ function createNativePlugin(config) {
|
|
|
221
243
|
const plugin = {
|
|
222
244
|
id: "native-provider",
|
|
223
245
|
name: "Native Media Provider",
|
|
224
|
-
version:
|
|
246
|
+
version: PKG_VERSION,
|
|
225
247
|
type: "provider",
|
|
226
248
|
description: "Native HTML5 playback for video (MP4, WebM, MOV) and audio (MP3, WAV, FLAC, AAC)",
|
|
227
249
|
canPlay(src) {
|
|
@@ -263,6 +285,9 @@ function createNativePlugin(config) {
|
|
|
263
285
|
const unsubRate = api.on("playback:ratechange", ({ rate }) => {
|
|
264
286
|
if (video) video.playbackRate = rate;
|
|
265
287
|
});
|
|
288
|
+
const unsubPoster = api.subscribeToState((event) => {
|
|
289
|
+
if (event.key === "poster") applyPoster();
|
|
290
|
+
});
|
|
266
291
|
api.onDestroy(() => {
|
|
267
292
|
unsubPlay();
|
|
268
293
|
unsubPause();
|
|
@@ -270,6 +295,7 @@ function createNativePlugin(config) {
|
|
|
270
295
|
unsubVolume();
|
|
271
296
|
unsubMute();
|
|
272
297
|
unsubRate();
|
|
298
|
+
unsubPoster();
|
|
273
299
|
});
|
|
274
300
|
},
|
|
275
301
|
async destroy() {
|
|
@@ -281,12 +307,14 @@ function createNativePlugin(config) {
|
|
|
281
307
|
video = null;
|
|
282
308
|
api = null;
|
|
283
309
|
derived_title = null;
|
|
310
|
+
is_audio_source = false;
|
|
284
311
|
},
|
|
285
312
|
async loadSource(src) {
|
|
286
313
|
if (!api) throw new Error("Plugin not initialized");
|
|
287
314
|
const ext = getExtension(src);
|
|
288
315
|
const mimeType = getMimeType(ext);
|
|
289
316
|
const isAudio = isAudioExtension(ext);
|
|
317
|
+
is_audio_source = isAudio;
|
|
290
318
|
api.logger.info("Loading native media source", { src, mimeType, isAudio });
|
|
291
319
|
cleanup();
|
|
292
320
|
api.setState("playbackState", "loading");
|
|
@@ -310,16 +338,8 @@ function createNativePlugin(config) {
|
|
|
310
338
|
api.setState("qualities", []);
|
|
311
339
|
api.setState("currentQuality", null);
|
|
312
340
|
const videoEl = getOrCreateVideo();
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
videoEl.poster = "";
|
|
316
|
-
} else {
|
|
317
|
-
videoEl.style.display = "block";
|
|
318
|
-
const poster = api.getState("poster");
|
|
319
|
-
if (poster) {
|
|
320
|
-
videoEl.poster = poster;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
341
|
+
videoEl.style.display = isAudio ? "none" : "block";
|
|
342
|
+
applyPoster();
|
|
323
343
|
cleanupEvents = setupEventListeners(videoEl);
|
|
324
344
|
return new Promise((resolve, reject) => {
|
|
325
345
|
let watchdog = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scarlett-player/native",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "Native Video Provider Plugin for Scarlett Player (MP4, WebM, MOV, MKV)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@scarlett-player/core": "^1.0
|
|
20
|
+
"@scarlett-player/core": "^1.7.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"typescript": "^5.3.0",
|
|
24
24
|
"tsup": "^8.0.0",
|
|
25
25
|
"vitest": "^1.6.0",
|
|
26
|
-
"@scarlett-player/core": "1.
|
|
26
|
+
"@scarlett-player/core": "1.8.0"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"video",
|
|
@@ -45,9 +45,10 @@
|
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://scarlettplayer.com",
|
|
47
47
|
"scripts": {
|
|
48
|
-
"build": "tsup
|
|
49
|
-
"dev": "tsup
|
|
48
|
+
"build": "tsup",
|
|
49
|
+
"dev": "tsup --watch",
|
|
50
50
|
"test": "vitest --run",
|
|
51
|
-
"test:watch": "vitest"
|
|
51
|
+
"test:watch": "vitest",
|
|
52
|
+
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
|
|
52
53
|
}
|
|
53
54
|
}
|