@scarlett-player/hls 0.1.2 → 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/chunk-CBBDW7UR.js +263 -0
- package/dist/index.cjs +4 -0
- package/dist/index.d.cts +4 -100
- package/dist/index.d.ts +4 -100
- package/dist/index.js +10 -258
- package/dist/light.cjs +792 -0
- package/dist/light.d.cts +27 -0
- package/dist/light.d.ts +27 -0
- package/dist/light.js +503 -0
- package/dist/types-Co8zOXVb.d.cts +101 -0
- package/dist/types-Co8zOXVb.d.ts +101 -0
- package/package.json +18 -13
package/dist/index.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mapLevels,
|
|
3
|
+
setupHlsEventHandlers,
|
|
4
|
+
setupVideoEventHandlers
|
|
5
|
+
} from "./chunk-CBBDW7UR.js";
|
|
6
|
+
|
|
1
7
|
// src/hls-loader.ts
|
|
2
8
|
var hlsConstructor = null;
|
|
3
9
|
var loadingPromise = null;
|
|
@@ -50,264 +56,6 @@ function getHlsConstructor() {
|
|
|
50
56
|
return hlsConstructor;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
|
-
// src/quality.ts
|
|
54
|
-
function formatLevel(level) {
|
|
55
|
-
if (level.name) {
|
|
56
|
-
return level.name;
|
|
57
|
-
}
|
|
58
|
-
if (level.height) {
|
|
59
|
-
const standardLabels = {
|
|
60
|
-
2160: "4K",
|
|
61
|
-
1440: "1440p",
|
|
62
|
-
1080: "1080p",
|
|
63
|
-
720: "720p",
|
|
64
|
-
480: "480p",
|
|
65
|
-
360: "360p",
|
|
66
|
-
240: "240p",
|
|
67
|
-
144: "144p"
|
|
68
|
-
};
|
|
69
|
-
const closest = Object.keys(standardLabels).map(Number).sort((a, b) => Math.abs(a - level.height) - Math.abs(b - level.height))[0];
|
|
70
|
-
if (Math.abs(closest - level.height) <= 20) {
|
|
71
|
-
return standardLabels[closest];
|
|
72
|
-
}
|
|
73
|
-
return `${level.height}p`;
|
|
74
|
-
}
|
|
75
|
-
if (level.bitrate) {
|
|
76
|
-
return formatBitrate(level.bitrate);
|
|
77
|
-
}
|
|
78
|
-
return "Unknown";
|
|
79
|
-
}
|
|
80
|
-
function formatBitrate(bitrate) {
|
|
81
|
-
if (bitrate >= 1e6) {
|
|
82
|
-
return `${(bitrate / 1e6).toFixed(1)} Mbps`;
|
|
83
|
-
}
|
|
84
|
-
if (bitrate >= 1e3) {
|
|
85
|
-
return `${Math.round(bitrate / 1e3)} Kbps`;
|
|
86
|
-
}
|
|
87
|
-
return `${bitrate} bps`;
|
|
88
|
-
}
|
|
89
|
-
function mapLevels(levels, currentLevel) {
|
|
90
|
-
return levels.map((level, index) => ({
|
|
91
|
-
index,
|
|
92
|
-
width: level.width || 0,
|
|
93
|
-
height: level.height || 0,
|
|
94
|
-
bitrate: level.bitrate || 0,
|
|
95
|
-
label: formatLevel(level),
|
|
96
|
-
codec: level.codecSet
|
|
97
|
-
}));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// src/event-map.ts
|
|
101
|
-
var HLS_ERROR_TYPES = {
|
|
102
|
-
NETWORK_ERROR: "networkError",
|
|
103
|
-
MEDIA_ERROR: "mediaError",
|
|
104
|
-
KEY_SYSTEM_ERROR: "keySystemError",
|
|
105
|
-
MUX_ERROR: "muxError",
|
|
106
|
-
OTHER_ERROR: "otherError"
|
|
107
|
-
};
|
|
108
|
-
function mapErrorType(hlsType) {
|
|
109
|
-
switch (hlsType) {
|
|
110
|
-
case HLS_ERROR_TYPES.NETWORK_ERROR:
|
|
111
|
-
return "network";
|
|
112
|
-
case HLS_ERROR_TYPES.MEDIA_ERROR:
|
|
113
|
-
return "media";
|
|
114
|
-
case HLS_ERROR_TYPES.MUX_ERROR:
|
|
115
|
-
return "mux";
|
|
116
|
-
default:
|
|
117
|
-
return "other";
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
function parseHlsError(data) {
|
|
121
|
-
return {
|
|
122
|
-
type: mapErrorType(data.type),
|
|
123
|
-
details: data.details || "Unknown error",
|
|
124
|
-
fatal: data.fatal || false,
|
|
125
|
-
url: data.url,
|
|
126
|
-
reason: data.reason,
|
|
127
|
-
response: data.response
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function setupHlsEventHandlers(hls, api, callbacks) {
|
|
131
|
-
const handlers = [];
|
|
132
|
-
const addHandler = (event, handler) => {
|
|
133
|
-
hls.on(event, handler);
|
|
134
|
-
handlers.push({ event, handler });
|
|
135
|
-
};
|
|
136
|
-
addHandler("hlsManifestParsed", (_event, data) => {
|
|
137
|
-
api.logger.debug("HLS manifest parsed", { levels: data.levels.length });
|
|
138
|
-
const levels = data.levels.map((level, index) => ({
|
|
139
|
-
id: `level-${index}`,
|
|
140
|
-
label: formatLevel(level),
|
|
141
|
-
width: level.width,
|
|
142
|
-
height: level.height,
|
|
143
|
-
bitrate: level.bitrate,
|
|
144
|
-
active: index === hls.currentLevel
|
|
145
|
-
}));
|
|
146
|
-
api.setState("qualities", levels);
|
|
147
|
-
api.emit("quality:levels", {
|
|
148
|
-
levels: levels.map((l) => ({ id: l.id, label: l.label }))
|
|
149
|
-
});
|
|
150
|
-
callbacks.onManifestParsed?.(data.levels);
|
|
151
|
-
});
|
|
152
|
-
addHandler("hlsLevelSwitched", (_event, data) => {
|
|
153
|
-
const level = hls.levels[data.level];
|
|
154
|
-
const isAuto = callbacks.getIsAutoQuality?.() ?? hls.autoLevelEnabled;
|
|
155
|
-
api.logger.debug("HLS level switched", { level: data.level, height: level?.height, auto: isAuto });
|
|
156
|
-
if (level) {
|
|
157
|
-
const label = isAuto ? `Auto (${formatLevel(level)})` : formatLevel(level);
|
|
158
|
-
api.setState("currentQuality", {
|
|
159
|
-
id: isAuto ? "auto" : `level-${data.level}`,
|
|
160
|
-
label,
|
|
161
|
-
width: level.width,
|
|
162
|
-
height: level.height,
|
|
163
|
-
bitrate: level.bitrate,
|
|
164
|
-
active: true
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
api.emit("quality:change", {
|
|
168
|
-
quality: level ? formatLevel(level) : "auto",
|
|
169
|
-
auto: isAuto
|
|
170
|
-
});
|
|
171
|
-
callbacks.onLevelSwitched?.(data.level);
|
|
172
|
-
});
|
|
173
|
-
addHandler("hlsFragBuffered", () => {
|
|
174
|
-
api.setState("buffering", false);
|
|
175
|
-
callbacks.onBufferUpdate?.();
|
|
176
|
-
});
|
|
177
|
-
addHandler("hlsFragLoading", () => {
|
|
178
|
-
api.setState("buffering", true);
|
|
179
|
-
});
|
|
180
|
-
addHandler("hlsLevelLoaded", (_event, data) => {
|
|
181
|
-
if (data.details?.live !== void 0) {
|
|
182
|
-
api.setState("live", data.details.live);
|
|
183
|
-
callbacks.onLiveUpdate?.();
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
addHandler("hlsError", (_event, data) => {
|
|
187
|
-
const error = parseHlsError(data);
|
|
188
|
-
api.logger.warn("HLS error", { error });
|
|
189
|
-
callbacks.onError?.(error);
|
|
190
|
-
});
|
|
191
|
-
return () => {
|
|
192
|
-
for (const { event, handler } of handlers) {
|
|
193
|
-
hls.off(event, handler);
|
|
194
|
-
}
|
|
195
|
-
handlers.length = 0;
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
function setupVideoEventHandlers(video, api) {
|
|
199
|
-
const handlers = [];
|
|
200
|
-
const addHandler = (event, handler) => {
|
|
201
|
-
video.addEventListener(event, handler);
|
|
202
|
-
handlers.push({ event, handler });
|
|
203
|
-
};
|
|
204
|
-
addHandler("playing", () => {
|
|
205
|
-
api.setState("playing", true);
|
|
206
|
-
api.setState("paused", false);
|
|
207
|
-
api.setState("playbackState", "playing");
|
|
208
|
-
});
|
|
209
|
-
addHandler("pause", () => {
|
|
210
|
-
api.setState("playing", false);
|
|
211
|
-
api.setState("paused", true);
|
|
212
|
-
api.setState("playbackState", "paused");
|
|
213
|
-
});
|
|
214
|
-
addHandler("ended", () => {
|
|
215
|
-
api.setState("playing", false);
|
|
216
|
-
api.setState("ended", true);
|
|
217
|
-
api.setState("playbackState", "ended");
|
|
218
|
-
api.emit("playback:ended", void 0);
|
|
219
|
-
});
|
|
220
|
-
addHandler("timeupdate", () => {
|
|
221
|
-
api.setState("currentTime", video.currentTime);
|
|
222
|
-
api.emit("playback:timeupdate", { currentTime: video.currentTime });
|
|
223
|
-
});
|
|
224
|
-
addHandler("durationchange", () => {
|
|
225
|
-
api.setState("duration", video.duration || 0);
|
|
226
|
-
api.emit("media:loadedmetadata", { duration: video.duration || 0 });
|
|
227
|
-
});
|
|
228
|
-
addHandler("waiting", () => {
|
|
229
|
-
api.setState("waiting", true);
|
|
230
|
-
api.setState("buffering", true);
|
|
231
|
-
api.emit("media:waiting", void 0);
|
|
232
|
-
});
|
|
233
|
-
addHandler("canplay", () => {
|
|
234
|
-
api.setState("waiting", false);
|
|
235
|
-
api.setState("playbackState", "ready");
|
|
236
|
-
api.emit("media:canplay", void 0);
|
|
237
|
-
});
|
|
238
|
-
addHandler("canplaythrough", () => {
|
|
239
|
-
api.setState("buffering", false);
|
|
240
|
-
api.emit("media:canplaythrough", void 0);
|
|
241
|
-
});
|
|
242
|
-
addHandler("progress", () => {
|
|
243
|
-
if (video.buffered.length > 0) {
|
|
244
|
-
const bufferedEnd = video.buffered.end(video.buffered.length - 1);
|
|
245
|
-
const bufferedAmount = video.duration > 0 ? bufferedEnd / video.duration : 0;
|
|
246
|
-
api.setState("bufferedAmount", bufferedAmount);
|
|
247
|
-
api.setState("buffered", video.buffered);
|
|
248
|
-
api.emit("media:progress", { buffered: bufferedAmount });
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
addHandler("seeking", () => {
|
|
252
|
-
api.setState("seeking", true);
|
|
253
|
-
});
|
|
254
|
-
addHandler("seeked", () => {
|
|
255
|
-
api.setState("seeking", false);
|
|
256
|
-
api.emit("playback:seeked", { time: video.currentTime });
|
|
257
|
-
});
|
|
258
|
-
addHandler("volumechange", () => {
|
|
259
|
-
api.setState("volume", video.volume);
|
|
260
|
-
api.setState("muted", video.muted);
|
|
261
|
-
api.emit("volume:change", { volume: video.volume, muted: video.muted });
|
|
262
|
-
});
|
|
263
|
-
addHandler("ratechange", () => {
|
|
264
|
-
api.setState("playbackRate", video.playbackRate);
|
|
265
|
-
api.emit("playback:ratechange", { rate: video.playbackRate });
|
|
266
|
-
});
|
|
267
|
-
addHandler("loadedmetadata", () => {
|
|
268
|
-
api.setState("duration", video.duration);
|
|
269
|
-
api.setState("mediaType", video.videoWidth > 0 ? "video" : "audio");
|
|
270
|
-
});
|
|
271
|
-
addHandler("error", () => {
|
|
272
|
-
const error = video.error;
|
|
273
|
-
if (error) {
|
|
274
|
-
api.logger.error("Video element error", { code: error.code, message: error.message });
|
|
275
|
-
api.emit("media:error", { error: new Error(error.message || "Video playback error") });
|
|
276
|
-
}
|
|
277
|
-
});
|
|
278
|
-
addHandler("enterpictureinpicture", () => {
|
|
279
|
-
api.setState("pip", true);
|
|
280
|
-
api.logger.debug("PiP: entered (standard)");
|
|
281
|
-
});
|
|
282
|
-
addHandler("leavepictureinpicture", () => {
|
|
283
|
-
api.setState("pip", false);
|
|
284
|
-
api.logger.debug("PiP: exited (standard)");
|
|
285
|
-
if (!video.paused || api.getState("playing")) {
|
|
286
|
-
video.play().catch(() => {
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
const webkitVideo = video;
|
|
291
|
-
if ("webkitPresentationMode" in video) {
|
|
292
|
-
addHandler("webkitpresentationmodechanged", () => {
|
|
293
|
-
const mode = webkitVideo.webkitPresentationMode;
|
|
294
|
-
const isInPip = mode === "picture-in-picture";
|
|
295
|
-
api.setState("pip", isInPip);
|
|
296
|
-
api.logger.debug(`PiP: mode changed to ${mode} (webkit)`);
|
|
297
|
-
if (mode === "inline" && video.paused) {
|
|
298
|
-
video.play().catch(() => {
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
return () => {
|
|
304
|
-
for (const { event, handler } of handlers) {
|
|
305
|
-
video.removeEventListener(event, handler);
|
|
306
|
-
}
|
|
307
|
-
handlers.length = 0;
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
59
|
// src/index.ts
|
|
312
60
|
var DEFAULT_CONFIG = {
|
|
313
61
|
debug: false,
|
|
@@ -353,6 +101,10 @@ function createHLSPlugin(config) {
|
|
|
353
101
|
video.preload = "metadata";
|
|
354
102
|
video.controls = false;
|
|
355
103
|
video.playsInline = true;
|
|
104
|
+
const poster = api?.getState("poster");
|
|
105
|
+
if (poster) {
|
|
106
|
+
video.poster = poster;
|
|
107
|
+
}
|
|
356
108
|
api?.container.appendChild(video);
|
|
357
109
|
return video;
|
|
358
110
|
};
|