@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.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 (isHlsJsSupported()) {
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)}`;
@@ -4892,7 +4954,10 @@ function createPlaylistPlugin(config) {
4892
4954
  const mergedConfig = { ...DEFAULT_CONFIG$1, ...config };
4893
4955
  let api = null;
4894
4956
  let tracks = mergedConfig.tracks || [];
4895
- let currentIndex = -1;
4957
+ let currentIndex = mergedConfig.initialIndex ?? -1;
4958
+ if (currentIndex < -1 || currentIndex >= tracks.length) {
4959
+ currentIndex = -1;
4960
+ }
4896
4961
  let shuffle = mergedConfig.shuffle || false;
4897
4962
  let repeat = mergedConfig.repeat || "none";
4898
4963
  let shuffleOrder = [];
@@ -5019,6 +5084,9 @@ function createPlaylistPlugin(config) {
5019
5084
  }
5020
5085
  api?.setState("mediaType", track.type || "audio");
5021
5086
  emitChange();
5087
+ if (mergedConfig.autoLoad !== false && track.src) {
5088
+ api?.emit("media:load-request", { src: track.src, autoplay: true });
5089
+ }
5022
5090
  };
5023
5091
  const plugin = {
5024
5092
  id: "playlist",
@@ -5033,12 +5101,20 @@ function createPlaylistPlugin(config) {
5033
5101
  if (shuffle && tracks.length > 0) {
5034
5102
  generateShuffleOrder();
5035
5103
  }
5104
+ let advanceTimeout = null;
5036
5105
  const unsubEnded = api.on("playback:ended", () => {
5037
5106
  if (!mergedConfig.autoAdvance) return;
5038
5107
  const nextIdx = getNextIndex();
5039
5108
  if (nextIdx >= 0) {
5040
- api?.logger.debug("Auto-advancing to next track", { nextIdx });
5041
- setCurrentTrack(nextIdx);
5109
+ const advance = () => {
5110
+ api?.logger.debug("Auto-advancing to next track", { nextIdx });
5111
+ setCurrentTrack(nextIdx);
5112
+ };
5113
+ if (mergedConfig.advanceDelay) {
5114
+ advanceTimeout = setTimeout(advance, mergedConfig.advanceDelay);
5115
+ } else {
5116
+ advance();
5117
+ }
5042
5118
  } else {
5043
5119
  api?.logger.info("Playlist ended");
5044
5120
  api?.emit("playlist:ended", void 0);
@@ -5046,6 +5122,10 @@ function createPlaylistPlugin(config) {
5046
5122
  });
5047
5123
  api.onDestroy(() => {
5048
5124
  unsubEnded();
5125
+ if (advanceTimeout) {
5126
+ clearTimeout(advanceTimeout);
5127
+ advanceTimeout = null;
5128
+ }
5049
5129
  persistPlaylist();
5050
5130
  });
5051
5131
  },
@@ -5491,6 +5571,336 @@ function createMediaSessionPlugin(config) {
5491
5571
  };
5492
5572
  return plugin;
5493
5573
  }
5574
+ var POSITIONS = ["top-left", "top-right", "bottom-left", "bottom-right", "center"];
5575
+ var POSITION_STYLES = {
5576
+ "top-left": "top:10px;left:10px;",
5577
+ "top-right": "top:10px;right:10px;",
5578
+ "bottom-left": "bottom:40px;left:10px;",
5579
+ "bottom-right": "bottom:40px;right:10px;",
5580
+ "center": "top:50%;left:50%;transform:translate(-50%,-50%);"
5581
+ };
5582
+ function createWatermarkPlugin(config = {}) {
5583
+ let api = null;
5584
+ let element = null;
5585
+ let dynamicTimer = null;
5586
+ let showDelayTimer = null;
5587
+ let currentPosition = config.position || "bottom-right";
5588
+ const opacity = config.opacity ?? 0.5;
5589
+ const fontSize = config.fontSize ?? 14;
5590
+ const dynamic = config.dynamic ?? false;
5591
+ const dynamicInterval = config.dynamicInterval ?? 1e4;
5592
+ const showDelay = config.showDelay ?? 0;
5593
+ const createElement2 = () => {
5594
+ const el = document.createElement("div");
5595
+ el.className = "sp-watermark sp-watermark--hidden";
5596
+ 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]}`;
5597
+ el.setAttribute("data-position", currentPosition);
5598
+ updateContent(el);
5599
+ return el;
5600
+ };
5601
+ const updateContent = (el, imageUrl, text) => {
5602
+ const img = imageUrl || config.imageUrl;
5603
+ const txt = text || config.text;
5604
+ el.innerHTML = "";
5605
+ if (img) {
5606
+ const imgEl = document.createElement("img");
5607
+ imgEl.src = img;
5608
+ imgEl.style.cssText = `max-height:${fontSize * 2}px;opacity:inherit;display:block;`;
5609
+ imgEl.alt = "";
5610
+ el.appendChild(imgEl);
5611
+ } else if (txt) {
5612
+ el.textContent = txt;
5613
+ }
5614
+ };
5615
+ const setPosition = (position) => {
5616
+ if (!element) return;
5617
+ currentPosition = position;
5618
+ element.style.top = "";
5619
+ element.style.right = "";
5620
+ element.style.bottom = "";
5621
+ element.style.left = "";
5622
+ element.style.transform = "";
5623
+ const styles2 = POSITION_STYLES[position];
5624
+ styles2.split(";").filter(Boolean).forEach((rule) => {
5625
+ const colonIdx = rule.indexOf(":");
5626
+ if (colonIdx === -1) return;
5627
+ const prop = rule.slice(0, colonIdx).trim();
5628
+ const val = rule.slice(colonIdx + 1).trim();
5629
+ if (prop && val) {
5630
+ element.style.setProperty(prop, val);
5631
+ }
5632
+ });
5633
+ element.setAttribute("data-position", position);
5634
+ const isVisible = element.classList.contains("sp-watermark--visible");
5635
+ const visClass = isVisible ? " sp-watermark--visible" : " sp-watermark--hidden";
5636
+ element.className = `sp-watermark sp-watermark--${position}${visClass}${dynamic ? " sp-watermark--dynamic" : ""}`;
5637
+ };
5638
+ const randomizePosition = () => {
5639
+ const available = POSITIONS.filter((p) => p !== currentPosition);
5640
+ const next = available[Math.floor(Math.random() * available.length)];
5641
+ setPosition(next);
5642
+ };
5643
+ const show = () => {
5644
+ if (!element) return;
5645
+ element.classList.remove("sp-watermark--hidden");
5646
+ element.classList.add("sp-watermark--visible");
5647
+ };
5648
+ const hide = () => {
5649
+ if (!element) return;
5650
+ element.classList.remove("sp-watermark--visible");
5651
+ element.classList.add("sp-watermark--hidden");
5652
+ };
5653
+ const startDynamic = () => {
5654
+ if (!dynamic || dynamicTimer) return;
5655
+ dynamicTimer = setInterval(randomizePosition, dynamicInterval);
5656
+ };
5657
+ const stopDynamic = () => {
5658
+ if (dynamicTimer) {
5659
+ clearInterval(dynamicTimer);
5660
+ dynamicTimer = null;
5661
+ }
5662
+ };
5663
+ const cleanup = () => {
5664
+ stopDynamic();
5665
+ if (showDelayTimer) {
5666
+ clearTimeout(showDelayTimer);
5667
+ showDelayTimer = null;
5668
+ }
5669
+ if (element?.parentNode) {
5670
+ element.parentNode.removeChild(element);
5671
+ }
5672
+ element = null;
5673
+ };
5674
+ return {
5675
+ id: "watermark",
5676
+ name: "Watermark",
5677
+ version: "1.0.0",
5678
+ type: "feature",
5679
+ description: "Anti-piracy watermark overlay with text/image support and dynamic repositioning",
5680
+ init(pluginApi) {
5681
+ api = pluginApi;
5682
+ api.logger.debug("Watermark plugin initialized");
5683
+ element = createElement2();
5684
+ api.container.appendChild(element);
5685
+ const unsubPlay = api.on("playback:play", () => {
5686
+ if (showDelay > 0) {
5687
+ showDelayTimer = setTimeout(() => {
5688
+ show();
5689
+ startDynamic();
5690
+ }, showDelay);
5691
+ } else {
5692
+ show();
5693
+ startDynamic();
5694
+ }
5695
+ });
5696
+ const unsubPause = api.on("playback:pause", () => {
5697
+ hide();
5698
+ stopDynamic();
5699
+ if (showDelayTimer) {
5700
+ clearTimeout(showDelayTimer);
5701
+ showDelayTimer = null;
5702
+ }
5703
+ });
5704
+ const unsubEnded = api.on("playback:ended", () => {
5705
+ hide();
5706
+ stopDynamic();
5707
+ });
5708
+ const unsubChange = api.on("playlist:change", ({ track }) => {
5709
+ if (!element || !track) return;
5710
+ const metadata = track.metadata;
5711
+ if (metadata) {
5712
+ const watermarkUrl = metadata.watermarkUrl;
5713
+ const watermarkText = metadata.watermarkText;
5714
+ if (watermarkUrl || watermarkText) {
5715
+ updateContent(element, watermarkUrl, watermarkText);
5716
+ }
5717
+ }
5718
+ });
5719
+ api.onDestroy(() => {
5720
+ unsubPlay();
5721
+ unsubPause();
5722
+ unsubEnded();
5723
+ unsubChange();
5724
+ cleanup();
5725
+ });
5726
+ },
5727
+ destroy() {
5728
+ api?.logger.debug("Watermark plugin destroyed");
5729
+ cleanup();
5730
+ api = null;
5731
+ },
5732
+ setText(text) {
5733
+ if (element) updateContent(element, void 0, text);
5734
+ },
5735
+ setImage(imageUrl) {
5736
+ if (element) updateContent(element, imageUrl);
5737
+ },
5738
+ setPosition,
5739
+ setOpacity(value) {
5740
+ if (element) element.style.opacity = String(Math.max(0, Math.min(1, value)));
5741
+ },
5742
+ show,
5743
+ hide,
5744
+ getConfig() {
5745
+ return { ...config, position: currentPosition, opacity: element ? parseFloat(element.style.opacity) || opacity : opacity };
5746
+ }
5747
+ };
5748
+ }
5749
+ function createCaptionsPlugin(config = {}) {
5750
+ let api = null;
5751
+ let video = null;
5752
+ let addedTrackElements = [];
5753
+ const extractFromHLS = config.extractFromHLS !== false;
5754
+ const autoSelect = config.autoSelect ?? false;
5755
+ const defaultLanguage = config.defaultLanguage ?? "en";
5756
+ const getVideo2 = () => {
5757
+ if (video) return video;
5758
+ video = api?.container.querySelector("video") ?? null;
5759
+ return video;
5760
+ };
5761
+ const cleanupTracks = () => {
5762
+ for (const trackEl of addedTrackElements) {
5763
+ trackEl.parentNode?.removeChild(trackEl);
5764
+ }
5765
+ addedTrackElements = [];
5766
+ api?.setState("textTracks", []);
5767
+ api?.setState("currentTextTrack", null);
5768
+ };
5769
+ const addTrackElement = (source) => {
5770
+ const videoEl = getVideo2();
5771
+ if (!videoEl) throw new Error("No video element");
5772
+ const trackEl = document.createElement("track");
5773
+ trackEl.kind = source.kind || "subtitles";
5774
+ trackEl.label = source.label;
5775
+ trackEl.srclang = source.language;
5776
+ trackEl.src = source.src;
5777
+ trackEl.default = false;
5778
+ videoEl.appendChild(trackEl);
5779
+ addedTrackElements.push(trackEl);
5780
+ if (trackEl.track) {
5781
+ trackEl.track.mode = "disabled";
5782
+ }
5783
+ return trackEl;
5784
+ };
5785
+ const syncTracksToState = () => {
5786
+ const videoEl = getVideo2();
5787
+ if (!videoEl) return;
5788
+ const tracks = [];
5789
+ let currentTrack = null;
5790
+ for (let i = 0; i < videoEl.textTracks.length; i++) {
5791
+ const track = videoEl.textTracks[i];
5792
+ if (track.kind !== "subtitles" && track.kind !== "captions") continue;
5793
+ const scarlettTrack = {
5794
+ id: `track-${i}`,
5795
+ label: track.label || `Track ${i + 1}`,
5796
+ language: track.language || "",
5797
+ kind: track.kind,
5798
+ active: track.mode === "showing"
5799
+ };
5800
+ tracks.push(scarlettTrack);
5801
+ if (track.mode === "showing") {
5802
+ currentTrack = scarlettTrack;
5803
+ }
5804
+ }
5805
+ api?.setState("textTracks", tracks);
5806
+ api?.setState("currentTextTrack", currentTrack);
5807
+ };
5808
+ const selectTrack = (trackId) => {
5809
+ const videoEl = getVideo2();
5810
+ if (!videoEl) return;
5811
+ for (let i = 0; i < videoEl.textTracks.length; i++) {
5812
+ const track = videoEl.textTracks[i];
5813
+ if (track.kind !== "subtitles" && track.kind !== "captions") continue;
5814
+ const id = `track-${i}`;
5815
+ if (trackId && id === trackId) {
5816
+ track.mode = "showing";
5817
+ } else {
5818
+ track.mode = "disabled";
5819
+ }
5820
+ }
5821
+ syncTracksToState();
5822
+ };
5823
+ const extractHlsSubtitles = () => {
5824
+ if (!extractFromHLS || !api) return;
5825
+ const hlsPlugin = api.getPlugin("hls-provider");
5826
+ if (!hlsPlugin || hlsPlugin.isNativeHLS()) return;
5827
+ const hlsInstance = hlsPlugin.getHlsInstance();
5828
+ if (!hlsInstance?.subtitleTracks?.length) return;
5829
+ api.logger.debug("Extracting HLS subtitle tracks", {
5830
+ count: hlsInstance.subtitleTracks.length
5831
+ });
5832
+ for (const hlsTrack of hlsInstance.subtitleTracks) {
5833
+ addTrackElement({
5834
+ language: hlsTrack.lang || "unknown",
5835
+ label: hlsTrack.name || `Subtitle ${hlsTrack.id}`,
5836
+ src: hlsTrack.url,
5837
+ kind: "subtitles"
5838
+ });
5839
+ }
5840
+ syncTracksToState();
5841
+ if (autoSelect) {
5842
+ autoSelectTrack();
5843
+ }
5844
+ };
5845
+ const autoSelectTrack = () => {
5846
+ const tracks = api?.getState("textTracks") || [];
5847
+ const match = tracks.find((t) => t.language === defaultLanguage);
5848
+ if (match) {
5849
+ selectTrack(match.id);
5850
+ api?.logger.debug("Auto-selected caption track", { language: defaultLanguage, id: match.id });
5851
+ }
5852
+ };
5853
+ const initSources = () => {
5854
+ if (!config.sources?.length) return;
5855
+ for (const source of config.sources) {
5856
+ addTrackElement(source);
5857
+ }
5858
+ syncTracksToState();
5859
+ if (autoSelect) {
5860
+ autoSelectTrack();
5861
+ }
5862
+ };
5863
+ return {
5864
+ id: "captions",
5865
+ name: "Captions",
5866
+ version: "1.0.0",
5867
+ type: "feature",
5868
+ description: "WebVTT subtitles and closed captions with HLS extraction",
5869
+ init(pluginApi) {
5870
+ api = pluginApi;
5871
+ api.logger.debug("Captions plugin initialized");
5872
+ api.setState("textTracks", []);
5873
+ api.setState("currentTextTrack", null);
5874
+ const unsubTrackText = api.on("track:text", ({ trackId }) => {
5875
+ selectTrack(trackId);
5876
+ });
5877
+ const unsubLoaded = api.on("media:loaded", () => {
5878
+ video = null;
5879
+ cleanupTracks();
5880
+ initSources();
5881
+ if (extractFromHLS) {
5882
+ setTimeout(extractHlsSubtitles, 500);
5883
+ }
5884
+ });
5885
+ const unsubLoadRequest = api.on("media:load-request", () => {
5886
+ video = null;
5887
+ cleanupTracks();
5888
+ });
5889
+ api.onDestroy(() => {
5890
+ unsubTrackText();
5891
+ unsubLoaded();
5892
+ unsubLoadRequest();
5893
+ cleanupTracks();
5894
+ });
5895
+ },
5896
+ destroy() {
5897
+ api?.logger.debug("Captions plugin destroyed");
5898
+ cleanupTracks();
5899
+ video = null;
5900
+ api = null;
5901
+ }
5902
+ };
5903
+ }
5494
5904
  class Signal {
5495
5905
  constructor(initialValue) {
5496
5906
  this.subscribers = /* @__PURE__ */ new Set();
@@ -7030,6 +7440,13 @@ class ScarlettPlayer {
7030
7440
  await this.pluginManager.initPlugin(id);
7031
7441
  }
7032
7442
  }
7443
+ this.eventBus.on("media:load-request", async ({ src, autoplay }) => {
7444
+ if (this.stateManager.getValue("chromecastActive")) return;
7445
+ await this.load(src);
7446
+ if (autoplay !== false) {
7447
+ await this.play();
7448
+ }
7449
+ });
7033
7450
  if (this.initialSrc) {
7034
7451
  await this.load(this.initialSrc);
7035
7452
  }
@@ -7857,6 +8274,12 @@ async function createEmbedPlayer(container, config, pluginCreators2, availableTy
7857
8274
  artwork: config.artwork || config.poster || config.playlist?.[0]?.artwork
7858
8275
  }));
7859
8276
  }
8277
+ if (pluginCreators2.watermark && config.watermark) {
8278
+ plugins.push(pluginCreators2.watermark(config.watermark));
8279
+ }
8280
+ if (pluginCreators2.captions) {
8281
+ plugins.push(pluginCreators2.captions(config.captions || {}));
8282
+ }
7860
8283
  if (pluginCreators2.analytics && config.analytics?.beaconUrl) {
7861
8284
  plugins.push(pluginCreators2.analytics({
7862
8285
  beaconUrl: config.analytics.beaconUrl,
@@ -7974,7 +8397,7 @@ function setupAutoInit(pluginCreators2, availableTypes) {
7974
8397
  }
7975
8398
  }
7976
8399
  }
7977
- const VERSION = "0.3.0";
8400
+ const VERSION = "0.5.3";
7978
8401
  const AVAILABLE_TYPES = ["video", "audio", "audio-mini"];
7979
8402
  const pluginCreators = {
7980
8403
  hls: createHLSPlugin,
@@ -7982,7 +8405,9 @@ const pluginCreators = {
7982
8405
  audioUI: createAudioUIPlugin,
7983
8406
  analytics: createAnalyticsPlugin,
7984
8407
  playlist: createPlaylistPlugin,
7985
- mediaSession: createMediaSessionPlugin
8408
+ mediaSession: createMediaSessionPlugin,
8409
+ watermark: createWatermarkPlugin,
8410
+ captions: createCaptionsPlugin
7986
8411
  };
7987
8412
  const ScarlettPlayerAPI = createScarlettPlayerAPI(
7988
8413
  pluginCreators,