vidply 1.0.25 → 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 (46) hide show
  1. package/README.md +5 -0
  2. package/dist/dev/{vidply.HLSRenderer-PNP5OPES.js → vidply.HLSRenderer-ENLZE4QS.js} +19 -8
  3. package/dist/dev/vidply.HLSRenderer-ENLZE4QS.js.map +7 -0
  4. package/dist/dev/{vidply.HTML5Renderer-LXQ3I45Q.js → vidply.HTML5Renderer-6SBDI6S2.js} +2 -2
  5. package/dist/dev/vidply.SoundCloudRenderer-CD7VJKNS.js +280 -0
  6. package/dist/dev/vidply.SoundCloudRenderer-CD7VJKNS.js.map +7 -0
  7. package/dist/dev/{vidply.VimeoRenderer-DCETT5IZ.js → vidply.VimeoRenderer-VPH4RNES.js} +17 -4
  8. package/dist/dev/vidply.VimeoRenderer-VPH4RNES.js.map +7 -0
  9. package/dist/dev/{vidply.YouTubeRenderer-QLMMD757.js → vidply.YouTubeRenderer-6MGKEFTZ.js} +14 -6
  10. package/dist/dev/vidply.YouTubeRenderer-6MGKEFTZ.js.map +7 -0
  11. package/dist/dev/{vidply.chunk-UEIJOJH6.js → vidply.chunk-BCOFCT6U.js} +4 -1
  12. package/dist/dev/vidply.chunk-BCOFCT6U.js.map +7 -0
  13. package/dist/dev/vidply.esm.js +292 -13
  14. package/dist/dev/vidply.esm.js.map +2 -2
  15. package/dist/legacy/vidply.js +619 -24
  16. package/dist/legacy/vidply.js.map +3 -3
  17. package/dist/legacy/vidply.min.js +1 -1
  18. package/dist/legacy/vidply.min.meta.json +26 -13
  19. package/dist/prod/vidply.HLSRenderer-CBXZ4RF2.min.js +6 -0
  20. package/dist/prod/{vidply.HTML5Renderer-XJCSUETP.min.js → vidply.HTML5Renderer-MY7XDV7R.min.js} +1 -1
  21. package/dist/prod/vidply.SoundCloudRenderer-MOR2CUFH.min.js +6 -0
  22. package/dist/prod/vidply.VimeoRenderer-3HBMM2WR.min.js +6 -0
  23. package/dist/prod/vidply.YouTubeRenderer-MFC2GMAC.min.js +6 -0
  24. package/dist/prod/vidply.chunk-OXXPY2XB.min.js +6 -0
  25. package/dist/prod/vidply.esm.min.js +6 -6
  26. package/dist/vidply.css +52 -1
  27. package/dist/vidply.esm.min.meta.json +56 -28
  28. package/dist/vidply.min.css +2 -2
  29. package/package.json +1 -1
  30. package/src/core/Player.js +124 -10
  31. package/src/features/PlaylistManager.js +312 -4
  32. package/src/renderers/HLSRenderer.js +17 -9
  33. package/src/renderers/HTML5Renderer.js +5 -0
  34. package/src/renderers/SoundCloudRenderer.js +355 -0
  35. package/src/renderers/VimeoRenderer.js +20 -4
  36. package/src/renderers/YouTubeRenderer.js +12 -6
  37. package/src/styles/vidply.css +51 -0
  38. package/dist/dev/vidply.HLSRenderer-PNP5OPES.js.map +0 -7
  39. package/dist/dev/vidply.VimeoRenderer-DCETT5IZ.js.map +0 -7
  40. package/dist/dev/vidply.YouTubeRenderer-QLMMD757.js.map +0 -7
  41. package/dist/dev/vidply.chunk-UEIJOJH6.js.map +0 -7
  42. package/dist/prod/vidply.HLSRenderer-4PW35TCX.min.js +0 -6
  43. package/dist/prod/vidply.VimeoRenderer-P3PU27S7.min.js +0 -6
  44. package/dist/prod/vidply.YouTubeRenderer-DGKKWB5M.min.js +0 -6
  45. package/dist/prod/vidply.chunk-BQBGEJF7.min.js +0 -6
  46. /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);
@@ -5085,6 +5114,12 @@
5085
5114
  }
5086
5115
  return [];
5087
5116
  }
5117
+ getCurrentQuality() {
5118
+ if (this.hls) {
5119
+ return this.hls.currentLevel;
5120
+ }
5121
+ return -1;
5122
+ }
5088
5123
  destroy() {
5089
5124
  if (this.hls) {
5090
5125
  this.hls.destroy();
@@ -5095,6 +5130,287 @@
5095
5130
  }
5096
5131
  });
5097
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
+
5098
5414
  // src/utils/EventEmitter.js
5099
5415
  var EventEmitter = class {
5100
5416
  constructor() {
@@ -8174,6 +8490,7 @@
8174
8490
  this.element.appendChild(mediaElement);
8175
8491
  this.element = mediaElement;
8176
8492
  }
8493
+ this._originalElement = this.element;
8177
8494
  this.options = __spreadValues({
8178
8495
  // Display
8179
8496
  width: null,
@@ -8447,7 +8764,7 @@
8447
8764
  }
8448
8765
  /**
8449
8766
  * Detect language from HTML lang attribute
8450
- * @returns {string|null} Language code if available in translations, null otherwise
8767
+ * @returns {string|null} Language code if available in translations or as built-in, null otherwise
8451
8768
  */
8452
8769
  detectHtmlLanguage() {
8453
8770
  const htmlLang = document.documentElement.lang || document.documentElement.getAttribute("lang");
@@ -8458,6 +8775,9 @@
8458
8775
  if (i18n.translations[normalizedLang]) {
8459
8776
  return normalizedLang;
8460
8777
  }
8778
+ if (i18n.builtInLanguageLoaders && i18n.builtInLanguageLoaders[normalizedLang]) {
8779
+ return normalizedLang;
8780
+ }
8461
8781
  this.log('Language "'.concat(htmlLang, '" not available, using English as fallback'));
8462
8782
  return null;
8463
8783
  }
@@ -8585,10 +8905,12 @@
8585
8905
  }
8586
8906
  async initializeRenderer() {
8587
8907
  var _a;
8588
- 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);
8589
8909
  if (!src) {
8590
8910
  throw new Error("No media source found");
8591
8911
  }
8912
+ this.currentSource = src;
8913
+ this._pendingSource = null;
8592
8914
  const sourceElements = this.sourceElements;
8593
8915
  for (const sourceEl of sourceElements) {
8594
8916
  const descSrc = sourceEl.getAttribute("data-desc-src");
@@ -8649,6 +8971,9 @@
8649
8971
  } else if (src.includes(".m3u8")) {
8650
8972
  const module = await Promise.resolve().then(() => (init_HLSRenderer(), HLSRenderer_exports));
8651
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;
8652
8977
  }
8653
8978
  this.log("Using ".concat((rendererClass == null ? void 0 : rendererClass.name) || "HTML5Renderer", " renderer"));
8654
8979
  this.renderer = new rendererClass(this);
@@ -8760,6 +9085,11 @@
8760
9085
  const resolvedPoster = this.resolvePosterPath(poster);
8761
9086
  this.videoWrapper.style.setProperty("--vidply-poster-image", 'url("'.concat(resolvedPoster, '")'));
8762
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
+ }
8763
9093
  }
8764
9094
  hidePosterOverlay() {
8765
9095
  if (!this.videoWrapper) {
@@ -8802,6 +9132,15 @@
8802
9132
  * @param {string} [config.audioDescriptionSrc] - Audio description video URL
8803
9133
  * @param {string} [config.signLanguageSrc] - Sign language video URL
8804
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
+ }
8805
9144
  async load(config) {
8806
9145
  var _a;
8807
9146
  try {
@@ -8814,12 +9153,44 @@
8814
9153
  const existingTracks = this.trackElements;
8815
9154
  existingTracks.forEach((track) => track.remove());
8816
9155
  this.invalidateTrackCache();
8817
- this.element.src = config.src;
8818
- if (config.type) {
8819
- 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
+ }
8820
9178
  }
8821
9179
  if (config.poster && this.element.tagName === "VIDEO") {
8822
- 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
+ }
8823
9194
  }
8824
9195
  if (config.tracks && config.tracks.length > 0) {
8825
9196
  config.tracks.forEach((trackConfig) => {
@@ -8862,6 +9233,13 @@
8862
9233
  this.renderer.media = this.element;
8863
9234
  this.element.load();
8864
9235
  }
9236
+ if (isExternalRenderer) {
9237
+ setTimeout(() => {
9238
+ this._switchingRenderer = false;
9239
+ }, 500);
9240
+ } else {
9241
+ this._switchingRenderer = false;
9242
+ }
8865
9243
  window.scrollTo(scrollX, scrollY);
8866
9244
  if (this.captionManager) {
8867
9245
  this.captionManager.destroy();
@@ -8920,11 +9298,13 @@
8920
9298
  const isYouTube = src.includes("youtube.com") || src.includes("youtu.be");
8921
9299
  const isVimeo = src.includes("vimeo.com");
8922
9300
  const isHLS = src.includes(".m3u8");
9301
+ const isSoundCloud = src.includes("soundcloud.com") || src.includes("api.soundcloud.com");
8923
9302
  const currentRendererName = this.renderer.constructor.name;
8924
9303
  if (isYouTube && currentRendererName !== "YouTubeRenderer") return true;
8925
9304
  if (isVimeo && currentRendererName !== "VimeoRenderer") return true;
8926
9305
  if (isHLS && currentRendererName !== "HLSRenderer") return true;
8927
- 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;
8928
9308
  return false;
8929
9309
  }
8930
9310
  // Playback controls
@@ -11139,6 +11519,10 @@
11139
11519
  }
11140
11520
  // Error handling
11141
11521
  handleError(error) {
11522
+ if (this._switchingRenderer) {
11523
+ this.log("Suppressing error during renderer switch:", error, "debug");
11524
+ return;
11525
+ }
11142
11526
  this.log("Error:", error, "error");
11143
11527
  this.emit("error", error);
11144
11528
  if (this.options.onError) {
@@ -11682,7 +12066,9 @@
11682
12066
  autoPlayFirst: options.autoPlayFirst !== false,
11683
12067
  // Default true - auto-play first track on load
11684
12068
  loop: options.loop || false,
11685
- showPanel: options.showPanel !== false
12069
+ showPanel: options.showPanel !== false,
12070
+ // Default true
12071
+ recreatePlayers: options.recreatePlayers || false
11686
12072
  }, options);
11687
12073
  this.container = null;
11688
12074
  this.playlistPanel = null;
@@ -11690,6 +12076,8 @@
11690
12076
  this.navigationFeedback = null;
11691
12077
  this.isPanelVisible = this.options.showPanel !== false;
11692
12078
  this.isChangingTrack = false;
12079
+ this.hostElement = options.hostElement || null;
12080
+ this.PlayerClass = options.PlayerClass || null;
11693
12081
  this.handleTrackEnd = this.handleTrackEnd.bind(this);
11694
12082
  this.handleTrackError = this.handleTrackError.bind(this);
11695
12083
  this.player.playlistManager = this;
@@ -11699,6 +12087,159 @@
11699
12087
  this.loadPlaylist(this.initialTracks);
11700
12088
  }
11701
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
+ }
11702
12243
  init() {
11703
12244
  this.player.on("ended", this.handleTrackEnd);
11704
12245
  this.player.on("error", this.handleTrackError);
@@ -11809,7 +12350,7 @@
11809
12350
  * Load a track without playing
11810
12351
  * @param {number} index - Track index
11811
12352
  */
11812
- loadTrack(index) {
12353
+ async loadTrack(index) {
11813
12354
  if (index < 0 || index >= this.tracks.length) {
11814
12355
  console.warn("VidPly Playlist: Invalid track index", index);
11815
12356
  return;
@@ -11817,6 +12358,25 @@
11817
12358
  const track = this.tracks[index];
11818
12359
  this.isChangingTrack = true;
11819
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
+ }
11820
12380
  this.player.load({
11821
12381
  src: track.src,
11822
12382
  type: track.type,
@@ -11841,7 +12401,7 @@
11841
12401
  * @param {number} index - Track index
11842
12402
  * @param {boolean} userInitiated - Whether this was triggered by user action (default: false)
11843
12403
  */
11844
- play(index, userInitiated = false) {
12404
+ async play(index, userInitiated = false) {
11845
12405
  if (index < 0 || index >= this.tracks.length) {
11846
12406
  console.warn("VidPly Playlist: Invalid track index", index);
11847
12407
  return;
@@ -11849,6 +12409,25 @@
11849
12409
  const track = this.tracks[index];
11850
12410
  this.isChangingTrack = true;
11851
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
+ }
11852
12431
  this.player.load({
11853
12432
  src: track.src,
11854
12433
  type: track.type,
@@ -11910,10 +12489,26 @@
11910
12489
  this.next();
11911
12490
  }
11912
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
+ }
11913
12501
  /**
11914
12502
  * Handle track error
11915
12503
  */
11916
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
+ }
11917
12512
  console.error("VidPly Playlist: Track error", e);
11918
12513
  if (this.options.autoAdvance) {
11919
12514
  setTimeout(() => {