vidply 1.0.26 → 1.0.27

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.
Files changed (45) hide show
  1. package/dist/dev/{vidply.HLSRenderer-X46P47LY.js → vidply.HLSRenderer-ENLZE4QS.js} +13 -8
  2. package/dist/dev/vidply.HLSRenderer-ENLZE4QS.js.map +7 -0
  3. package/dist/dev/{vidply.HTML5Renderer-LXQ3I45Q.js → vidply.HTML5Renderer-6SBDI6S2.js} +2 -2
  4. package/dist/dev/vidply.SoundCloudRenderer-CD7VJKNS.js +280 -0
  5. package/dist/dev/vidply.SoundCloudRenderer-CD7VJKNS.js.map +7 -0
  6. package/dist/dev/{vidply.VimeoRenderer-DCETT5IZ.js → vidply.VimeoRenderer-VPH4RNES.js} +17 -4
  7. package/dist/dev/vidply.VimeoRenderer-VPH4RNES.js.map +7 -0
  8. package/dist/dev/{vidply.YouTubeRenderer-QLMMD757.js → vidply.YouTubeRenderer-6MGKEFTZ.js} +14 -6
  9. package/dist/dev/vidply.YouTubeRenderer-6MGKEFTZ.js.map +7 -0
  10. package/dist/dev/{vidply.chunk-UEIJOJH6.js → vidply.chunk-BCOFCT6U.js} +4 -1
  11. package/dist/dev/vidply.chunk-BCOFCT6U.js.map +7 -0
  12. package/dist/dev/vidply.esm.js +288 -12
  13. package/dist/dev/vidply.esm.js.map +2 -2
  14. package/dist/legacy/vidply.js +609 -23
  15. package/dist/legacy/vidply.js.map +3 -3
  16. package/dist/legacy/vidply.min.js +1 -1
  17. package/dist/legacy/vidply.min.meta.json +26 -13
  18. package/dist/prod/vidply.HLSRenderer-CBXZ4RF2.min.js +6 -0
  19. package/dist/prod/{vidply.HTML5Renderer-XJCSUETP.min.js → vidply.HTML5Renderer-MY7XDV7R.min.js} +1 -1
  20. package/dist/prod/vidply.SoundCloudRenderer-MOR2CUFH.min.js +6 -0
  21. package/dist/prod/vidply.VimeoRenderer-3HBMM2WR.min.js +6 -0
  22. package/dist/prod/vidply.YouTubeRenderer-MFC2GMAC.min.js +6 -0
  23. package/dist/prod/vidply.chunk-OXXPY2XB.min.js +6 -0
  24. package/dist/prod/vidply.esm.min.js +6 -6
  25. package/dist/vidply.css +51 -0
  26. package/dist/vidply.esm.min.meta.json +56 -28
  27. package/dist/vidply.min.css +1 -1
  28. package/package.json +1 -1
  29. package/src/core/Player.js +117 -8
  30. package/src/features/PlaylistManager.js +312 -4
  31. package/src/renderers/HLSRenderer.js +17 -9
  32. package/src/renderers/HTML5Renderer.js +5 -0
  33. package/src/renderers/SoundCloudRenderer.js +355 -0
  34. package/src/renderers/VimeoRenderer.js +20 -4
  35. package/src/renderers/YouTubeRenderer.js +12 -6
  36. package/src/styles/vidply.css +51 -0
  37. package/dist/dev/vidply.HLSRenderer-X46P47LY.js.map +0 -7
  38. package/dist/dev/vidply.VimeoRenderer-DCETT5IZ.js.map +0 -7
  39. package/dist/dev/vidply.YouTubeRenderer-QLMMD757.js.map +0 -7
  40. package/dist/dev/vidply.chunk-UEIJOJH6.js.map +0 -7
  41. package/dist/prod/vidply.HLSRenderer-LDXSMWTI.min.js +0 -6
  42. package/dist/prod/vidply.VimeoRenderer-P3PU27S7.min.js +0 -6
  43. package/dist/prod/vidply.YouTubeRenderer-DGKKWB5M.min.js +0 -6
  44. package/dist/prod/vidply.chunk-BQBGEJF7.min.js +0 -6
  45. /package/dist/dev/{vidply.HTML5Renderer-LXQ3I45Q.js.map → vidply.HTML5Renderer-6SBDI6S2.js.map} +0 -0
@@ -1647,6 +1647,9 @@
1647
1647
  this.attachEvents();
1648
1648
  this.media.preload = this.player.options.preload;
1649
1649
  this.media.load();
1650
+ if (this.player.container) {
1651
+ this.player.container.classList.remove("vidply-external-controls");
1652
+ }
1650
1653
  }
1651
1654
  attachEvents() {
1652
1655
  this.media.addEventListener("loadedmetadata", () => {
@@ -4417,7 +4420,8 @@
4417
4420
  this.iframe = null;
4418
4421
  }
4419
4422
  async init() {
4420
- this.videoId = this.extractVideoId(this.player.element.src);
4423
+ const src = this.player.currentSource || this.player.element.src;
4424
+ this.videoId = this.extractVideoId(src);
4421
4425
  if (!this.videoId) {
4422
4426
  throw new Error("Invalid YouTube URL");
4423
4427
  }
@@ -4466,7 +4470,8 @@
4466
4470
  this.iframe = document.createElement("div");
4467
4471
  this.iframe.id = "youtube-player-".concat(Math.random().toString(36).substr(2, 9));
4468
4472
  this.iframe.style.width = "100%";
4469
- this.iframe.style.height = "100%";
4473
+ this.iframe.style.aspectRatio = "16 / 9";
4474
+ this.iframe.style.maxHeight = "100%";
4470
4475
  this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
4471
4476
  }
4472
4477
  async initializePlayer() {
@@ -4476,9 +4481,12 @@
4476
4481
  width: "100%",
4477
4482
  height: "100%",
4478
4483
  playerVars: {
4479
- controls: 0,
4480
- disablekb: 1,
4481
- fs: 0,
4484
+ controls: 1,
4485
+ // Use YouTube native controls
4486
+ disablekb: 0,
4487
+ // Allow keyboard controls
4488
+ fs: 1,
4489
+ // Allow fullscreen
4482
4490
  modestbranding: 1,
4483
4491
  rel: 0,
4484
4492
  showinfo: 0,
@@ -4491,6 +4499,9 @@
4491
4499
  onReady: (event) => {
4492
4500
  this.isReady = true;
4493
4501
  this.attachEvents();
4502
+ if (this.player.container) {
4503
+ this.player.container.classList.add("vidply-external-controls");
4504
+ }
4494
4505
  resolve();
4495
4506
  },
4496
4507
  onStateChange: (event) => this.handleStateChange(event),
@@ -4644,7 +4655,8 @@
4644
4655
  this.iframe = null;
4645
4656
  }
4646
4657
  async init() {
4647
- this.videoId = this.extractVideoId(this.player.element.src);
4658
+ const src = this.player.currentSource || this.player.element.src;
4659
+ this.videoId = this.extractVideoId(src);
4648
4660
  if (!this.videoId) {
4649
4661
  throw new Error("Invalid Vimeo URL");
4650
4662
  }
@@ -4683,7 +4695,8 @@
4683
4695
  this.iframe = document.createElement("div");
4684
4696
  this.iframe.id = "vimeo-player-".concat(Math.random().toString(36).substr(2, 9));
4685
4697
  this.iframe.style.width = "100%";
4686
- this.iframe.style.height = "100%";
4698
+ this.iframe.style.aspectRatio = "16 / 9";
4699
+ this.iframe.style.maxHeight = "100%";
4687
4700
  this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
4688
4701
  }
4689
4702
  async initializePlayer() {
@@ -4691,7 +4704,8 @@
4691
4704
  id: this.videoId,
4692
4705
  width: "100%",
4693
4706
  height: "100%",
4694
- controls: false,
4707
+ controls: true,
4708
+ // Use Vimeo native controls
4695
4709
  autoplay: this.player.options.autoplay,
4696
4710
  muted: this.player.options.muted,
4697
4711
  loop: this.player.options.loop,
@@ -4703,6 +4717,16 @@
4703
4717
  this.vimeo = new window.Vimeo.Player(this.iframe.id, options);
4704
4718
  await this.vimeo.ready();
4705
4719
  this.isReady = true;
4720
+ const vimeoIframe = this.iframe.querySelector("iframe");
4721
+ if (vimeoIframe) {
4722
+ vimeoIframe.style.width = "100%";
4723
+ vimeoIframe.style.height = "100%";
4724
+ vimeoIframe.setAttribute("width", "100%");
4725
+ vimeoIframe.setAttribute("height", "100%");
4726
+ }
4727
+ if (this.player.container) {
4728
+ this.player.container.classList.add("vidply-external-controls");
4729
+ }
4706
4730
  this.attachEvents();
4707
4731
  try {
4708
4732
  const duration = await this.vimeo.getDuration();
@@ -4915,12 +4939,14 @@
4915
4939
  fragLoadingMaxRetryTimeout: 64e3
4916
4940
  });
4917
4941
  this.hls.attachMedia(this.media);
4918
- let src;
4919
- const sourceElement = this.player.element.querySelector("source");
4920
- if (sourceElement) {
4921
- src = sourceElement.getAttribute("src");
4922
- } else {
4923
- src = this.player.element.getAttribute("src") || this.player.element.src;
4942
+ let src = this.player.currentSource;
4943
+ if (!src) {
4944
+ const sourceElement = this.player.element.querySelector("source");
4945
+ if (sourceElement) {
4946
+ src = sourceElement.getAttribute("src");
4947
+ } else {
4948
+ src = this.player.element.getAttribute("src") || this.player.element.src;
4949
+ }
4924
4950
  }
4925
4951
  this.player.log("Loading HLS source: ".concat(src), "log");
4926
4952
  if (!src) {
@@ -4943,6 +4969,9 @@
4943
4969
  this.hls.on(window.Hls.Events.MANIFEST_PARSED, (event, data) => {
4944
4970
  this.player.log("HLS manifest loaded, found " + data.levels.length + " quality levels");
4945
4971
  this.player.emit("hlsmanifestparsed", data);
4972
+ if (this.player.container) {
4973
+ this.player.container.classList.remove("vidply-external-controls");
4974
+ }
4946
4975
  });
4947
4976
  this.hls.on(window.Hls.Events.LEVEL_SWITCHED, (event, data) => {
4948
4977
  this.player.log("HLS level switched to " + data.level);
@@ -5101,6 +5130,287 @@
5101
5130
  }
5102
5131
  });
5103
5132
 
5133
+ // src/renderers/SoundCloudRenderer.js
5134
+ var SoundCloudRenderer_exports = {};
5135
+ __export(SoundCloudRenderer_exports, {
5136
+ SoundCloudRenderer: () => SoundCloudRenderer,
5137
+ default: () => SoundCloudRenderer_default
5138
+ });
5139
+ var SoundCloudRenderer, SoundCloudRenderer_default;
5140
+ var init_SoundCloudRenderer = __esm({
5141
+ "src/renderers/SoundCloudRenderer.js"() {
5142
+ SoundCloudRenderer = class {
5143
+ constructor(player) {
5144
+ this.player = player;
5145
+ this.widget = null;
5146
+ this.trackUrl = null;
5147
+ this.isReady = false;
5148
+ this.iframe = null;
5149
+ this.iframeId = null;
5150
+ }
5151
+ async init() {
5152
+ var _a;
5153
+ this.trackUrl = this.player.currentSource || this.player.element.src || ((_a = this.player.element.querySelector("source")) == null ? void 0 : _a.src);
5154
+ if (!this.trackUrl || !this.isValidSoundCloudUrl(this.trackUrl)) {
5155
+ throw new Error("Invalid SoundCloud URL");
5156
+ }
5157
+ await this.loadSoundCloudAPI();
5158
+ this.createIframe();
5159
+ await this.initializeWidget();
5160
+ }
5161
+ /**
5162
+ * Validate SoundCloud URL
5163
+ * @param {string} url
5164
+ * @returns {boolean}
5165
+ */
5166
+ isValidSoundCloudUrl(url) {
5167
+ return url.includes("soundcloud.com") || url.includes("api.soundcloud.com");
5168
+ }
5169
+ /**
5170
+ * Check if URL is a playlist/set
5171
+ */
5172
+ isPlaylist() {
5173
+ return this.trackUrl && this.trackUrl.includes("/sets/");
5174
+ }
5175
+ /**
5176
+ * Extract track/playlist info from URL for embed
5177
+ * SoundCloud URLs can be:
5178
+ * - https://soundcloud.com/artist/track
5179
+ * - https://soundcloud.com/artist/sets/playlist
5180
+ * - https://api.soundcloud.com/tracks/123456
5181
+ */
5182
+ getEmbedUrl() {
5183
+ const encodedUrl = encodeURIComponent(this.trackUrl);
5184
+ const params = new URLSearchParams({
5185
+ url: this.trackUrl,
5186
+ auto_play: this.player.options.autoplay ? "true" : "false",
5187
+ hide_related: "true",
5188
+ show_comments: "false",
5189
+ show_user: "true",
5190
+ show_reposts: "false",
5191
+ show_teaser: "false",
5192
+ visual: "false",
5193
+ // Use classic player for better control
5194
+ color: "%23007bff"
5195
+ });
5196
+ return "https://w.soundcloud.com/player/?".concat(params.toString());
5197
+ }
5198
+ async loadSoundCloudAPI() {
5199
+ if (window.SC && window.SC.Widget) {
5200
+ return Promise.resolve();
5201
+ }
5202
+ return new Promise((resolve, reject) => {
5203
+ const script = document.createElement("script");
5204
+ script.src = "https://w.soundcloud.com/player/api.js";
5205
+ script.onload = () => {
5206
+ setTimeout(() => {
5207
+ if (window.SC && window.SC.Widget) {
5208
+ resolve();
5209
+ } else {
5210
+ reject(new Error("SoundCloud Widget API not available"));
5211
+ }
5212
+ }, 100);
5213
+ };
5214
+ script.onerror = () => reject(new Error("Failed to load SoundCloud Widget API"));
5215
+ document.head.appendChild(script);
5216
+ });
5217
+ }
5218
+ createIframe() {
5219
+ this.player.element.style.display = "none";
5220
+ this.player.element.removeAttribute("poster");
5221
+ if (this.player.videoWrapper) {
5222
+ this.player.videoWrapper.classList.remove("vidply-forced-poster");
5223
+ this.player.videoWrapper.style.removeProperty("--vidply-poster-image");
5224
+ }
5225
+ this.iframeId = "soundcloud-player-".concat(Math.random().toString(36).substr(2, 9));
5226
+ this.iframe = document.createElement("iframe");
5227
+ this.iframe.id = this.iframeId;
5228
+ this.iframe.scrolling = "no";
5229
+ this.iframe.frameBorder = "no";
5230
+ this.iframe.allow = "autoplay";
5231
+ this.iframe.src = this.getEmbedUrl();
5232
+ this.iframe.style.width = "100%";
5233
+ this.iframe.style.display = "block";
5234
+ if (this.isPlaylist()) {
5235
+ this.iframe.style.aspectRatio = "16 / 9";
5236
+ this.iframe.classList.add("vidply-soundcloud-iframe", "vidply-soundcloud-playlist");
5237
+ } else {
5238
+ this.iframe.style.aspectRatio = "16 / 3";
5239
+ this.iframe.classList.add("vidply-soundcloud-iframe");
5240
+ }
5241
+ this.iframe.style.maxHeight = "100%";
5242
+ this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
5243
+ }
5244
+ async initializeWidget() {
5245
+ return new Promise((resolve, reject) => {
5246
+ this.iframe.addEventListener("load", () => {
5247
+ try {
5248
+ this.widget = window.SC.Widget(this.iframe);
5249
+ this.widget.bind(window.SC.Widget.Events.READY, () => {
5250
+ this.isReady = true;
5251
+ this.attachEvents();
5252
+ if (this.player.container) {
5253
+ this.player.container.classList.add("vidply-external-controls");
5254
+ }
5255
+ this.widget.getCurrentSound((sound) => {
5256
+ if (sound) {
5257
+ this.player.state.duration = sound.duration / 1e3;
5258
+ this.player.emit("loadedmetadata");
5259
+ }
5260
+ });
5261
+ resolve();
5262
+ });
5263
+ this.widget.bind(window.SC.Widget.Events.ERROR, (error) => {
5264
+ this.player.handleError(new Error("SoundCloud error: ".concat(error.message || "Unknown error")));
5265
+ });
5266
+ } catch (error) {
5267
+ reject(error);
5268
+ }
5269
+ });
5270
+ setTimeout(() => {
5271
+ if (!this.isReady) {
5272
+ reject(new Error("SoundCloud widget initialization timeout"));
5273
+ }
5274
+ }, 1e4);
5275
+ });
5276
+ }
5277
+ attachEvents() {
5278
+ if (!this.widget) return;
5279
+ const Events = window.SC.Widget.Events;
5280
+ this.widget.bind(Events.PLAY, () => {
5281
+ this.player.state.playing = true;
5282
+ this.player.state.paused = false;
5283
+ this.player.state.ended = false;
5284
+ this.player.emit("play");
5285
+ if (this.player.options.onPlay) {
5286
+ this.player.options.onPlay.call(this.player);
5287
+ }
5288
+ });
5289
+ this.widget.bind(Events.PAUSE, () => {
5290
+ this.player.state.playing = false;
5291
+ this.player.state.paused = true;
5292
+ this.player.emit("pause");
5293
+ if (this.player.options.onPause) {
5294
+ this.player.options.onPause.call(this.player);
5295
+ }
5296
+ });
5297
+ this.widget.bind(Events.FINISH, () => {
5298
+ this.player.state.playing = false;
5299
+ this.player.state.paused = true;
5300
+ this.player.state.ended = true;
5301
+ this.player.emit("ended");
5302
+ if (this.player.options.onEnded) {
5303
+ this.player.options.onEnded.call(this.player);
5304
+ }
5305
+ if (this.player.options.loop) {
5306
+ this.seek(0);
5307
+ this.play();
5308
+ }
5309
+ });
5310
+ this.widget.bind(Events.PLAY_PROGRESS, (data) => {
5311
+ const currentTime = data.currentPosition / 1e3;
5312
+ this.player.state.currentTime = currentTime;
5313
+ this.player.emit("timeupdate", currentTime);
5314
+ if (this.player.options.onTimeUpdate) {
5315
+ this.player.options.onTimeUpdate.call(this.player, currentTime);
5316
+ }
5317
+ });
5318
+ this.widget.bind(Events.SEEK, (data) => {
5319
+ this.player.state.currentTime = data.currentPosition / 1e3;
5320
+ this.player.emit("seeked");
5321
+ });
5322
+ this.widget.bind(Events.LOAD_PROGRESS, (data) => {
5323
+ if (this.player.state.duration) {
5324
+ const buffered = data.loadedProgress * this.player.state.duration;
5325
+ this.player.emit("progress", buffered);
5326
+ }
5327
+ });
5328
+ }
5329
+ play() {
5330
+ if (this.isReady && this.widget) {
5331
+ const scrollX = window.scrollX;
5332
+ const scrollY = window.scrollY;
5333
+ this.widget.play();
5334
+ window.scrollTo(scrollX, scrollY);
5335
+ }
5336
+ }
5337
+ pause() {
5338
+ if (this.isReady && this.widget) {
5339
+ this.widget.pause();
5340
+ }
5341
+ }
5342
+ seek(time) {
5343
+ if (this.isReady && this.widget) {
5344
+ this.widget.seekTo(time * 1e3);
5345
+ this.player.state.currentTime = time;
5346
+ }
5347
+ }
5348
+ setVolume(volume) {
5349
+ if (this.isReady && this.widget) {
5350
+ this.widget.setVolume(volume * 100);
5351
+ this.player.state.volume = volume;
5352
+ }
5353
+ }
5354
+ setMuted(muted) {
5355
+ if (this.isReady && this.widget) {
5356
+ if (muted) {
5357
+ this.widget.getVolume((vol) => {
5358
+ this._previousVolume = vol;
5359
+ this.widget.setVolume(0);
5360
+ });
5361
+ } else {
5362
+ this.widget.setVolume(this._previousVolume || 100);
5363
+ }
5364
+ this.player.state.muted = muted;
5365
+ }
5366
+ }
5367
+ setPlaybackSpeed(speed) {
5368
+ this.player.log("SoundCloud does not support playback speed control", "warn");
5369
+ }
5370
+ /**
5371
+ * Get current track info
5372
+ * @returns {Promise<Object>}
5373
+ */
5374
+ getCurrentSound() {
5375
+ return new Promise((resolve) => {
5376
+ if (this.isReady && this.widget) {
5377
+ this.widget.getCurrentSound((sound) => {
5378
+ resolve(sound);
5379
+ });
5380
+ } else {
5381
+ resolve(null);
5382
+ }
5383
+ });
5384
+ }
5385
+ destroy() {
5386
+ if (this.widget) {
5387
+ const Events = window.SC.Widget.Events;
5388
+ try {
5389
+ this.widget.unbind(Events.READY);
5390
+ this.widget.unbind(Events.PLAY);
5391
+ this.widget.unbind(Events.PAUSE);
5392
+ this.widget.unbind(Events.FINISH);
5393
+ this.widget.unbind(Events.PLAY_PROGRESS);
5394
+ this.widget.unbind(Events.SEEK);
5395
+ this.widget.unbind(Events.LOAD_PROGRESS);
5396
+ this.widget.unbind(Events.ERROR);
5397
+ } catch (e) {
5398
+ }
5399
+ }
5400
+ if (this.iframe && this.iframe.parentNode) {
5401
+ this.iframe.parentNode.removeChild(this.iframe);
5402
+ }
5403
+ if (this.player.element) {
5404
+ this.player.element.style.display = "";
5405
+ }
5406
+ this.widget = null;
5407
+ this.isReady = false;
5408
+ }
5409
+ };
5410
+ SoundCloudRenderer_default = SoundCloudRenderer;
5411
+ }
5412
+ });
5413
+
5104
5414
  // src/utils/EventEmitter.js
5105
5415
  var EventEmitter = class {
5106
5416
  constructor() {
@@ -8180,6 +8490,7 @@
8180
8490
  this.element.appendChild(mediaElement);
8181
8491
  this.element = mediaElement;
8182
8492
  }
8493
+ this._originalElement = this.element;
8183
8494
  this.options = __spreadValues({
8184
8495
  // Display
8185
8496
  width: null,
@@ -8594,10 +8905,12 @@
8594
8905
  }
8595
8906
  async initializeRenderer() {
8596
8907
  var _a;
8597
- const src = this.element.src || ((_a = this.element.querySelector("source")) == null ? void 0 : _a.src);
8908
+ let src = this._pendingSource || this.element.src || ((_a = this.element.querySelector("source")) == null ? void 0 : _a.src);
8598
8909
  if (!src) {
8599
8910
  throw new Error("No media source found");
8600
8911
  }
8912
+ this.currentSource = src;
8913
+ this._pendingSource = null;
8601
8914
  const sourceElements = this.sourceElements;
8602
8915
  for (const sourceEl of sourceElements) {
8603
8916
  const descSrc = sourceEl.getAttribute("data-desc-src");
@@ -8658,6 +8971,9 @@
8658
8971
  } else if (src.includes(".m3u8")) {
8659
8972
  const module = await Promise.resolve().then(() => (init_HLSRenderer(), HLSRenderer_exports));
8660
8973
  rendererClass = module.HLSRenderer || module.default;
8974
+ } else if (src.includes("soundcloud.com") || src.includes("api.soundcloud.com")) {
8975
+ const module = await Promise.resolve().then(() => (init_SoundCloudRenderer(), SoundCloudRenderer_exports));
8976
+ rendererClass = module.SoundCloudRenderer || module.default;
8661
8977
  }
8662
8978
  this.log("Using ".concat((rendererClass == null ? void 0 : rendererClass.name) || "HTML5Renderer", " renderer"));
8663
8979
  this.renderer = new rendererClass(this);
@@ -8769,6 +9085,11 @@
8769
9085
  const resolvedPoster = this.resolvePosterPath(poster);
8770
9086
  this.videoWrapper.style.setProperty("--vidply-poster-image", 'url("'.concat(resolvedPoster, '")'));
8771
9087
  this.videoWrapper.classList.add("vidply-forced-poster");
9088
+ if (this._isAudioContent && this.container) {
9089
+ this.container.classList.add("vidply-audio-content");
9090
+ } else if (this.container) {
9091
+ this.container.classList.remove("vidply-audio-content");
9092
+ }
8772
9093
  }
8773
9094
  hidePosterOverlay() {
8774
9095
  if (!this.videoWrapper) {
@@ -8811,6 +9132,15 @@
8811
9132
  * @param {string} [config.audioDescriptionSrc] - Audio description video URL
8812
9133
  * @param {string} [config.signLanguageSrc] - Sign language video URL
8813
9134
  */
9135
+ /**
9136
+ * Check if a source URL requires an external renderer (YouTube, Vimeo, SoundCloud, HLS)
9137
+ * @param {string} src - Source URL
9138
+ * @returns {boolean}
9139
+ */
9140
+ isExternalRendererUrl(src) {
9141
+ if (!src) return false;
9142
+ return src.includes("youtube.com") || src.includes("youtu.be") || src.includes("vimeo.com") || src.includes("soundcloud.com") || src.includes("api.soundcloud.com") || src.includes(".m3u8");
9143
+ }
8814
9144
  async load(config) {
8815
9145
  var _a;
8816
9146
  try {
@@ -8823,12 +9153,44 @@
8823
9153
  const existingTracks = this.trackElements;
8824
9154
  existingTracks.forEach((track) => track.remove());
8825
9155
  this.invalidateTrackCache();
8826
- this.element.src = config.src;
8827
- if (config.type) {
8828
- this.element.type = config.type;
9156
+ const isExternalRenderer = this.isExternalRendererUrl(config.src);
9157
+ if (isExternalRenderer) {
9158
+ this._switchingRenderer = true;
9159
+ }
9160
+ if (!isExternalRenderer) {
9161
+ this.element.src = config.src;
9162
+ if (config.type) {
9163
+ this.element.type = config.type;
9164
+ }
9165
+ } else {
9166
+ this.element.removeAttribute("src");
9167
+ const sources = this.element.querySelectorAll("source");
9168
+ sources.forEach((s) => s.removeAttribute("src"));
9169
+ }
9170
+ this._pendingSource = config.src;
9171
+ this._isAudioContent = config.type && config.type.startsWith("audio/");
9172
+ if (this.container) {
9173
+ if (this._isAudioContent) {
9174
+ this.container.classList.add("vidply-audio-content");
9175
+ } else {
9176
+ this.container.classList.remove("vidply-audio-content");
9177
+ }
8829
9178
  }
8830
9179
  if (config.poster && this.element.tagName === "VIDEO") {
8831
- this.element.poster = this.resolvePosterPath(config.poster);
9180
+ if (this._isAudioContent) {
9181
+ this.element.removeAttribute("poster");
9182
+ if (this.videoWrapper) {
9183
+ const resolvedPoster = this.resolvePosterPath(config.poster);
9184
+ this.videoWrapper.style.setProperty("--vidply-poster-image", 'url("'.concat(resolvedPoster, '")'));
9185
+ this.videoWrapper.classList.add("vidply-forced-poster");
9186
+ }
9187
+ } else {
9188
+ this.element.poster = this.resolvePosterPath(config.poster);
9189
+ if (this.videoWrapper) {
9190
+ this.videoWrapper.classList.remove("vidply-forced-poster");
9191
+ this.videoWrapper.style.removeProperty("--vidply-poster-image");
9192
+ }
9193
+ }
8832
9194
  }
8833
9195
  if (config.tracks && config.tracks.length > 0) {
8834
9196
  config.tracks.forEach((trackConfig) => {
@@ -8871,6 +9233,13 @@
8871
9233
  this.renderer.media = this.element;
8872
9234
  this.element.load();
8873
9235
  }
9236
+ if (isExternalRenderer) {
9237
+ setTimeout(() => {
9238
+ this._switchingRenderer = false;
9239
+ }, 500);
9240
+ } else {
9241
+ this._switchingRenderer = false;
9242
+ }
8874
9243
  window.scrollTo(scrollX, scrollY);
8875
9244
  if (this.captionManager) {
8876
9245
  this.captionManager.destroy();
@@ -8929,11 +9298,13 @@
8929
9298
  const isYouTube = src.includes("youtube.com") || src.includes("youtu.be");
8930
9299
  const isVimeo = src.includes("vimeo.com");
8931
9300
  const isHLS = src.includes(".m3u8");
9301
+ const isSoundCloud = src.includes("soundcloud.com") || src.includes("api.soundcloud.com");
8932
9302
  const currentRendererName = this.renderer.constructor.name;
8933
9303
  if (isYouTube && currentRendererName !== "YouTubeRenderer") return true;
8934
9304
  if (isVimeo && currentRendererName !== "VimeoRenderer") return true;
8935
9305
  if (isHLS && currentRendererName !== "HLSRenderer") return true;
8936
- if (!isYouTube && !isVimeo && !isHLS && currentRendererName !== "HTML5Renderer") return true;
9306
+ if (isSoundCloud && currentRendererName !== "SoundCloudRenderer") return true;
9307
+ if (!isYouTube && !isVimeo && !isHLS && !isSoundCloud && currentRendererName !== "HTML5Renderer") return true;
8937
9308
  return false;
8938
9309
  }
8939
9310
  // Playback controls
@@ -11148,6 +11519,10 @@
11148
11519
  }
11149
11520
  // Error handling
11150
11521
  handleError(error) {
11522
+ if (this._switchingRenderer) {
11523
+ this.log("Suppressing error during renderer switch:", error, "debug");
11524
+ return;
11525
+ }
11151
11526
  this.log("Error:", error, "error");
11152
11527
  this.emit("error", error);
11153
11528
  if (this.options.onError) {
@@ -11691,7 +12066,9 @@
11691
12066
  autoPlayFirst: options.autoPlayFirst !== false,
11692
12067
  // Default true - auto-play first track on load
11693
12068
  loop: options.loop || false,
11694
- showPanel: options.showPanel !== false
12069
+ showPanel: options.showPanel !== false,
12070
+ // Default true
12071
+ recreatePlayers: options.recreatePlayers || false
11695
12072
  }, options);
11696
12073
  this.container = null;
11697
12074
  this.playlistPanel = null;
@@ -11699,6 +12076,8 @@
11699
12076
  this.navigationFeedback = null;
11700
12077
  this.isPanelVisible = this.options.showPanel !== false;
11701
12078
  this.isChangingTrack = false;
12079
+ this.hostElement = options.hostElement || null;
12080
+ this.PlayerClass = options.PlayerClass || null;
11702
12081
  this.handleTrackEnd = this.handleTrackEnd.bind(this);
11703
12082
  this.handleTrackError = this.handleTrackError.bind(this);
11704
12083
  this.player.playlistManager = this;
@@ -11708,6 +12087,159 @@
11708
12087
  this.loadPlaylist(this.initialTracks);
11709
12088
  }
11710
12089
  }
12090
+ /**
12091
+ * Determine the media type for a track
12092
+ * @param {Object} track - Track object
12093
+ * @returns {string} - 'audio', 'video', 'youtube', 'vimeo', 'soundcloud', 'hls'
12094
+ */
12095
+ getTrackMediaType(track) {
12096
+ const src = track.src || "";
12097
+ if (src.includes("youtube.com") || src.includes("youtu.be")) {
12098
+ return "youtube";
12099
+ }
12100
+ if (src.includes("vimeo.com")) {
12101
+ return "vimeo";
12102
+ }
12103
+ if (src.includes("soundcloud.com") || src.includes("api.soundcloud.com")) {
12104
+ return "soundcloud";
12105
+ }
12106
+ if (src.includes(".m3u8")) {
12107
+ return "hls";
12108
+ }
12109
+ if (track.type && track.type.startsWith("audio/")) {
12110
+ return "audio";
12111
+ }
12112
+ return "video";
12113
+ }
12114
+ /**
12115
+ * Recreate the player with the appropriate element type for the track
12116
+ * @param {Object} track - Track to load
12117
+ * @param {boolean} autoPlay - Whether to auto-play after creation
12118
+ */
12119
+ async recreatePlayerForTrack(track, autoPlay = false) {
12120
+ if (!this.hostElement || !this.PlayerClass) {
12121
+ console.warn("VidPly Playlist: Cannot recreate player - missing hostElement or PlayerClass");
12122
+ return false;
12123
+ }
12124
+ const mediaType = this.getTrackMediaType(track);
12125
+ const elementType = mediaType === "audio" ? "audio" : "video";
12126
+ const wasVisible = this.isPanelVisible;
12127
+ const savedTracks = [...this.tracks];
12128
+ const savedIndex = this.currentIndex;
12129
+ if (this.trackArtworkElement && this.trackArtworkElement.parentNode) {
12130
+ this.trackArtworkElement.parentNode.removeChild(this.trackArtworkElement);
12131
+ }
12132
+ if (this.trackInfoElement && this.trackInfoElement.parentNode) {
12133
+ this.trackInfoElement.parentNode.removeChild(this.trackInfoElement);
12134
+ }
12135
+ if (this.navigationFeedback && this.navigationFeedback.parentNode) {
12136
+ this.navigationFeedback.parentNode.removeChild(this.navigationFeedback);
12137
+ }
12138
+ if (this.playlistPanel && this.playlistPanel.parentNode) {
12139
+ this.playlistPanel.parentNode.removeChild(this.playlistPanel);
12140
+ }
12141
+ if (this.player) {
12142
+ this.player.off("ended", this.handleTrackEnd);
12143
+ this.player.off("error", this.handleTrackError);
12144
+ this.player.destroy();
12145
+ }
12146
+ this.hostElement.innerHTML = "";
12147
+ const mediaElement = document.createElement(elementType);
12148
+ mediaElement.setAttribute("preload", "metadata");
12149
+ if (elementType === "video" && track.poster && (mediaType === "video" || mediaType === "hls")) {
12150
+ mediaElement.setAttribute("poster", track.poster);
12151
+ }
12152
+ const isExternalRenderer = ["youtube", "vimeo", "soundcloud", "hls"].includes(mediaType);
12153
+ if (!isExternalRenderer) {
12154
+ const source = document.createElement("source");
12155
+ source.src = track.src;
12156
+ if (track.type) {
12157
+ source.type = track.type;
12158
+ }
12159
+ mediaElement.appendChild(source);
12160
+ if (track.tracks && track.tracks.length > 0) {
12161
+ track.tracks.forEach((trackConfig) => {
12162
+ const trackEl = document.createElement("track");
12163
+ trackEl.src = trackConfig.src;
12164
+ trackEl.kind = trackConfig.kind || "captions";
12165
+ trackEl.srclang = trackConfig.srclang || "en";
12166
+ trackEl.label = trackConfig.label || trackConfig.srclang;
12167
+ if (trackConfig.default) {
12168
+ trackEl.default = true;
12169
+ }
12170
+ mediaElement.appendChild(trackEl);
12171
+ });
12172
+ }
12173
+ }
12174
+ this.hostElement.appendChild(mediaElement);
12175
+ const playerOptions = {
12176
+ mediaType: elementType,
12177
+ poster: track.poster,
12178
+ audioDescriptionSrc: track.audioDescriptionSrc || null,
12179
+ audioDescriptionDuration: track.audioDescriptionDuration || null,
12180
+ signLanguageSrc: track.signLanguageSrc || null
12181
+ };
12182
+ this.player = new this.PlayerClass(mediaElement, playerOptions);
12183
+ this.player.playlistManager = this;
12184
+ await new Promise((resolve) => {
12185
+ this.player.on("ready", resolve);
12186
+ });
12187
+ this.player.on("ended", this.handleTrackEnd);
12188
+ this.player.on("error", this.handleTrackError);
12189
+ if (this.player.container) {
12190
+ if (this.trackArtworkElement) {
12191
+ const videoWrapper = this.player.container.querySelector(".vidply-video-wrapper");
12192
+ if (videoWrapper) {
12193
+ this.player.container.insertBefore(this.trackArtworkElement, videoWrapper);
12194
+ } else {
12195
+ this.player.container.appendChild(this.trackArtworkElement);
12196
+ }
12197
+ }
12198
+ if (this.trackInfoElement) {
12199
+ this.player.container.appendChild(this.trackInfoElement);
12200
+ }
12201
+ if (this.navigationFeedback) {
12202
+ this.player.container.appendChild(this.navigationFeedback);
12203
+ }
12204
+ if (this.playlistPanel) {
12205
+ this.player.container.appendChild(this.playlistPanel);
12206
+ }
12207
+ }
12208
+ this.container = this.player.container;
12209
+ this.updatePlayerControls();
12210
+ this.tracks = savedTracks;
12211
+ this.currentIndex = savedIndex;
12212
+ this.updatePlaylistUI();
12213
+ this.isPanelVisible = wasVisible;
12214
+ if (this.playlistPanel) {
12215
+ this.playlistPanel.style.display = wasVisible ? "" : "none";
12216
+ }
12217
+ if (isExternalRenderer) {
12218
+ this.player.load({
12219
+ src: track.src,
12220
+ type: track.type,
12221
+ poster: track.poster,
12222
+ tracks: track.tracks || [],
12223
+ audioDescriptionSrc: track.audioDescriptionSrc || null,
12224
+ signLanguageSrc: track.signLanguageSrc || null
12225
+ });
12226
+ } else {
12227
+ this.player.load({
12228
+ src: track.src,
12229
+ type: track.type,
12230
+ poster: track.poster,
12231
+ tracks: track.tracks || [],
12232
+ audioDescriptionSrc: track.audioDescriptionSrc || null,
12233
+ signLanguageSrc: track.signLanguageSrc || null
12234
+ });
12235
+ }
12236
+ if (autoPlay) {
12237
+ setTimeout(() => {
12238
+ this.player.play();
12239
+ }, 100);
12240
+ }
12241
+ return true;
12242
+ }
11711
12243
  init() {
11712
12244
  this.player.on("ended", this.handleTrackEnd);
11713
12245
  this.player.on("error", this.handleTrackError);
@@ -11818,7 +12350,7 @@
11818
12350
  * Load a track without playing
11819
12351
  * @param {number} index - Track index
11820
12352
  */
11821
- loadTrack(index) {
12353
+ async loadTrack(index) {
11822
12354
  if (index < 0 || index >= this.tracks.length) {
11823
12355
  console.warn("VidPly Playlist: Invalid track index", index);
11824
12356
  return;
@@ -11826,6 +12358,25 @@
11826
12358
  const track = this.tracks[index];
11827
12359
  this.isChangingTrack = true;
11828
12360
  this.currentIndex = index;
12361
+ if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {
12362
+ const currentMediaType = this.player ? this.player.element.tagName === "AUDIO" ? "audio" : "video" : null;
12363
+ const newMediaType = this.getTrackMediaType(track);
12364
+ const newElementType = newMediaType === "audio" || newMediaType === "soundcloud" ? "audio" : "video";
12365
+ if (currentMediaType !== newElementType) {
12366
+ await this.recreatePlayerForTrack(track, false);
12367
+ this.updateTrackInfo(track);
12368
+ this.updatePlaylistUI();
12369
+ this.player.emit("playlisttrackchange", {
12370
+ index,
12371
+ item: track,
12372
+ total: this.tracks.length
12373
+ });
12374
+ setTimeout(() => {
12375
+ this.isChangingTrack = false;
12376
+ }, 150);
12377
+ return;
12378
+ }
12379
+ }
11829
12380
  this.player.load({
11830
12381
  src: track.src,
11831
12382
  type: track.type,
@@ -11850,7 +12401,7 @@
11850
12401
  * @param {number} index - Track index
11851
12402
  * @param {boolean} userInitiated - Whether this was triggered by user action (default: false)
11852
12403
  */
11853
- play(index, userInitiated = false) {
12404
+ async play(index, userInitiated = false) {
11854
12405
  if (index < 0 || index >= this.tracks.length) {
11855
12406
  console.warn("VidPly Playlist: Invalid track index", index);
11856
12407
  return;
@@ -11858,6 +12409,25 @@
11858
12409
  const track = this.tracks[index];
11859
12410
  this.isChangingTrack = true;
11860
12411
  this.currentIndex = index;
12412
+ if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {
12413
+ const currentMediaType = this.player ? this.player.element.tagName === "AUDIO" ? "audio" : "video" : null;
12414
+ const newMediaType = this.getTrackMediaType(track);
12415
+ const newElementType = newMediaType === "audio" || newMediaType === "soundcloud" ? "audio" : "video";
12416
+ if (currentMediaType !== newElementType) {
12417
+ await this.recreatePlayerForTrack(track, true);
12418
+ this.updateTrackInfo(track);
12419
+ this.updatePlaylistUI();
12420
+ this.player.emit("playlisttrackchange", {
12421
+ index,
12422
+ item: track,
12423
+ total: this.tracks.length
12424
+ });
12425
+ setTimeout(() => {
12426
+ this.isChangingTrack = false;
12427
+ }, 150);
12428
+ return;
12429
+ }
12430
+ }
11861
12431
  this.player.load({
11862
12432
  src: track.src,
11863
12433
  type: track.type,
@@ -11919,10 +12489,26 @@
11919
12489
  this.next();
11920
12490
  }
11921
12491
  }
12492
+ /**
12493
+ * Check if a source URL requires an external renderer
12494
+ * @param {string} src - Source URL
12495
+ * @returns {boolean}
12496
+ */
12497
+ isExternalRendererUrl(src) {
12498
+ if (!src) return false;
12499
+ return src.includes("youtube.com") || src.includes("youtu.be") || src.includes("vimeo.com") || src.includes("soundcloud.com") || src.includes("api.soundcloud.com") || src.includes(".m3u8");
12500
+ }
11922
12501
  /**
11923
12502
  * Handle track error
11924
12503
  */
11925
12504
  handleTrackError(e) {
12505
+ const currentTrack = this.getCurrentTrack();
12506
+ if (currentTrack && currentTrack.src && this.isExternalRendererUrl(currentTrack.src)) {
12507
+ return;
12508
+ }
12509
+ if (this.isChangingTrack) {
12510
+ return;
12511
+ }
11926
12512
  console.error("VidPly Playlist: Track error", e);
11927
12513
  if (this.options.autoAdvance) {
11928
12514
  setTimeout(() => {