@scarlett-player/embed 0.5.3 → 1.0.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/dist/embed.audio.js +61 -5
- 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 +428 -6
- 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 +408 -3
- 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 +10 -8
package/dist/embed.js
CHANGED
|
@@ -43,6 +43,18 @@ function mapLevels(levels, _currentLevel) {
|
|
|
43
43
|
codec: level.codecSet
|
|
44
44
|
}));
|
|
45
45
|
}
|
|
46
|
+
function getInitialBandwidthEstimate(overrideBps) {
|
|
47
|
+
const HLS_DEFAULT_ESTIMATE = 5e5;
|
|
48
|
+
if (overrideBps !== void 0 && overrideBps > 0) {
|
|
49
|
+
return overrideBps;
|
|
50
|
+
}
|
|
51
|
+
const connection = navigator.connection;
|
|
52
|
+
if (connection?.downlink && connection.downlink > 0) {
|
|
53
|
+
const bps = connection.downlink * 1e6;
|
|
54
|
+
return Math.round(bps * 0.85);
|
|
55
|
+
}
|
|
56
|
+
return HLS_DEFAULT_ESTIMATE;
|
|
57
|
+
}
|
|
46
58
|
var HLS_ERROR_TYPES = {
|
|
47
59
|
NETWORK_ERROR: "networkError",
|
|
48
60
|
MEDIA_ERROR: "mediaError",
|
|
@@ -113,6 +125,14 @@ function setupHlsEventHandlers(hls, api, callbacks) {
|
|
|
113
125
|
});
|
|
114
126
|
callbacks.onLevelSwitched?.(data.level);
|
|
115
127
|
});
|
|
128
|
+
let lastBandwidthUpdate = 0;
|
|
129
|
+
addHandler("hlsFragLoaded", () => {
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
if (now - lastBandwidthUpdate >= 2e3 && hls.bandwidthEstimate) {
|
|
132
|
+
lastBandwidthUpdate = now;
|
|
133
|
+
api.setState("bandwidth", Math.round(hls.bandwidthEstimate));
|
|
134
|
+
}
|
|
135
|
+
});
|
|
116
136
|
addHandler("hlsFragBuffered", () => {
|
|
117
137
|
api.setState("buffering", false);
|
|
118
138
|
callbacks.onBufferUpdate?.();
|
|
@@ -355,6 +375,7 @@ var DEFAULT_CONFIG$4 = {
|
|
|
355
375
|
maxMaxBufferLength: 600,
|
|
356
376
|
backBufferLength: 30,
|
|
357
377
|
enableWorker: true,
|
|
378
|
+
capLevelToPlayerSize: true,
|
|
358
379
|
// Error recovery settings
|
|
359
380
|
maxNetworkRetries: 3,
|
|
360
381
|
maxMediaRetries: 2,
|
|
@@ -424,11 +445,13 @@ function createHLSPlugin(config) {
|
|
|
424
445
|
startPosition: mergedConfig.startPosition,
|
|
425
446
|
startLevel: -1,
|
|
426
447
|
// Auto quality selection (ABR)
|
|
448
|
+
abrEwmaDefaultEstimate: getInitialBandwidthEstimate(mergedConfig.initialBandwidthEstimate),
|
|
427
449
|
lowLatencyMode: mergedConfig.lowLatencyMode,
|
|
428
450
|
maxBufferLength: mergedConfig.maxBufferLength,
|
|
429
451
|
maxMaxBufferLength: mergedConfig.maxMaxBufferLength,
|
|
430
452
|
backBufferLength: mergedConfig.backBufferLength,
|
|
431
453
|
enableWorker: mergedConfig.enableWorker,
|
|
454
|
+
capLevelToPlayerSize: mergedConfig.capLevelToPlayerSize,
|
|
432
455
|
// Minimize hls.js internal retries - we handle retries ourselves
|
|
433
456
|
fragLoadingMaxRetry: 1,
|
|
434
457
|
manifestLoadingMaxRetry: 1,
|
|
@@ -699,7 +722,10 @@ function createHLSPlugin(config) {
|
|
|
699
722
|
currentSrc = src;
|
|
700
723
|
api.setState("playbackState", "loading");
|
|
701
724
|
api.setState("buffering", true);
|
|
702
|
-
if (
|
|
725
|
+
if (api.getState("airplayActive") && supportsNativeHLS()) {
|
|
726
|
+
api.logger.info("Using native HLS (AirPlay active)");
|
|
727
|
+
await loadNative(src);
|
|
728
|
+
} else if (isHlsJsSupported()) {
|
|
703
729
|
api.logger.info("Using hls.js for HLS playback");
|
|
704
730
|
await loadWithHlsJs(src);
|
|
705
731
|
} else if (supportsNativeHLS()) {
|
|
@@ -3241,6 +3267,37 @@ var CaptionsButton = class {
|
|
|
3241
3267
|
this.el.remove();
|
|
3242
3268
|
}
|
|
3243
3269
|
};
|
|
3270
|
+
var ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.55a11 11 0 0 1 14.08 0"/><path d="M1.42 9a16 16 0 0 1 21.16 0"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/><line x1="2" y1="2" x2="22" y2="22" stroke="currentColor" stroke-width="2"/></svg>`;
|
|
3271
|
+
var BandwidthIndicator = class {
|
|
3272
|
+
constructor(api) {
|
|
3273
|
+
this.api = api;
|
|
3274
|
+
this.el = createElement("div", { className: "sp-bandwidth-indicator" });
|
|
3275
|
+
this.el.innerHTML = ICON_SVG;
|
|
3276
|
+
this.el.setAttribute("aria-label", "Bandwidth is limiting video quality");
|
|
3277
|
+
this.el.setAttribute("title", "Bandwidth is limiting video quality");
|
|
3278
|
+
this.el.style.display = "none";
|
|
3279
|
+
}
|
|
3280
|
+
render() {
|
|
3281
|
+
return this.el;
|
|
3282
|
+
}
|
|
3283
|
+
update() {
|
|
3284
|
+
const bandwidth = this.api.getState("bandwidth");
|
|
3285
|
+
const qualities = this.api.getState("qualities");
|
|
3286
|
+
if (!bandwidth || !qualities || qualities.length === 0) {
|
|
3287
|
+
this.el.style.display = "none";
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3290
|
+
const highestBitrate = Math.max(...qualities.map((q) => q.bitrate));
|
|
3291
|
+
if (highestBitrate > 0 && bandwidth < highestBitrate) {
|
|
3292
|
+
this.el.style.display = "";
|
|
3293
|
+
} else {
|
|
3294
|
+
this.el.style.display = "none";
|
|
3295
|
+
}
|
|
3296
|
+
}
|
|
3297
|
+
destroy() {
|
|
3298
|
+
this.el.remove();
|
|
3299
|
+
}
|
|
3300
|
+
};
|
|
3244
3301
|
var DEFAULT_LAYOUT = [
|
|
3245
3302
|
"play",
|
|
3246
3303
|
"skip-backward",
|
|
@@ -3248,6 +3305,7 @@ var DEFAULT_LAYOUT = [
|
|
|
3248
3305
|
"volume",
|
|
3249
3306
|
"time",
|
|
3250
3307
|
"live-indicator",
|
|
3308
|
+
"bandwidth-indicator",
|
|
3251
3309
|
"spacer",
|
|
3252
3310
|
"settings",
|
|
3253
3311
|
"captions",
|
|
@@ -3288,6 +3346,8 @@ function uiPlugin(config = {}) {
|
|
|
3288
3346
|
return new TimeDisplay(api);
|
|
3289
3347
|
case "live-indicator":
|
|
3290
3348
|
return new LiveIndicator(api);
|
|
3349
|
+
case "bandwidth-indicator":
|
|
3350
|
+
return new BandwidthIndicator(api);
|
|
3291
3351
|
case "quality":
|
|
3292
3352
|
return new QualityMenu(api);
|
|
3293
3353
|
case "settings":
|
|
@@ -4875,7 +4935,9 @@ var DEFAULT_CONFIG$1 = {
|
|
|
4875
4935
|
persist: false,
|
|
4876
4936
|
persistKey: "scarlett-playlist",
|
|
4877
4937
|
shuffle: false,
|
|
4878
|
-
repeat: "none"
|
|
4938
|
+
repeat: "none",
|
|
4939
|
+
autoLoad: true,
|
|
4940
|
+
advanceDelay: 0
|
|
4879
4941
|
};
|
|
4880
4942
|
function generateId() {
|
|
4881
4943
|
return `track-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -5019,6 +5081,9 @@ function createPlaylistPlugin(config) {
|
|
|
5019
5081
|
}
|
|
5020
5082
|
api?.setState("mediaType", track.type || "audio");
|
|
5021
5083
|
emitChange();
|
|
5084
|
+
if (mergedConfig.autoLoad !== false && track.src) {
|
|
5085
|
+
api?.emit("media:load-request", { src: track.src, autoplay: true });
|
|
5086
|
+
}
|
|
5022
5087
|
};
|
|
5023
5088
|
const plugin = {
|
|
5024
5089
|
id: "playlist",
|
|
@@ -5033,12 +5098,20 @@ function createPlaylistPlugin(config) {
|
|
|
5033
5098
|
if (shuffle && tracks.length > 0) {
|
|
5034
5099
|
generateShuffleOrder();
|
|
5035
5100
|
}
|
|
5101
|
+
let advanceTimeout = null;
|
|
5036
5102
|
const unsubEnded = api.on("playback:ended", () => {
|
|
5037
5103
|
if (!mergedConfig.autoAdvance) return;
|
|
5038
5104
|
const nextIdx = getNextIndex();
|
|
5039
5105
|
if (nextIdx >= 0) {
|
|
5040
|
-
|
|
5041
|
-
|
|
5106
|
+
const advance = () => {
|
|
5107
|
+
api?.logger.debug("Auto-advancing to next track", { nextIdx });
|
|
5108
|
+
setCurrentTrack(nextIdx);
|
|
5109
|
+
};
|
|
5110
|
+
if (mergedConfig.advanceDelay) {
|
|
5111
|
+
advanceTimeout = setTimeout(advance, mergedConfig.advanceDelay);
|
|
5112
|
+
} else {
|
|
5113
|
+
advance();
|
|
5114
|
+
}
|
|
5042
5115
|
} else {
|
|
5043
5116
|
api?.logger.info("Playlist ended");
|
|
5044
5117
|
api?.emit("playlist:ended", void 0);
|
|
@@ -5046,6 +5119,10 @@ function createPlaylistPlugin(config) {
|
|
|
5046
5119
|
});
|
|
5047
5120
|
api.onDestroy(() => {
|
|
5048
5121
|
unsubEnded();
|
|
5122
|
+
if (advanceTimeout) {
|
|
5123
|
+
clearTimeout(advanceTimeout);
|
|
5124
|
+
advanceTimeout = null;
|
|
5125
|
+
}
|
|
5049
5126
|
persistPlaylist();
|
|
5050
5127
|
});
|
|
5051
5128
|
},
|
|
@@ -5491,6 +5568,336 @@ function createMediaSessionPlugin(config) {
|
|
|
5491
5568
|
};
|
|
5492
5569
|
return plugin;
|
|
5493
5570
|
}
|
|
5571
|
+
var POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right", "center"];
|
|
5572
|
+
var POSITION_STYLES = {
|
|
5573
|
+
"top-left": "top:10px;left:10px;",
|
|
5574
|
+
"top-right": "top:10px;right:10px;",
|
|
5575
|
+
"bottom-left": "bottom:40px;left:10px;",
|
|
5576
|
+
"bottom-right": "bottom:40px;right:10px;",
|
|
5577
|
+
"center": "top:50%;left:50%;transform:translate(-50%,-50%);"
|
|
5578
|
+
};
|
|
5579
|
+
function createWatermarkPlugin(config = {}) {
|
|
5580
|
+
let api = null;
|
|
5581
|
+
let element = null;
|
|
5582
|
+
let dynamicTimer = null;
|
|
5583
|
+
let showDelayTimer = null;
|
|
5584
|
+
let currentPosition = config.position || "bottom-right";
|
|
5585
|
+
const opacity = config.opacity ?? 0.5;
|
|
5586
|
+
const fontSize = config.fontSize ?? 14;
|
|
5587
|
+
const dynamic = config.dynamic ?? false;
|
|
5588
|
+
const dynamicInterval = config.dynamicInterval ?? 1e4;
|
|
5589
|
+
const showDelay = config.showDelay ?? 0;
|
|
5590
|
+
const createElement2 = () => {
|
|
5591
|
+
const el = document.createElement("div");
|
|
5592
|
+
el.className = "sp-watermark sp-watermark--hidden";
|
|
5593
|
+
el.style.cssText = `position:absolute;z-index:10;pointer-events:none;opacity:${opacity};font-size:${fontSize}px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,0.6);font-family:sans-serif;transition:all 0.5s ease;${POSITION_STYLES[currentPosition]}`;
|
|
5594
|
+
el.setAttribute("data-position", currentPosition);
|
|
5595
|
+
updateContent(el);
|
|
5596
|
+
return el;
|
|
5597
|
+
};
|
|
5598
|
+
const updateContent = (el, imageUrl, text) => {
|
|
5599
|
+
const img = imageUrl || config.imageUrl;
|
|
5600
|
+
const txt = text || config.text;
|
|
5601
|
+
el.innerHTML = "";
|
|
5602
|
+
if (img) {
|
|
5603
|
+
const imgEl = document.createElement("img");
|
|
5604
|
+
imgEl.src = img;
|
|
5605
|
+
imgEl.style.cssText = `max-height:${fontSize * 2}px;opacity:inherit;display:block;`;
|
|
5606
|
+
imgEl.alt = "";
|
|
5607
|
+
el.appendChild(imgEl);
|
|
5608
|
+
} else if (txt) {
|
|
5609
|
+
el.textContent = txt;
|
|
5610
|
+
}
|
|
5611
|
+
};
|
|
5612
|
+
const setPosition = (position) => {
|
|
5613
|
+
if (!element) return;
|
|
5614
|
+
currentPosition = position;
|
|
5615
|
+
element.style.top = "";
|
|
5616
|
+
element.style.right = "";
|
|
5617
|
+
element.style.bottom = "";
|
|
5618
|
+
element.style.left = "";
|
|
5619
|
+
element.style.transform = "";
|
|
5620
|
+
const styles2 = POSITION_STYLES[position];
|
|
5621
|
+
styles2.split(";").filter(Boolean).forEach((rule) => {
|
|
5622
|
+
const colonIdx = rule.indexOf(":");
|
|
5623
|
+
if (colonIdx === -1) return;
|
|
5624
|
+
const prop = rule.slice(0, colonIdx).trim();
|
|
5625
|
+
const val = rule.slice(colonIdx + 1).trim();
|
|
5626
|
+
if (prop && val) {
|
|
5627
|
+
element.style.setProperty(prop, val);
|
|
5628
|
+
}
|
|
5629
|
+
});
|
|
5630
|
+
element.setAttribute("data-position", position);
|
|
5631
|
+
const isVisible = element.classList.contains("sp-watermark--visible");
|
|
5632
|
+
const visClass = isVisible ? " sp-watermark--visible" : " sp-watermark--hidden";
|
|
5633
|
+
element.className = `sp-watermark sp-watermark--${position}${visClass}${dynamic ? " sp-watermark--dynamic" : ""}`;
|
|
5634
|
+
};
|
|
5635
|
+
const randomizePosition = () => {
|
|
5636
|
+
const available = POSITIONS.filter((p) => p !== currentPosition);
|
|
5637
|
+
const next = available[Math.floor(Math.random() * available.length)];
|
|
5638
|
+
setPosition(next);
|
|
5639
|
+
};
|
|
5640
|
+
const show = () => {
|
|
5641
|
+
if (!element) return;
|
|
5642
|
+
element.classList.remove("sp-watermark--hidden");
|
|
5643
|
+
element.classList.add("sp-watermark--visible");
|
|
5644
|
+
};
|
|
5645
|
+
const hide = () => {
|
|
5646
|
+
if (!element) return;
|
|
5647
|
+
element.classList.remove("sp-watermark--visible");
|
|
5648
|
+
element.classList.add("sp-watermark--hidden");
|
|
5649
|
+
};
|
|
5650
|
+
const startDynamic = () => {
|
|
5651
|
+
if (!dynamic || dynamicTimer) return;
|
|
5652
|
+
dynamicTimer = setInterval(randomizePosition, dynamicInterval);
|
|
5653
|
+
};
|
|
5654
|
+
const stopDynamic = () => {
|
|
5655
|
+
if (dynamicTimer) {
|
|
5656
|
+
clearInterval(dynamicTimer);
|
|
5657
|
+
dynamicTimer = null;
|
|
5658
|
+
}
|
|
5659
|
+
};
|
|
5660
|
+
const cleanup = () => {
|
|
5661
|
+
stopDynamic();
|
|
5662
|
+
if (showDelayTimer) {
|
|
5663
|
+
clearTimeout(showDelayTimer);
|
|
5664
|
+
showDelayTimer = null;
|
|
5665
|
+
}
|
|
5666
|
+
if (element?.parentNode) {
|
|
5667
|
+
element.parentNode.removeChild(element);
|
|
5668
|
+
}
|
|
5669
|
+
element = null;
|
|
5670
|
+
};
|
|
5671
|
+
return {
|
|
5672
|
+
id: "watermark",
|
|
5673
|
+
name: "Watermark",
|
|
5674
|
+
version: "1.0.0",
|
|
5675
|
+
type: "feature",
|
|
5676
|
+
description: "Anti-piracy watermark overlay with text/image support and dynamic repositioning",
|
|
5677
|
+
init(pluginApi) {
|
|
5678
|
+
api = pluginApi;
|
|
5679
|
+
api.logger.debug("Watermark plugin initialized");
|
|
5680
|
+
element = createElement2();
|
|
5681
|
+
api.container.appendChild(element);
|
|
5682
|
+
const unsubPlay = api.on("playback:play", () => {
|
|
5683
|
+
if (showDelay > 0) {
|
|
5684
|
+
showDelayTimer = setTimeout(() => {
|
|
5685
|
+
show();
|
|
5686
|
+
startDynamic();
|
|
5687
|
+
}, showDelay);
|
|
5688
|
+
} else {
|
|
5689
|
+
show();
|
|
5690
|
+
startDynamic();
|
|
5691
|
+
}
|
|
5692
|
+
});
|
|
5693
|
+
const unsubPause = api.on("playback:pause", () => {
|
|
5694
|
+
hide();
|
|
5695
|
+
stopDynamic();
|
|
5696
|
+
if (showDelayTimer) {
|
|
5697
|
+
clearTimeout(showDelayTimer);
|
|
5698
|
+
showDelayTimer = null;
|
|
5699
|
+
}
|
|
5700
|
+
});
|
|
5701
|
+
const unsubEnded = api.on("playback:ended", () => {
|
|
5702
|
+
hide();
|
|
5703
|
+
stopDynamic();
|
|
5704
|
+
});
|
|
5705
|
+
const unsubChange = api.on("playlist:change", ({ track }) => {
|
|
5706
|
+
if (!element || !track) return;
|
|
5707
|
+
const metadata = track.metadata;
|
|
5708
|
+
if (metadata) {
|
|
5709
|
+
const watermarkUrl = metadata.watermarkUrl;
|
|
5710
|
+
const watermarkText = metadata.watermarkText;
|
|
5711
|
+
if (watermarkUrl || watermarkText) {
|
|
5712
|
+
updateContent(element, watermarkUrl, watermarkText);
|
|
5713
|
+
}
|
|
5714
|
+
}
|
|
5715
|
+
});
|
|
5716
|
+
api.onDestroy(() => {
|
|
5717
|
+
unsubPlay();
|
|
5718
|
+
unsubPause();
|
|
5719
|
+
unsubEnded();
|
|
5720
|
+
unsubChange();
|
|
5721
|
+
cleanup();
|
|
5722
|
+
});
|
|
5723
|
+
},
|
|
5724
|
+
destroy() {
|
|
5725
|
+
api?.logger.debug("Watermark plugin destroyed");
|
|
5726
|
+
cleanup();
|
|
5727
|
+
api = null;
|
|
5728
|
+
},
|
|
5729
|
+
setText(text) {
|
|
5730
|
+
if (element) updateContent(element, void 0, text);
|
|
5731
|
+
},
|
|
5732
|
+
setImage(imageUrl) {
|
|
5733
|
+
if (element) updateContent(element, imageUrl);
|
|
5734
|
+
},
|
|
5735
|
+
setPosition,
|
|
5736
|
+
setOpacity(value) {
|
|
5737
|
+
if (element) element.style.opacity = String(Math.max(0, Math.min(1, value)));
|
|
5738
|
+
},
|
|
5739
|
+
show,
|
|
5740
|
+
hide,
|
|
5741
|
+
getConfig() {
|
|
5742
|
+
return { ...config, position: currentPosition, opacity: element ? parseFloat(element.style.opacity) || opacity : opacity };
|
|
5743
|
+
}
|
|
5744
|
+
};
|
|
5745
|
+
}
|
|
5746
|
+
function createCaptionsPlugin(config = {}) {
|
|
5747
|
+
let api = null;
|
|
5748
|
+
let video = null;
|
|
5749
|
+
let addedTrackElements = [];
|
|
5750
|
+
const extractFromHLS = config.extractFromHLS !== false;
|
|
5751
|
+
const autoSelect = config.autoSelect ?? false;
|
|
5752
|
+
const defaultLanguage = config.defaultLanguage ?? "en";
|
|
5753
|
+
const getVideo2 = () => {
|
|
5754
|
+
if (video) return video;
|
|
5755
|
+
video = api?.container.querySelector("video") ?? null;
|
|
5756
|
+
return video;
|
|
5757
|
+
};
|
|
5758
|
+
const cleanupTracks = () => {
|
|
5759
|
+
for (const trackEl of addedTrackElements) {
|
|
5760
|
+
trackEl.parentNode?.removeChild(trackEl);
|
|
5761
|
+
}
|
|
5762
|
+
addedTrackElements = [];
|
|
5763
|
+
api?.setState("textTracks", []);
|
|
5764
|
+
api?.setState("currentTextTrack", null);
|
|
5765
|
+
};
|
|
5766
|
+
const addTrackElement = (source) => {
|
|
5767
|
+
const videoEl = getVideo2();
|
|
5768
|
+
if (!videoEl) throw new Error("No video element");
|
|
5769
|
+
const trackEl = document.createElement("track");
|
|
5770
|
+
trackEl.kind = source.kind || "subtitles";
|
|
5771
|
+
trackEl.label = source.label;
|
|
5772
|
+
trackEl.srclang = source.language;
|
|
5773
|
+
trackEl.src = source.src;
|
|
5774
|
+
trackEl.default = false;
|
|
5775
|
+
videoEl.appendChild(trackEl);
|
|
5776
|
+
addedTrackElements.push(trackEl);
|
|
5777
|
+
if (trackEl.track) {
|
|
5778
|
+
trackEl.track.mode = "disabled";
|
|
5779
|
+
}
|
|
5780
|
+
return trackEl;
|
|
5781
|
+
};
|
|
5782
|
+
const syncTracksToState = () => {
|
|
5783
|
+
const videoEl = getVideo2();
|
|
5784
|
+
if (!videoEl) return;
|
|
5785
|
+
const tracks = [];
|
|
5786
|
+
let currentTrack = null;
|
|
5787
|
+
for (let i = 0; i < videoEl.textTracks.length; i++) {
|
|
5788
|
+
const track = videoEl.textTracks[i];
|
|
5789
|
+
if (track.kind !== "subtitles" && track.kind !== "captions") continue;
|
|
5790
|
+
const scarlettTrack = {
|
|
5791
|
+
id: `track-${i}`,
|
|
5792
|
+
label: track.label || `Track ${i + 1}`,
|
|
5793
|
+
language: track.language || "",
|
|
5794
|
+
kind: track.kind,
|
|
5795
|
+
active: track.mode === "showing"
|
|
5796
|
+
};
|
|
5797
|
+
tracks.push(scarlettTrack);
|
|
5798
|
+
if (track.mode === "showing") {
|
|
5799
|
+
currentTrack = scarlettTrack;
|
|
5800
|
+
}
|
|
5801
|
+
}
|
|
5802
|
+
api?.setState("textTracks", tracks);
|
|
5803
|
+
api?.setState("currentTextTrack", currentTrack);
|
|
5804
|
+
};
|
|
5805
|
+
const selectTrack = (trackId) => {
|
|
5806
|
+
const videoEl = getVideo2();
|
|
5807
|
+
if (!videoEl) return;
|
|
5808
|
+
for (let i = 0; i < videoEl.textTracks.length; i++) {
|
|
5809
|
+
const track = videoEl.textTracks[i];
|
|
5810
|
+
if (track.kind !== "subtitles" && track.kind !== "captions") continue;
|
|
5811
|
+
const id = `track-${i}`;
|
|
5812
|
+
if (trackId && id === trackId) {
|
|
5813
|
+
track.mode = "showing";
|
|
5814
|
+
} else {
|
|
5815
|
+
track.mode = "disabled";
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
syncTracksToState();
|
|
5819
|
+
};
|
|
5820
|
+
const extractHlsSubtitles = () => {
|
|
5821
|
+
if (!extractFromHLS || !api) return;
|
|
5822
|
+
const hlsPlugin = api.getPlugin("hls-provider");
|
|
5823
|
+
if (!hlsPlugin || hlsPlugin.isNativeHLS()) return;
|
|
5824
|
+
const hlsInstance = hlsPlugin.getHlsInstance();
|
|
5825
|
+
if (!hlsInstance?.subtitleTracks?.length) return;
|
|
5826
|
+
api.logger.debug("Extracting HLS subtitle tracks", {
|
|
5827
|
+
count: hlsInstance.subtitleTracks.length
|
|
5828
|
+
});
|
|
5829
|
+
for (const hlsTrack of hlsInstance.subtitleTracks) {
|
|
5830
|
+
addTrackElement({
|
|
5831
|
+
language: hlsTrack.lang || "unknown",
|
|
5832
|
+
label: hlsTrack.name || `Subtitle ${hlsTrack.id}`,
|
|
5833
|
+
src: hlsTrack.url,
|
|
5834
|
+
kind: "subtitles"
|
|
5835
|
+
});
|
|
5836
|
+
}
|
|
5837
|
+
syncTracksToState();
|
|
5838
|
+
if (autoSelect) {
|
|
5839
|
+
autoSelectTrack();
|
|
5840
|
+
}
|
|
5841
|
+
};
|
|
5842
|
+
const autoSelectTrack = () => {
|
|
5843
|
+
const tracks = api?.getState("textTracks") || [];
|
|
5844
|
+
const match = tracks.find((t) => t.language === defaultLanguage);
|
|
5845
|
+
if (match) {
|
|
5846
|
+
selectTrack(match.id);
|
|
5847
|
+
api?.logger.debug("Auto-selected caption track", { language: defaultLanguage, id: match.id });
|
|
5848
|
+
}
|
|
5849
|
+
};
|
|
5850
|
+
const initSources = () => {
|
|
5851
|
+
if (!config.sources?.length) return;
|
|
5852
|
+
for (const source of config.sources) {
|
|
5853
|
+
addTrackElement(source);
|
|
5854
|
+
}
|
|
5855
|
+
syncTracksToState();
|
|
5856
|
+
if (autoSelect) {
|
|
5857
|
+
autoSelectTrack();
|
|
5858
|
+
}
|
|
5859
|
+
};
|
|
5860
|
+
return {
|
|
5861
|
+
id: "captions",
|
|
5862
|
+
name: "Captions",
|
|
5863
|
+
version: "1.0.0",
|
|
5864
|
+
type: "feature",
|
|
5865
|
+
description: "WebVTT subtitles and closed captions with HLS extraction",
|
|
5866
|
+
init(pluginApi) {
|
|
5867
|
+
api = pluginApi;
|
|
5868
|
+
api.logger.debug("Captions plugin initialized");
|
|
5869
|
+
api.setState("textTracks", []);
|
|
5870
|
+
api.setState("currentTextTrack", null);
|
|
5871
|
+
const unsubTrackText = api.on("track:text", ({ trackId }) => {
|
|
5872
|
+
selectTrack(trackId);
|
|
5873
|
+
});
|
|
5874
|
+
const unsubLoaded = api.on("media:loaded", () => {
|
|
5875
|
+
video = null;
|
|
5876
|
+
cleanupTracks();
|
|
5877
|
+
initSources();
|
|
5878
|
+
if (extractFromHLS) {
|
|
5879
|
+
setTimeout(extractHlsSubtitles, 500);
|
|
5880
|
+
}
|
|
5881
|
+
});
|
|
5882
|
+
const unsubLoadRequest = api.on("media:load-request", () => {
|
|
5883
|
+
video = null;
|
|
5884
|
+
cleanupTracks();
|
|
5885
|
+
});
|
|
5886
|
+
api.onDestroy(() => {
|
|
5887
|
+
unsubTrackText();
|
|
5888
|
+
unsubLoaded();
|
|
5889
|
+
unsubLoadRequest();
|
|
5890
|
+
cleanupTracks();
|
|
5891
|
+
});
|
|
5892
|
+
},
|
|
5893
|
+
destroy() {
|
|
5894
|
+
api?.logger.debug("Captions plugin destroyed");
|
|
5895
|
+
cleanupTracks();
|
|
5896
|
+
video = null;
|
|
5897
|
+
api = null;
|
|
5898
|
+
}
|
|
5899
|
+
};
|
|
5900
|
+
}
|
|
5494
5901
|
class Signal {
|
|
5495
5902
|
constructor(initialValue) {
|
|
5496
5903
|
this.subscribers = /* @__PURE__ */ new Set();
|
|
@@ -7030,6 +7437,13 @@ class ScarlettPlayer {
|
|
|
7030
7437
|
await this.pluginManager.initPlugin(id);
|
|
7031
7438
|
}
|
|
7032
7439
|
}
|
|
7440
|
+
this.eventBus.on("media:load-request", async ({ src, autoplay }) => {
|
|
7441
|
+
if (this.stateManager.getValue("chromecastActive")) return;
|
|
7442
|
+
await this.load(src);
|
|
7443
|
+
if (autoplay !== false) {
|
|
7444
|
+
await this.play();
|
|
7445
|
+
}
|
|
7446
|
+
});
|
|
7033
7447
|
if (this.initialSrc) {
|
|
7034
7448
|
await this.load(this.initialSrc);
|
|
7035
7449
|
}
|
|
@@ -7857,6 +8271,12 @@ async function createEmbedPlayer(container, config, pluginCreators2, availableTy
|
|
|
7857
8271
|
artwork: config.artwork || config.poster || config.playlist?.[0]?.artwork
|
|
7858
8272
|
}));
|
|
7859
8273
|
}
|
|
8274
|
+
if (pluginCreators2.watermark && config.watermark) {
|
|
8275
|
+
plugins.push(pluginCreators2.watermark(config.watermark));
|
|
8276
|
+
}
|
|
8277
|
+
if (pluginCreators2.captions) {
|
|
8278
|
+
plugins.push(pluginCreators2.captions(config.captions || {}));
|
|
8279
|
+
}
|
|
7860
8280
|
if (pluginCreators2.analytics && config.analytics?.beaconUrl) {
|
|
7861
8281
|
plugins.push(pluginCreators2.analytics({
|
|
7862
8282
|
beaconUrl: config.analytics.beaconUrl,
|
|
@@ -7974,7 +8394,7 @@ function setupAutoInit(pluginCreators2, availableTypes) {
|
|
|
7974
8394
|
}
|
|
7975
8395
|
}
|
|
7976
8396
|
}
|
|
7977
|
-
const VERSION = "0.3
|
|
8397
|
+
const VERSION = "0.5.3";
|
|
7978
8398
|
const AVAILABLE_TYPES = ["video", "audio", "audio-mini"];
|
|
7979
8399
|
const pluginCreators = {
|
|
7980
8400
|
hls: createHLSPlugin,
|
|
@@ -7982,7 +8402,9 @@ const pluginCreators = {
|
|
|
7982
8402
|
audioUI: createAudioUIPlugin,
|
|
7983
8403
|
analytics: createAnalyticsPlugin,
|
|
7984
8404
|
playlist: createPlaylistPlugin,
|
|
7985
|
-
mediaSession: createMediaSessionPlugin
|
|
8405
|
+
mediaSession: createMediaSessionPlugin,
|
|
8406
|
+
watermark: createWatermarkPlugin,
|
|
8407
|
+
captions: createCaptionsPlugin
|
|
7986
8408
|
};
|
|
7987
8409
|
const ScarlettPlayerAPI = createScarlettPlayerAPI(
|
|
7988
8410
|
pluginCreators,
|