@scarlett-player/embed 0.5.3 → 1.0.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/dist/embed.audio.js +65 -6
- 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 +432 -7
- 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.video.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 = {
|
|
|
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":
|
|
@@ -3556,6 +3616,336 @@ function uiPlugin(config = {}) {
|
|
|
3556
3616
|
}
|
|
3557
3617
|
};
|
|
3558
3618
|
}
|
|
3619
|
+
var POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right", "center"];
|
|
3620
|
+
var POSITION_STYLES = {
|
|
3621
|
+
"top-left": "top:10px;left:10px;",
|
|
3622
|
+
"top-right": "top:10px;right:10px;",
|
|
3623
|
+
"bottom-left": "bottom:40px;left:10px;",
|
|
3624
|
+
"bottom-right": "bottom:40px;right:10px;",
|
|
3625
|
+
"center": "top:50%;left:50%;transform:translate(-50%,-50%);"
|
|
3626
|
+
};
|
|
3627
|
+
function createWatermarkPlugin(config = {}) {
|
|
3628
|
+
let api = null;
|
|
3629
|
+
let element = null;
|
|
3630
|
+
let dynamicTimer = null;
|
|
3631
|
+
let showDelayTimer = null;
|
|
3632
|
+
let currentPosition = config.position || "bottom-right";
|
|
3633
|
+
const opacity = config.opacity ?? 0.5;
|
|
3634
|
+
const fontSize = config.fontSize ?? 14;
|
|
3635
|
+
const dynamic = config.dynamic ?? false;
|
|
3636
|
+
const dynamicInterval = config.dynamicInterval ?? 1e4;
|
|
3637
|
+
const showDelay = config.showDelay ?? 0;
|
|
3638
|
+
const createElement2 = () => {
|
|
3639
|
+
const el = document.createElement("div");
|
|
3640
|
+
el.className = "sp-watermark sp-watermark--hidden";
|
|
3641
|
+
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]}`;
|
|
3642
|
+
el.setAttribute("data-position", currentPosition);
|
|
3643
|
+
updateContent(el);
|
|
3644
|
+
return el;
|
|
3645
|
+
};
|
|
3646
|
+
const updateContent = (el, imageUrl, text) => {
|
|
3647
|
+
const img = imageUrl || config.imageUrl;
|
|
3648
|
+
const txt = text || config.text;
|
|
3649
|
+
el.innerHTML = "";
|
|
3650
|
+
if (img) {
|
|
3651
|
+
const imgEl = document.createElement("img");
|
|
3652
|
+
imgEl.src = img;
|
|
3653
|
+
imgEl.style.cssText = `max-height:${fontSize * 2}px;opacity:inherit;display:block;`;
|
|
3654
|
+
imgEl.alt = "";
|
|
3655
|
+
el.appendChild(imgEl);
|
|
3656
|
+
} else if (txt) {
|
|
3657
|
+
el.textContent = txt;
|
|
3658
|
+
}
|
|
3659
|
+
};
|
|
3660
|
+
const setPosition = (position) => {
|
|
3661
|
+
if (!element) return;
|
|
3662
|
+
currentPosition = position;
|
|
3663
|
+
element.style.top = "";
|
|
3664
|
+
element.style.right = "";
|
|
3665
|
+
element.style.bottom = "";
|
|
3666
|
+
element.style.left = "";
|
|
3667
|
+
element.style.transform = "";
|
|
3668
|
+
const styles2 = POSITION_STYLES[position];
|
|
3669
|
+
styles2.split(";").filter(Boolean).forEach((rule) => {
|
|
3670
|
+
const colonIdx = rule.indexOf(":");
|
|
3671
|
+
if (colonIdx === -1) return;
|
|
3672
|
+
const prop = rule.slice(0, colonIdx).trim();
|
|
3673
|
+
const val = rule.slice(colonIdx + 1).trim();
|
|
3674
|
+
if (prop && val) {
|
|
3675
|
+
element.style.setProperty(prop, val);
|
|
3676
|
+
}
|
|
3677
|
+
});
|
|
3678
|
+
element.setAttribute("data-position", position);
|
|
3679
|
+
const isVisible = element.classList.contains("sp-watermark--visible");
|
|
3680
|
+
const visClass = isVisible ? " sp-watermark--visible" : " sp-watermark--hidden";
|
|
3681
|
+
element.className = `sp-watermark sp-watermark--${position}${visClass}${dynamic ? " sp-watermark--dynamic" : ""}`;
|
|
3682
|
+
};
|
|
3683
|
+
const randomizePosition = () => {
|
|
3684
|
+
const available = POSITIONS.filter((p) => p !== currentPosition);
|
|
3685
|
+
const next = available[Math.floor(Math.random() * available.length)];
|
|
3686
|
+
setPosition(next);
|
|
3687
|
+
};
|
|
3688
|
+
const show = () => {
|
|
3689
|
+
if (!element) return;
|
|
3690
|
+
element.classList.remove("sp-watermark--hidden");
|
|
3691
|
+
element.classList.add("sp-watermark--visible");
|
|
3692
|
+
};
|
|
3693
|
+
const hide = () => {
|
|
3694
|
+
if (!element) return;
|
|
3695
|
+
element.classList.remove("sp-watermark--visible");
|
|
3696
|
+
element.classList.add("sp-watermark--hidden");
|
|
3697
|
+
};
|
|
3698
|
+
const startDynamic = () => {
|
|
3699
|
+
if (!dynamic || dynamicTimer) return;
|
|
3700
|
+
dynamicTimer = setInterval(randomizePosition, dynamicInterval);
|
|
3701
|
+
};
|
|
3702
|
+
const stopDynamic = () => {
|
|
3703
|
+
if (dynamicTimer) {
|
|
3704
|
+
clearInterval(dynamicTimer);
|
|
3705
|
+
dynamicTimer = null;
|
|
3706
|
+
}
|
|
3707
|
+
};
|
|
3708
|
+
const cleanup = () => {
|
|
3709
|
+
stopDynamic();
|
|
3710
|
+
if (showDelayTimer) {
|
|
3711
|
+
clearTimeout(showDelayTimer);
|
|
3712
|
+
showDelayTimer = null;
|
|
3713
|
+
}
|
|
3714
|
+
if (element?.parentNode) {
|
|
3715
|
+
element.parentNode.removeChild(element);
|
|
3716
|
+
}
|
|
3717
|
+
element = null;
|
|
3718
|
+
};
|
|
3719
|
+
return {
|
|
3720
|
+
id: "watermark",
|
|
3721
|
+
name: "Watermark",
|
|
3722
|
+
version: "1.0.0",
|
|
3723
|
+
type: "feature",
|
|
3724
|
+
description: "Anti-piracy watermark overlay with text/image support and dynamic repositioning",
|
|
3725
|
+
init(pluginApi) {
|
|
3726
|
+
api = pluginApi;
|
|
3727
|
+
api.logger.debug("Watermark plugin initialized");
|
|
3728
|
+
element = createElement2();
|
|
3729
|
+
api.container.appendChild(element);
|
|
3730
|
+
const unsubPlay = api.on("playback:play", () => {
|
|
3731
|
+
if (showDelay > 0) {
|
|
3732
|
+
showDelayTimer = setTimeout(() => {
|
|
3733
|
+
show();
|
|
3734
|
+
startDynamic();
|
|
3735
|
+
}, showDelay);
|
|
3736
|
+
} else {
|
|
3737
|
+
show();
|
|
3738
|
+
startDynamic();
|
|
3739
|
+
}
|
|
3740
|
+
});
|
|
3741
|
+
const unsubPause = api.on("playback:pause", () => {
|
|
3742
|
+
hide();
|
|
3743
|
+
stopDynamic();
|
|
3744
|
+
if (showDelayTimer) {
|
|
3745
|
+
clearTimeout(showDelayTimer);
|
|
3746
|
+
showDelayTimer = null;
|
|
3747
|
+
}
|
|
3748
|
+
});
|
|
3749
|
+
const unsubEnded = api.on("playback:ended", () => {
|
|
3750
|
+
hide();
|
|
3751
|
+
stopDynamic();
|
|
3752
|
+
});
|
|
3753
|
+
const unsubChange = api.on("playlist:change", ({ track }) => {
|
|
3754
|
+
if (!element || !track) return;
|
|
3755
|
+
const metadata = track.metadata;
|
|
3756
|
+
if (metadata) {
|
|
3757
|
+
const watermarkUrl = metadata.watermarkUrl;
|
|
3758
|
+
const watermarkText = metadata.watermarkText;
|
|
3759
|
+
if (watermarkUrl || watermarkText) {
|
|
3760
|
+
updateContent(element, watermarkUrl, watermarkText);
|
|
3761
|
+
}
|
|
3762
|
+
}
|
|
3763
|
+
});
|
|
3764
|
+
api.onDestroy(() => {
|
|
3765
|
+
unsubPlay();
|
|
3766
|
+
unsubPause();
|
|
3767
|
+
unsubEnded();
|
|
3768
|
+
unsubChange();
|
|
3769
|
+
cleanup();
|
|
3770
|
+
});
|
|
3771
|
+
},
|
|
3772
|
+
destroy() {
|
|
3773
|
+
api?.logger.debug("Watermark plugin destroyed");
|
|
3774
|
+
cleanup();
|
|
3775
|
+
api = null;
|
|
3776
|
+
},
|
|
3777
|
+
setText(text) {
|
|
3778
|
+
if (element) updateContent(element, void 0, text);
|
|
3779
|
+
},
|
|
3780
|
+
setImage(imageUrl) {
|
|
3781
|
+
if (element) updateContent(element, imageUrl);
|
|
3782
|
+
},
|
|
3783
|
+
setPosition,
|
|
3784
|
+
setOpacity(value) {
|
|
3785
|
+
if (element) element.style.opacity = String(Math.max(0, Math.min(1, value)));
|
|
3786
|
+
},
|
|
3787
|
+
show,
|
|
3788
|
+
hide,
|
|
3789
|
+
getConfig() {
|
|
3790
|
+
return { ...config, position: currentPosition, opacity: element ? parseFloat(element.style.opacity) || opacity : opacity };
|
|
3791
|
+
}
|
|
3792
|
+
};
|
|
3793
|
+
}
|
|
3794
|
+
function createCaptionsPlugin(config = {}) {
|
|
3795
|
+
let api = null;
|
|
3796
|
+
let video = null;
|
|
3797
|
+
let addedTrackElements = [];
|
|
3798
|
+
const extractFromHLS = config.extractFromHLS !== false;
|
|
3799
|
+
const autoSelect = config.autoSelect ?? false;
|
|
3800
|
+
const defaultLanguage = config.defaultLanguage ?? "en";
|
|
3801
|
+
const getVideo2 = () => {
|
|
3802
|
+
if (video) return video;
|
|
3803
|
+
video = api?.container.querySelector("video") ?? null;
|
|
3804
|
+
return video;
|
|
3805
|
+
};
|
|
3806
|
+
const cleanupTracks = () => {
|
|
3807
|
+
for (const trackEl of addedTrackElements) {
|
|
3808
|
+
trackEl.parentNode?.removeChild(trackEl);
|
|
3809
|
+
}
|
|
3810
|
+
addedTrackElements = [];
|
|
3811
|
+
api?.setState("textTracks", []);
|
|
3812
|
+
api?.setState("currentTextTrack", null);
|
|
3813
|
+
};
|
|
3814
|
+
const addTrackElement = (source) => {
|
|
3815
|
+
const videoEl = getVideo2();
|
|
3816
|
+
if (!videoEl) throw new Error("No video element");
|
|
3817
|
+
const trackEl = document.createElement("track");
|
|
3818
|
+
trackEl.kind = source.kind || "subtitles";
|
|
3819
|
+
trackEl.label = source.label;
|
|
3820
|
+
trackEl.srclang = source.language;
|
|
3821
|
+
trackEl.src = source.src;
|
|
3822
|
+
trackEl.default = false;
|
|
3823
|
+
videoEl.appendChild(trackEl);
|
|
3824
|
+
addedTrackElements.push(trackEl);
|
|
3825
|
+
if (trackEl.track) {
|
|
3826
|
+
trackEl.track.mode = "disabled";
|
|
3827
|
+
}
|
|
3828
|
+
return trackEl;
|
|
3829
|
+
};
|
|
3830
|
+
const syncTracksToState = () => {
|
|
3831
|
+
const videoEl = getVideo2();
|
|
3832
|
+
if (!videoEl) return;
|
|
3833
|
+
const tracks = [];
|
|
3834
|
+
let currentTrack = null;
|
|
3835
|
+
for (let i = 0; i < videoEl.textTracks.length; i++) {
|
|
3836
|
+
const track = videoEl.textTracks[i];
|
|
3837
|
+
if (track.kind !== "subtitles" && track.kind !== "captions") continue;
|
|
3838
|
+
const scarlettTrack = {
|
|
3839
|
+
id: `track-${i}`,
|
|
3840
|
+
label: track.label || `Track ${i + 1}`,
|
|
3841
|
+
language: track.language || "",
|
|
3842
|
+
kind: track.kind,
|
|
3843
|
+
active: track.mode === "showing"
|
|
3844
|
+
};
|
|
3845
|
+
tracks.push(scarlettTrack);
|
|
3846
|
+
if (track.mode === "showing") {
|
|
3847
|
+
currentTrack = scarlettTrack;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
api?.setState("textTracks", tracks);
|
|
3851
|
+
api?.setState("currentTextTrack", currentTrack);
|
|
3852
|
+
};
|
|
3853
|
+
const selectTrack = (trackId) => {
|
|
3854
|
+
const videoEl = getVideo2();
|
|
3855
|
+
if (!videoEl) return;
|
|
3856
|
+
for (let i = 0; i < videoEl.textTracks.length; i++) {
|
|
3857
|
+
const track = videoEl.textTracks[i];
|
|
3858
|
+
if (track.kind !== "subtitles" && track.kind !== "captions") continue;
|
|
3859
|
+
const id = `track-${i}`;
|
|
3860
|
+
if (trackId && id === trackId) {
|
|
3861
|
+
track.mode = "showing";
|
|
3862
|
+
} else {
|
|
3863
|
+
track.mode = "disabled";
|
|
3864
|
+
}
|
|
3865
|
+
}
|
|
3866
|
+
syncTracksToState();
|
|
3867
|
+
};
|
|
3868
|
+
const extractHlsSubtitles = () => {
|
|
3869
|
+
if (!extractFromHLS || !api) return;
|
|
3870
|
+
const hlsPlugin = api.getPlugin("hls-provider");
|
|
3871
|
+
if (!hlsPlugin || hlsPlugin.isNativeHLS()) return;
|
|
3872
|
+
const hlsInstance = hlsPlugin.getHlsInstance();
|
|
3873
|
+
if (!hlsInstance?.subtitleTracks?.length) return;
|
|
3874
|
+
api.logger.debug("Extracting HLS subtitle tracks", {
|
|
3875
|
+
count: hlsInstance.subtitleTracks.length
|
|
3876
|
+
});
|
|
3877
|
+
for (const hlsTrack of hlsInstance.subtitleTracks) {
|
|
3878
|
+
addTrackElement({
|
|
3879
|
+
language: hlsTrack.lang || "unknown",
|
|
3880
|
+
label: hlsTrack.name || `Subtitle ${hlsTrack.id}`,
|
|
3881
|
+
src: hlsTrack.url,
|
|
3882
|
+
kind: "subtitles"
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3885
|
+
syncTracksToState();
|
|
3886
|
+
if (autoSelect) {
|
|
3887
|
+
autoSelectTrack();
|
|
3888
|
+
}
|
|
3889
|
+
};
|
|
3890
|
+
const autoSelectTrack = () => {
|
|
3891
|
+
const tracks = api?.getState("textTracks") || [];
|
|
3892
|
+
const match = tracks.find((t) => t.language === defaultLanguage);
|
|
3893
|
+
if (match) {
|
|
3894
|
+
selectTrack(match.id);
|
|
3895
|
+
api?.logger.debug("Auto-selected caption track", { language: defaultLanguage, id: match.id });
|
|
3896
|
+
}
|
|
3897
|
+
};
|
|
3898
|
+
const initSources = () => {
|
|
3899
|
+
if (!config.sources?.length) return;
|
|
3900
|
+
for (const source of config.sources) {
|
|
3901
|
+
addTrackElement(source);
|
|
3902
|
+
}
|
|
3903
|
+
syncTracksToState();
|
|
3904
|
+
if (autoSelect) {
|
|
3905
|
+
autoSelectTrack();
|
|
3906
|
+
}
|
|
3907
|
+
};
|
|
3908
|
+
return {
|
|
3909
|
+
id: "captions",
|
|
3910
|
+
name: "Captions",
|
|
3911
|
+
version: "1.0.0",
|
|
3912
|
+
type: "feature",
|
|
3913
|
+
description: "WebVTT subtitles and closed captions with HLS extraction",
|
|
3914
|
+
init(pluginApi) {
|
|
3915
|
+
api = pluginApi;
|
|
3916
|
+
api.logger.debug("Captions plugin initialized");
|
|
3917
|
+
api.setState("textTracks", []);
|
|
3918
|
+
api.setState("currentTextTrack", null);
|
|
3919
|
+
const unsubTrackText = api.on("track:text", ({ trackId }) => {
|
|
3920
|
+
selectTrack(trackId);
|
|
3921
|
+
});
|
|
3922
|
+
const unsubLoaded = api.on("media:loaded", () => {
|
|
3923
|
+
video = null;
|
|
3924
|
+
cleanupTracks();
|
|
3925
|
+
initSources();
|
|
3926
|
+
if (extractFromHLS) {
|
|
3927
|
+
setTimeout(extractHlsSubtitles, 500);
|
|
3928
|
+
}
|
|
3929
|
+
});
|
|
3930
|
+
const unsubLoadRequest = api.on("media:load-request", () => {
|
|
3931
|
+
video = null;
|
|
3932
|
+
cleanupTracks();
|
|
3933
|
+
});
|
|
3934
|
+
api.onDestroy(() => {
|
|
3935
|
+
unsubTrackText();
|
|
3936
|
+
unsubLoaded();
|
|
3937
|
+
unsubLoadRequest();
|
|
3938
|
+
cleanupTracks();
|
|
3939
|
+
});
|
|
3940
|
+
},
|
|
3941
|
+
destroy() {
|
|
3942
|
+
api?.logger.debug("Captions plugin destroyed");
|
|
3943
|
+
cleanupTracks();
|
|
3944
|
+
video = null;
|
|
3945
|
+
api = null;
|
|
3946
|
+
}
|
|
3947
|
+
};
|
|
3948
|
+
}
|
|
3559
3949
|
class Signal {
|
|
3560
3950
|
constructor(initialValue) {
|
|
3561
3951
|
this.subscribers = /* @__PURE__ */ new Set();
|
|
@@ -5095,6 +5485,13 @@ class ScarlettPlayer {
|
|
|
5095
5485
|
await this.pluginManager.initPlugin(id);
|
|
5096
5486
|
}
|
|
5097
5487
|
}
|
|
5488
|
+
this.eventBus.on("media:load-request", async ({ src, autoplay }) => {
|
|
5489
|
+
if (this.stateManager.getValue("chromecastActive")) return;
|
|
5490
|
+
await this.load(src);
|
|
5491
|
+
if (autoplay !== false) {
|
|
5492
|
+
await this.play();
|
|
5493
|
+
}
|
|
5494
|
+
});
|
|
5098
5495
|
if (this.initialSrc) {
|
|
5099
5496
|
await this.load(this.initialSrc);
|
|
5100
5497
|
}
|
|
@@ -5922,6 +6319,12 @@ async function createEmbedPlayer(container, config, pluginCreators2, availableTy
|
|
|
5922
6319
|
artwork: config.artwork || config.poster || config.playlist?.[0]?.artwork
|
|
5923
6320
|
}));
|
|
5924
6321
|
}
|
|
6322
|
+
if (pluginCreators2.watermark && config.watermark) {
|
|
6323
|
+
plugins.push(pluginCreators2.watermark(config.watermark));
|
|
6324
|
+
}
|
|
6325
|
+
if (pluginCreators2.captions) {
|
|
6326
|
+
plugins.push(pluginCreators2.captions(config.captions || {}));
|
|
6327
|
+
}
|
|
5925
6328
|
if (pluginCreators2.analytics && config.analytics?.beaconUrl) {
|
|
5926
6329
|
plugins.push(pluginCreators2.analytics({
|
|
5927
6330
|
beaconUrl: config.analytics.beaconUrl,
|
|
@@ -6039,11 +6442,13 @@ function setupAutoInit(pluginCreators2, availableTypes) {
|
|
|
6039
6442
|
}
|
|
6040
6443
|
}
|
|
6041
6444
|
}
|
|
6042
|
-
const VERSION = "0.3
|
|
6445
|
+
const VERSION = "0.5.3-video";
|
|
6043
6446
|
const AVAILABLE_TYPES = ["video"];
|
|
6044
6447
|
const pluginCreators = {
|
|
6045
6448
|
hls: createHLSPlugin,
|
|
6046
|
-
videoUI: uiPlugin
|
|
6449
|
+
videoUI: uiPlugin,
|
|
6450
|
+
watermark: createWatermarkPlugin,
|
|
6451
|
+
captions: createCaptionsPlugin
|
|
6047
6452
|
// Audio UI not available in this build
|
|
6048
6453
|
// Analytics not available in this build
|
|
6049
6454
|
// Playlist not available in this build
|