@scarlett-player/native 0.1.1 → 0.2.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/LICENSE +21 -0
- package/dist/index.cjs +52 -8
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +52 -8
- package/package.json +12 -12
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Hackney Enterprises Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.cjs
CHANGED
|
@@ -24,15 +24,27 @@ __export(index_exports, {
|
|
|
24
24
|
default: () => index_default
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
|
-
var
|
|
27
|
+
var import_core = require("@scarlett-player/core");
|
|
28
|
+
var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "mkv", "ogv", "m4v"];
|
|
29
|
+
var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "weba"];
|
|
30
|
+
var SUPPORTED_EXTENSIONS = [...VIDEO_EXTENSIONS, ...AUDIO_EXTENSIONS];
|
|
28
31
|
var MIME_TYPES = {
|
|
32
|
+
// Video
|
|
29
33
|
mp4: "video/mp4",
|
|
30
34
|
m4v: "video/mp4",
|
|
31
35
|
webm: "video/webm",
|
|
32
36
|
mov: "video/quicktime",
|
|
33
37
|
mkv: "video/x-matroska",
|
|
34
38
|
ogv: "video/ogg",
|
|
35
|
-
|
|
39
|
+
// Audio
|
|
40
|
+
mp3: "audio/mpeg",
|
|
41
|
+
wav: "audio/wav",
|
|
42
|
+
ogg: "audio/ogg",
|
|
43
|
+
flac: "audio/flac",
|
|
44
|
+
aac: "audio/aac",
|
|
45
|
+
m4a: "audio/mp4",
|
|
46
|
+
opus: "audio/opus",
|
|
47
|
+
weba: "audio/webm"
|
|
36
48
|
};
|
|
37
49
|
function createNativePlugin(config) {
|
|
38
50
|
const preload = config?.preload ?? "metadata";
|
|
@@ -53,9 +65,13 @@ function createNativePlugin(config) {
|
|
|
53
65
|
const getMimeType = (ext) => {
|
|
54
66
|
return MIME_TYPES[ext] || "video/mp4";
|
|
55
67
|
};
|
|
68
|
+
const isAudioExtension = (ext) => {
|
|
69
|
+
return AUDIO_EXTENSIONS.includes(ext);
|
|
70
|
+
};
|
|
56
71
|
const canBrowserPlay = (mimeType) => {
|
|
57
|
-
const
|
|
58
|
-
const
|
|
72
|
+
const isAudio = mimeType.startsWith("audio/");
|
|
73
|
+
const testElement = isAudio ? document.createElement("audio") : document.createElement("video");
|
|
74
|
+
const canPlay = testElement.canPlayType(mimeType);
|
|
59
75
|
return canPlay === "probably" || canPlay === "maybe";
|
|
60
76
|
};
|
|
61
77
|
const getOrCreateVideo = () => {
|
|
@@ -70,6 +86,10 @@ function createNativePlugin(config) {
|
|
|
70
86
|
video.preload = preload;
|
|
71
87
|
video.controls = false;
|
|
72
88
|
video.playsInline = true;
|
|
89
|
+
const poster = api?.getState("poster");
|
|
90
|
+
if (poster) {
|
|
91
|
+
video.poster = poster;
|
|
92
|
+
}
|
|
73
93
|
api?.container.appendChild(video);
|
|
74
94
|
return video;
|
|
75
95
|
};
|
|
@@ -162,7 +182,7 @@ function createNativePlugin(config) {
|
|
|
162
182
|
}
|
|
163
183
|
api?.logger.error("Video error", { code: error?.code, message });
|
|
164
184
|
api?.emit("error", {
|
|
165
|
-
code:
|
|
185
|
+
code: import_core.ErrorCode.PLAYBACK_FAILED,
|
|
166
186
|
message,
|
|
167
187
|
fatal: true,
|
|
168
188
|
timestamp: Date.now()
|
|
@@ -185,10 +205,10 @@ function createNativePlugin(config) {
|
|
|
185
205
|
};
|
|
186
206
|
const plugin = {
|
|
187
207
|
id: "native-provider",
|
|
188
|
-
name: "Native
|
|
208
|
+
name: "Native Media Provider",
|
|
189
209
|
version: "1.0.0",
|
|
190
210
|
type: "provider",
|
|
191
|
-
description: "Native HTML5
|
|
211
|
+
description: "Native HTML5 playback for video (MP4, WebM, MOV) and audio (MP3, WAV, FLAC, AAC)",
|
|
192
212
|
canPlay(src) {
|
|
193
213
|
const ext = getExtension(src);
|
|
194
214
|
if (!SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
@@ -250,11 +270,35 @@ function createNativePlugin(config) {
|
|
|
250
270
|
if (!api) throw new Error("Plugin not initialized");
|
|
251
271
|
const ext = getExtension(src);
|
|
252
272
|
const mimeType = getMimeType(ext);
|
|
253
|
-
|
|
273
|
+
const isAudio = isAudioExtension(ext);
|
|
274
|
+
api.logger.info("Loading native media source", { src, mimeType, isAudio });
|
|
254
275
|
cleanup();
|
|
255
276
|
api.setState("playbackState", "loading");
|
|
256
277
|
api.setState("buffering", true);
|
|
278
|
+
api.setState("mediaType", isAudio ? "audio" : "video");
|
|
279
|
+
if (isAudio) {
|
|
280
|
+
try {
|
|
281
|
+
const url = new URL(src, window.location.href);
|
|
282
|
+
const filename = url.pathname.split("/").pop() || "Audio";
|
|
283
|
+
const title = decodeURIComponent(filename.replace(/\.[^.]+$/, "").replace(/[-_]/g, " "));
|
|
284
|
+
api.setState("title", title);
|
|
285
|
+
} catch {
|
|
286
|
+
api.setState("title", "Audio");
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
api.setState("qualities", []);
|
|
290
|
+
api.setState("currentQuality", null);
|
|
257
291
|
const videoEl = getOrCreateVideo();
|
|
292
|
+
if (isAudio) {
|
|
293
|
+
videoEl.style.display = "none";
|
|
294
|
+
videoEl.poster = "";
|
|
295
|
+
} else {
|
|
296
|
+
videoEl.style.display = "block";
|
|
297
|
+
const poster = api.getState("poster");
|
|
298
|
+
if (poster) {
|
|
299
|
+
videoEl.poster = poster;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
258
302
|
cleanupEvents = setupEventListeners(videoEl);
|
|
259
303
|
return new Promise((resolve, reject) => {
|
|
260
304
|
const onLoaded = () => {
|
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { PluginType, IPluginAPI } from '@scarlett-player/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Native
|
|
4
|
+
* Native Media Provider Plugin for Scarlett Player
|
|
5
5
|
*
|
|
6
6
|
* Provides playback for native browser-supported formats:
|
|
7
|
+
*
|
|
8
|
+
* Video:
|
|
7
9
|
* - MP4 (H.264/AAC)
|
|
8
10
|
* - WebM (VP8/VP9/Opus)
|
|
9
11
|
* - MOV (H.264/AAC)
|
|
10
12
|
* - MKV (varies by browser)
|
|
11
13
|
* - OGG/OGV (Theora/Vorbis)
|
|
14
|
+
*
|
|
15
|
+
* Audio:
|
|
16
|
+
* - MP3 (MPEG Audio Layer 3)
|
|
17
|
+
* - WAV (Waveform Audio)
|
|
18
|
+
* - OGG (Vorbis)
|
|
19
|
+
* - FLAC (Free Lossless Audio Codec)
|
|
20
|
+
* - AAC (Advanced Audio Coding)
|
|
21
|
+
* - M4A (MPEG-4 Audio)
|
|
12
22
|
*/
|
|
13
23
|
|
|
14
24
|
interface NativePluginConfig {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import { PluginType, IPluginAPI } from '@scarlett-player/core';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Native
|
|
4
|
+
* Native Media Provider Plugin for Scarlett Player
|
|
5
5
|
*
|
|
6
6
|
* Provides playback for native browser-supported formats:
|
|
7
|
+
*
|
|
8
|
+
* Video:
|
|
7
9
|
* - MP4 (H.264/AAC)
|
|
8
10
|
* - WebM (VP8/VP9/Opus)
|
|
9
11
|
* - MOV (H.264/AAC)
|
|
10
12
|
* - MKV (varies by browser)
|
|
11
13
|
* - OGG/OGV (Theora/Vorbis)
|
|
14
|
+
*
|
|
15
|
+
* Audio:
|
|
16
|
+
* - MP3 (MPEG Audio Layer 3)
|
|
17
|
+
* - WAV (Waveform Audio)
|
|
18
|
+
* - OGG (Vorbis)
|
|
19
|
+
* - FLAC (Free Lossless Audio Codec)
|
|
20
|
+
* - AAC (Advanced Audio Coding)
|
|
21
|
+
* - M4A (MPEG-4 Audio)
|
|
12
22
|
*/
|
|
13
23
|
|
|
14
24
|
interface NativePluginConfig {
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
|
|
2
|
+
import { ErrorCode } from "@scarlett-player/core";
|
|
3
|
+
var VIDEO_EXTENSIONS = ["mp4", "webm", "mov", "mkv", "ogv", "m4v"];
|
|
4
|
+
var AUDIO_EXTENSIONS = ["mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "weba"];
|
|
5
|
+
var SUPPORTED_EXTENSIONS = [...VIDEO_EXTENSIONS, ...AUDIO_EXTENSIONS];
|
|
3
6
|
var MIME_TYPES = {
|
|
7
|
+
// Video
|
|
4
8
|
mp4: "video/mp4",
|
|
5
9
|
m4v: "video/mp4",
|
|
6
10
|
webm: "video/webm",
|
|
7
11
|
mov: "video/quicktime",
|
|
8
12
|
mkv: "video/x-matroska",
|
|
9
13
|
ogv: "video/ogg",
|
|
10
|
-
|
|
14
|
+
// Audio
|
|
15
|
+
mp3: "audio/mpeg",
|
|
16
|
+
wav: "audio/wav",
|
|
17
|
+
ogg: "audio/ogg",
|
|
18
|
+
flac: "audio/flac",
|
|
19
|
+
aac: "audio/aac",
|
|
20
|
+
m4a: "audio/mp4",
|
|
21
|
+
opus: "audio/opus",
|
|
22
|
+
weba: "audio/webm"
|
|
11
23
|
};
|
|
12
24
|
function createNativePlugin(config) {
|
|
13
25
|
const preload = config?.preload ?? "metadata";
|
|
@@ -28,9 +40,13 @@ function createNativePlugin(config) {
|
|
|
28
40
|
const getMimeType = (ext) => {
|
|
29
41
|
return MIME_TYPES[ext] || "video/mp4";
|
|
30
42
|
};
|
|
43
|
+
const isAudioExtension = (ext) => {
|
|
44
|
+
return AUDIO_EXTENSIONS.includes(ext);
|
|
45
|
+
};
|
|
31
46
|
const canBrowserPlay = (mimeType) => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
47
|
+
const isAudio = mimeType.startsWith("audio/");
|
|
48
|
+
const testElement = isAudio ? document.createElement("audio") : document.createElement("video");
|
|
49
|
+
const canPlay = testElement.canPlayType(mimeType);
|
|
34
50
|
return canPlay === "probably" || canPlay === "maybe";
|
|
35
51
|
};
|
|
36
52
|
const getOrCreateVideo = () => {
|
|
@@ -45,6 +61,10 @@ function createNativePlugin(config) {
|
|
|
45
61
|
video.preload = preload;
|
|
46
62
|
video.controls = false;
|
|
47
63
|
video.playsInline = true;
|
|
64
|
+
const poster = api?.getState("poster");
|
|
65
|
+
if (poster) {
|
|
66
|
+
video.poster = poster;
|
|
67
|
+
}
|
|
48
68
|
api?.container.appendChild(video);
|
|
49
69
|
return video;
|
|
50
70
|
};
|
|
@@ -137,7 +157,7 @@ function createNativePlugin(config) {
|
|
|
137
157
|
}
|
|
138
158
|
api?.logger.error("Video error", { code: error?.code, message });
|
|
139
159
|
api?.emit("error", {
|
|
140
|
-
code:
|
|
160
|
+
code: ErrorCode.PLAYBACK_FAILED,
|
|
141
161
|
message,
|
|
142
162
|
fatal: true,
|
|
143
163
|
timestamp: Date.now()
|
|
@@ -160,10 +180,10 @@ function createNativePlugin(config) {
|
|
|
160
180
|
};
|
|
161
181
|
const plugin = {
|
|
162
182
|
id: "native-provider",
|
|
163
|
-
name: "Native
|
|
183
|
+
name: "Native Media Provider",
|
|
164
184
|
version: "1.0.0",
|
|
165
185
|
type: "provider",
|
|
166
|
-
description: "Native HTML5
|
|
186
|
+
description: "Native HTML5 playback for video (MP4, WebM, MOV) and audio (MP3, WAV, FLAC, AAC)",
|
|
167
187
|
canPlay(src) {
|
|
168
188
|
const ext = getExtension(src);
|
|
169
189
|
if (!SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
@@ -225,11 +245,35 @@ function createNativePlugin(config) {
|
|
|
225
245
|
if (!api) throw new Error("Plugin not initialized");
|
|
226
246
|
const ext = getExtension(src);
|
|
227
247
|
const mimeType = getMimeType(ext);
|
|
228
|
-
|
|
248
|
+
const isAudio = isAudioExtension(ext);
|
|
249
|
+
api.logger.info("Loading native media source", { src, mimeType, isAudio });
|
|
229
250
|
cleanup();
|
|
230
251
|
api.setState("playbackState", "loading");
|
|
231
252
|
api.setState("buffering", true);
|
|
253
|
+
api.setState("mediaType", isAudio ? "audio" : "video");
|
|
254
|
+
if (isAudio) {
|
|
255
|
+
try {
|
|
256
|
+
const url = new URL(src, window.location.href);
|
|
257
|
+
const filename = url.pathname.split("/").pop() || "Audio";
|
|
258
|
+
const title = decodeURIComponent(filename.replace(/\.[^.]+$/, "").replace(/[-_]/g, " "));
|
|
259
|
+
api.setState("title", title);
|
|
260
|
+
} catch {
|
|
261
|
+
api.setState("title", "Audio");
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
api.setState("qualities", []);
|
|
265
|
+
api.setState("currentQuality", null);
|
|
232
266
|
const videoEl = getOrCreateVideo();
|
|
267
|
+
if (isAudio) {
|
|
268
|
+
videoEl.style.display = "none";
|
|
269
|
+
videoEl.poster = "";
|
|
270
|
+
} else {
|
|
271
|
+
videoEl.style.display = "block";
|
|
272
|
+
const poster = api.getState("poster");
|
|
273
|
+
if (poster) {
|
|
274
|
+
videoEl.poster = poster;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
233
277
|
cleanupEvents = setupEventListeners(videoEl);
|
|
234
278
|
return new Promise((resolve, reject) => {
|
|
235
279
|
const onLoaded = () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scarlett-player/native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.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",
|
|
@@ -16,20 +16,14 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
21
|
-
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
22
|
-
"test": "vitest --run",
|
|
23
|
-
"test:watch": "vitest"
|
|
24
|
-
},
|
|
25
19
|
"peerDependencies": {
|
|
26
|
-
"@scarlett-player/core": "^0.
|
|
20
|
+
"@scarlett-player/core": "^0.2.0"
|
|
27
21
|
},
|
|
28
22
|
"devDependencies": {
|
|
29
|
-
"@scarlett-player/core": "file:../../core",
|
|
30
23
|
"typescript": "^5.3.0",
|
|
31
24
|
"tsup": "^8.0.0",
|
|
32
|
-
"vitest": "^1.6.0"
|
|
25
|
+
"vitest": "^1.6.0",
|
|
26
|
+
"@scarlett-player/core": "0.2.0"
|
|
33
27
|
},
|
|
34
28
|
"keywords": [
|
|
35
29
|
"video",
|
|
@@ -49,5 +43,11 @@
|
|
|
49
43
|
"bugs": {
|
|
50
44
|
"url": "https://github.com/Hackney-Enterprises-Inc/scarlett-player/issues"
|
|
51
45
|
},
|
|
52
|
-
"homepage": "https://scarlettplayer.com"
|
|
53
|
-
|
|
46
|
+
"homepage": "https://scarlettplayer.com",
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsup src/index.ts --format esm,cjs --dts",
|
|
49
|
+
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|
|
50
|
+
"test": "vitest --run",
|
|
51
|
+
"test:watch": "vitest"
|
|
52
|
+
}
|
|
53
|
+
}
|