vidply 1.0.26 → 1.0.28

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 (87) 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.TranscriptManager-QSF2PWUN.js +1744 -0
  7. package/dist/dev/vidply.TranscriptManager-QSF2PWUN.js.map +7 -0
  8. package/dist/dev/vidply.TranscriptManager-UTJBQC5B.js +1744 -0
  9. package/dist/dev/vidply.TranscriptManager-UTJBQC5B.js.map +7 -0
  10. package/dist/dev/{vidply.VimeoRenderer-DCETT5IZ.js → vidply.VimeoRenderer-VPH4RNES.js} +17 -4
  11. package/dist/dev/vidply.VimeoRenderer-VPH4RNES.js.map +7 -0
  12. package/dist/dev/{vidply.YouTubeRenderer-QLMMD757.js → vidply.YouTubeRenderer-6MGKEFTZ.js} +14 -6
  13. package/dist/dev/vidply.YouTubeRenderer-6MGKEFTZ.js.map +7 -0
  14. package/dist/dev/vidply.chunk-5663PYKK.js +1631 -0
  15. package/dist/dev/vidply.chunk-5663PYKK.js.map +7 -0
  16. package/dist/dev/{vidply.chunk-UEIJOJH6.js → vidply.chunk-BCOFCT6U.js} +4 -1
  17. package/dist/dev/vidply.chunk-BCOFCT6U.js.map +7 -0
  18. package/dist/dev/vidply.chunk-SRM7VNHG.js +1638 -0
  19. package/dist/dev/vidply.chunk-SRM7VNHG.js.map +7 -0
  20. package/dist/dev/vidply.de-RXAJM5QE.js +181 -0
  21. package/dist/dev/vidply.de-RXAJM5QE.js.map +7 -0
  22. package/dist/dev/vidply.de-SNL6AJ4D.js +188 -0
  23. package/dist/dev/vidply.de-SNL6AJ4D.js.map +7 -0
  24. package/dist/dev/vidply.es-2QCQKZ4U.js +188 -0
  25. package/dist/dev/vidply.es-2QCQKZ4U.js.map +7 -0
  26. package/dist/dev/vidply.es-SADVLJTQ.js +181 -0
  27. package/dist/dev/vidply.es-SADVLJTQ.js.map +7 -0
  28. package/dist/dev/vidply.esm.js +297 -21
  29. package/dist/dev/vidply.esm.js.map +2 -2
  30. package/dist/dev/vidply.fr-FJAZRL4L.js +188 -0
  31. package/dist/dev/vidply.fr-FJAZRL4L.js.map +7 -0
  32. package/dist/dev/vidply.fr-V3VAYBBT.js +181 -0
  33. package/dist/dev/vidply.fr-V3VAYBBT.js.map +7 -0
  34. package/dist/dev/vidply.ja-2XQOW53T.js +188 -0
  35. package/dist/dev/vidply.ja-2XQOW53T.js.map +7 -0
  36. package/dist/dev/vidply.ja-KL2TLZGJ.js +181 -0
  37. package/dist/dev/vidply.ja-KL2TLZGJ.js.map +7 -0
  38. package/dist/legacy/vidply.js +662 -36
  39. package/dist/legacy/vidply.js.map +3 -3
  40. package/dist/legacy/vidply.min.js +1 -1
  41. package/dist/legacy/vidply.min.meta.json +38 -25
  42. package/dist/prod/vidply.HLSRenderer-CBXZ4RF2.min.js +6 -0
  43. package/dist/prod/{vidply.HTML5Renderer-XJCSUETP.min.js → vidply.HTML5Renderer-MY7XDV7R.min.js} +1 -1
  44. package/dist/prod/vidply.SoundCloudRenderer-MOR2CUFH.min.js +6 -0
  45. package/dist/prod/vidply.TranscriptManager-DZ2WZU3K.min.js +6 -0
  46. package/dist/prod/vidply.TranscriptManager-E5QHGFIR.min.js +6 -0
  47. package/dist/prod/vidply.VimeoRenderer-3HBMM2WR.min.js +6 -0
  48. package/dist/prod/vidply.YouTubeRenderer-MFC2GMAC.min.js +6 -0
  49. package/dist/prod/vidply.chunk-5DWTMWEO.min.js +6 -0
  50. package/dist/prod/vidply.chunk-IBNYTGGM.min.js +6 -0
  51. package/dist/prod/vidply.chunk-OXXPY2XB.min.js +6 -0
  52. package/dist/prod/vidply.de-FR3XX54P.min.js +6 -0
  53. package/dist/prod/vidply.de-HGJBCLLE.min.js +6 -0
  54. package/dist/prod/vidply.es-3IJCQLJ7.min.js +6 -0
  55. package/dist/prod/vidply.es-CZEBXCZN.min.js +6 -0
  56. package/dist/prod/vidply.esm.min.js +5 -5
  57. package/dist/prod/vidply.fr-HFOL7MWA.min.js +6 -0
  58. package/dist/prod/vidply.fr-NC4VEAPH.min.js +6 -0
  59. package/dist/prod/vidply.ja-4ZC6ZQLV.min.js +6 -0
  60. package/dist/prod/vidply.ja-QTVU5C25.min.js +6 -0
  61. package/dist/vidply.css +51 -0
  62. package/dist/vidply.esm.min.meta.json +87 -59
  63. package/dist/vidply.min.css +1 -1
  64. package/package.json +1 -1
  65. package/src/controls/TranscriptManager.js +1 -1
  66. package/src/core/Player.js +117 -8
  67. package/src/features/PlaylistManager.js +324 -16
  68. package/src/i18n/languages/de.js +9 -1
  69. package/src/i18n/languages/en.js +9 -1
  70. package/src/i18n/languages/es.js +9 -1
  71. package/src/i18n/languages/fr.js +9 -1
  72. package/src/i18n/languages/ja.js +9 -1
  73. package/src/renderers/HLSRenderer.js +17 -9
  74. package/src/renderers/HTML5Renderer.js +5 -0
  75. package/src/renderers/SoundCloudRenderer.js +355 -0
  76. package/src/renderers/VimeoRenderer.js +20 -4
  77. package/src/renderers/YouTubeRenderer.js +12 -6
  78. package/src/styles/vidply.css +51 -0
  79. package/dist/dev/vidply.HLSRenderer-X46P47LY.js.map +0 -7
  80. package/dist/dev/vidply.VimeoRenderer-DCETT5IZ.js.map +0 -7
  81. package/dist/dev/vidply.YouTubeRenderer-QLMMD757.js.map +0 -7
  82. package/dist/dev/vidply.chunk-UEIJOJH6.js.map +0 -7
  83. package/dist/prod/vidply.HLSRenderer-LDXSMWTI.min.js +0 -6
  84. package/dist/prod/vidply.VimeoRenderer-P3PU27S7.min.js +0 -6
  85. package/dist/prod/vidply.YouTubeRenderer-DGKKWB5M.min.js +0 -6
  86. package/dist/prod/vidply.chunk-BQBGEJF7.min.js +0 -6
  87. /package/dist/dev/{vidply.HTML5Renderer-LXQ3I45Q.js.map → vidply.HTML5Renderer-6SBDI6S2.js.map} +0 -0
@@ -307,6 +307,7 @@
307
307
  },
308
308
  transcript: {
309
309
  title: "Transcript",
310
+ ariaLabel: "Video Transcript",
310
311
  close: "Close transcript",
311
312
  loading: "Loading transcript...",
312
313
  noTranscript: "No transcript available for this video.",
@@ -373,7 +374,14 @@
373
374
  currentlyPlaying: "Currently playing",
374
375
  notPlaying: "Not playing",
375
376
  pressEnterPlay: "Press Enter to play",
376
- pressEnterRestart: "Press Enter to restart"
377
+ pressEnterRestart: "Press Enter to restart",
378
+ keyboardInstructions: "Playlist navigation: Use Up and Down arrow keys to move between tracks. Press Page Up or Page Down to skip 5 tracks. Press Home to go to first track, End to go to last track. Press Enter or Space to play the selected track.",
379
+ endOfPlaylist: "End of playlist. {current} of {total}.",
380
+ beginningOfPlaylist: "Beginning of playlist. 1 of {total}.",
381
+ jumpedToLastTrack: "Jumped to last track. {current} of {total}.",
382
+ jumpedToFirstTrack: "Jumped to first track. 1 of {total}.",
383
+ firstTrack: "First track. 1 of {total}.",
384
+ lastTrack: "Last track. {current} of {total}."
377
385
  }
378
386
  };
379
387
  }
@@ -487,6 +495,7 @@
487
495
  },
488
496
  transcript: {
489
497
  title: "Transkript",
498
+ ariaLabel: "Video-Transkript",
490
499
  close: "Transkript schließen",
491
500
  loading: "Transkript wird geladen...",
492
501
  noTranscript: "Kein Transkript für dieses Video verfügbar.",
@@ -553,7 +562,14 @@
553
562
  currentlyPlaying: "Wird gerade abgespielt",
554
563
  notPlaying: "Nicht aktiv",
555
564
  pressEnterPlay: "Eingabetaste zum Abspielen",
556
- pressEnterRestart: "Eingabetaste zum Neustart"
565
+ pressEnterRestart: "Eingabetaste zum Neustart",
566
+ keyboardInstructions: "Wiedergabelisten-Navigation: Verwenden Sie die Pfeiltasten nach oben und unten, um zwischen Titeln zu wechseln. Drücken Sie Bild auf oder Bild ab, um 5 Titel zu überspringen. Drücken Sie Pos1, um zum ersten Titel zu springen, Ende für den letzten Titel. Drücken Sie die Eingabetaste oder Leertaste, um den ausgewählten Titel abzuspielen.",
567
+ endOfPlaylist: "Ende der Wiedergabeliste. {current} von {total}.",
568
+ beginningOfPlaylist: "Anfang der Wiedergabeliste. 1 von {total}.",
569
+ jumpedToLastTrack: "Zum letzten Titel gesprungen. {current} von {total}.",
570
+ jumpedToFirstTrack: "Zum ersten Titel gesprungen. 1 von {total}.",
571
+ firstTrack: "Erster Titel. 1 von {total}.",
572
+ lastTrack: "Letzter Titel. {current} von {total}."
557
573
  }
558
574
  };
559
575
  }
@@ -667,6 +683,7 @@
667
683
  },
668
684
  transcript: {
669
685
  title: "Transcripción",
686
+ ariaLabel: "Transcripción de video",
670
687
  close: "Cerrar transcripción",
671
688
  loading: "Cargando transcripción...",
672
689
  noTranscript: "No hay transcripción disponible para este video.",
@@ -733,7 +750,14 @@
733
750
  currentlyPlaying: "Reproduciendo actualmente",
734
751
  notPlaying: "Sin reproducir",
735
752
  pressEnterPlay: "Pulsa Enter para reproducir",
736
- pressEnterRestart: "Pulsa Enter para reiniciar"
753
+ pressEnterRestart: "Pulsa Enter para reiniciar",
754
+ keyboardInstructions: "Navegación de lista de reproducción: Use las teclas de flecha arriba y abajo para moverse entre pistas. Pulse Retroceder página o Avanzar página para saltar 5 pistas. Pulse Inicio para ir a la primera pista, Fin para la última pista. Pulse Intro o Espacio para reproducir la pista seleccionada.",
755
+ endOfPlaylist: "Fin de la lista de reproducción. {current} de {total}.",
756
+ beginningOfPlaylist: "Inicio de la lista de reproducción. 1 de {total}.",
757
+ jumpedToLastTrack: "Saltó a la última pista. {current} de {total}.",
758
+ jumpedToFirstTrack: "Saltó a la primera pista. 1 de {total}.",
759
+ firstTrack: "Primera pista. 1 de {total}.",
760
+ lastTrack: "Última pista. {current} de {total}."
737
761
  }
738
762
  };
739
763
  }
@@ -847,6 +871,7 @@
847
871
  },
848
872
  transcript: {
849
873
  title: "Transcription",
874
+ ariaLabel: "Transcription vidéo",
850
875
  close: "Fermer la transcription",
851
876
  loading: "Chargement de la transcription...",
852
877
  noTranscript: "Aucune transcription disponible pour cette vidéo.",
@@ -913,7 +938,14 @@
913
938
  currentlyPlaying: "En cours de lecture",
914
939
  notPlaying: "Non en lecture",
915
940
  pressEnterPlay: "Appuyez sur Entrée pour lire",
916
- pressEnterRestart: "Appuyez sur Entrée pour recommencer"
941
+ pressEnterRestart: "Appuyez sur Entrée pour recommencer",
942
+ keyboardInstructions: "Navigation de la liste de lecture : Utilisez les touches fléchées haut et bas pour naviguer entre les pistes. Appuyez sur Page précédente ou Page suivante pour sauter 5 pistes. Appuyez sur Début pour aller à la première piste, Fin pour la dernière piste. Appuyez sur Entrée ou Espace pour lire la piste sélectionnée.",
943
+ endOfPlaylist: "Fin de la liste de lecture. {current} sur {total}.",
944
+ beginningOfPlaylist: "Début de la liste de lecture. 1 sur {total}.",
945
+ jumpedToLastTrack: "Sauté à la dernière piste. {current} sur {total}.",
946
+ jumpedToFirstTrack: "Sauté à la première piste. 1 sur {total}.",
947
+ firstTrack: "Première piste. 1 sur {total}.",
948
+ lastTrack: "Dernière piste. {current} sur {total}."
917
949
  }
918
950
  };
919
951
  }
@@ -1027,6 +1059,7 @@
1027
1059
  },
1028
1060
  transcript: {
1029
1061
  title: "文字起こし",
1062
+ ariaLabel: "ビデオ文字起こし",
1030
1063
  close: "文字起こしを閉じる",
1031
1064
  loading: "文字起こしを読み込み中...",
1032
1065
  noTranscript: "このビデオの文字起こしはありません。",
@@ -1093,7 +1126,14 @@
1093
1126
  currentlyPlaying: "再生中",
1094
1127
  notPlaying: "停止中",
1095
1128
  pressEnterPlay: "Enterキーで再生",
1096
- pressEnterRestart: "Enterキーで最初から再生"
1129
+ pressEnterRestart: "Enterキーで最初から再生",
1130
+ keyboardInstructions: "プレイリストナビゲーション:上下の矢印キーでトラック間を移動します。Page UpまたはPage Downで5トラックをスキップします。Homeで最初のトラックへ、Endで最後のトラックへ移動します。EnterまたはSpaceで選択したトラックを再生します。",
1131
+ endOfPlaylist: "プレイリストの終わりです。{current}/{total}。",
1132
+ beginningOfPlaylist: "プレイリストの始めです。1/{total}。",
1133
+ jumpedToLastTrack: "最後のトラックにジャンプしました。{current}/{total}。",
1134
+ jumpedToFirstTrack: "最初のトラックにジャンプしました。1/{total}。",
1135
+ firstTrack: "最初のトラックです。1/{total}。",
1136
+ lastTrack: "最後のトラックです。{current}/{total}。"
1097
1137
  }
1098
1138
  };
1099
1139
  }
@@ -1647,6 +1687,9 @@
1647
1687
  this.attachEvents();
1648
1688
  this.media.preload = this.player.options.preload;
1649
1689
  this.media.load();
1690
+ if (this.player.container) {
1691
+ this.player.container.classList.remove("vidply-external-controls");
1692
+ }
1650
1693
  }
1651
1694
  attachEvents() {
1652
1695
  this.media.addEventListener("loadedmetadata", () => {
@@ -2821,7 +2864,7 @@
2821
2864
  className: "".concat(this.player.options.classPrefix, "-transcript-window"),
2822
2865
  attributes: {
2823
2866
  "role": "dialog",
2824
- "aria-label": "Video Transcript",
2867
+ "aria-label": i18n.t("transcript.ariaLabel"),
2825
2868
  "tabindex": "-1"
2826
2869
  }
2827
2870
  });
@@ -4417,7 +4460,8 @@
4417
4460
  this.iframe = null;
4418
4461
  }
4419
4462
  async init() {
4420
- this.videoId = this.extractVideoId(this.player.element.src);
4463
+ const src = this.player.currentSource || this.player.element.src;
4464
+ this.videoId = this.extractVideoId(src);
4421
4465
  if (!this.videoId) {
4422
4466
  throw new Error("Invalid YouTube URL");
4423
4467
  }
@@ -4466,7 +4510,8 @@
4466
4510
  this.iframe = document.createElement("div");
4467
4511
  this.iframe.id = "youtube-player-".concat(Math.random().toString(36).substr(2, 9));
4468
4512
  this.iframe.style.width = "100%";
4469
- this.iframe.style.height = "100%";
4513
+ this.iframe.style.aspectRatio = "16 / 9";
4514
+ this.iframe.style.maxHeight = "100%";
4470
4515
  this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
4471
4516
  }
4472
4517
  async initializePlayer() {
@@ -4476,9 +4521,12 @@
4476
4521
  width: "100%",
4477
4522
  height: "100%",
4478
4523
  playerVars: {
4479
- controls: 0,
4480
- disablekb: 1,
4481
- fs: 0,
4524
+ controls: 1,
4525
+ // Use YouTube native controls
4526
+ disablekb: 0,
4527
+ // Allow keyboard controls
4528
+ fs: 1,
4529
+ // Allow fullscreen
4482
4530
  modestbranding: 1,
4483
4531
  rel: 0,
4484
4532
  showinfo: 0,
@@ -4491,6 +4539,9 @@
4491
4539
  onReady: (event) => {
4492
4540
  this.isReady = true;
4493
4541
  this.attachEvents();
4542
+ if (this.player.container) {
4543
+ this.player.container.classList.add("vidply-external-controls");
4544
+ }
4494
4545
  resolve();
4495
4546
  },
4496
4547
  onStateChange: (event) => this.handleStateChange(event),
@@ -4644,7 +4695,8 @@
4644
4695
  this.iframe = null;
4645
4696
  }
4646
4697
  async init() {
4647
- this.videoId = this.extractVideoId(this.player.element.src);
4698
+ const src = this.player.currentSource || this.player.element.src;
4699
+ this.videoId = this.extractVideoId(src);
4648
4700
  if (!this.videoId) {
4649
4701
  throw new Error("Invalid Vimeo URL");
4650
4702
  }
@@ -4683,7 +4735,8 @@
4683
4735
  this.iframe = document.createElement("div");
4684
4736
  this.iframe.id = "vimeo-player-".concat(Math.random().toString(36).substr(2, 9));
4685
4737
  this.iframe.style.width = "100%";
4686
- this.iframe.style.height = "100%";
4738
+ this.iframe.style.aspectRatio = "16 / 9";
4739
+ this.iframe.style.maxHeight = "100%";
4687
4740
  this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
4688
4741
  }
4689
4742
  async initializePlayer() {
@@ -4691,7 +4744,8 @@
4691
4744
  id: this.videoId,
4692
4745
  width: "100%",
4693
4746
  height: "100%",
4694
- controls: false,
4747
+ controls: true,
4748
+ // Use Vimeo native controls
4695
4749
  autoplay: this.player.options.autoplay,
4696
4750
  muted: this.player.options.muted,
4697
4751
  loop: this.player.options.loop,
@@ -4703,6 +4757,16 @@
4703
4757
  this.vimeo = new window.Vimeo.Player(this.iframe.id, options);
4704
4758
  await this.vimeo.ready();
4705
4759
  this.isReady = true;
4760
+ const vimeoIframe = this.iframe.querySelector("iframe");
4761
+ if (vimeoIframe) {
4762
+ vimeoIframe.style.width = "100%";
4763
+ vimeoIframe.style.height = "100%";
4764
+ vimeoIframe.setAttribute("width", "100%");
4765
+ vimeoIframe.setAttribute("height", "100%");
4766
+ }
4767
+ if (this.player.container) {
4768
+ this.player.container.classList.add("vidply-external-controls");
4769
+ }
4706
4770
  this.attachEvents();
4707
4771
  try {
4708
4772
  const duration = await this.vimeo.getDuration();
@@ -4915,12 +4979,14 @@
4915
4979
  fragLoadingMaxRetryTimeout: 64e3
4916
4980
  });
4917
4981
  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;
4982
+ let src = this.player.currentSource;
4983
+ if (!src) {
4984
+ const sourceElement = this.player.element.querySelector("source");
4985
+ if (sourceElement) {
4986
+ src = sourceElement.getAttribute("src");
4987
+ } else {
4988
+ src = this.player.element.getAttribute("src") || this.player.element.src;
4989
+ }
4924
4990
  }
4925
4991
  this.player.log("Loading HLS source: ".concat(src), "log");
4926
4992
  if (!src) {
@@ -4943,6 +5009,9 @@
4943
5009
  this.hls.on(window.Hls.Events.MANIFEST_PARSED, (event, data) => {
4944
5010
  this.player.log("HLS manifest loaded, found " + data.levels.length + " quality levels");
4945
5011
  this.player.emit("hlsmanifestparsed", data);
5012
+ if (this.player.container) {
5013
+ this.player.container.classList.remove("vidply-external-controls");
5014
+ }
4946
5015
  });
4947
5016
  this.hls.on(window.Hls.Events.LEVEL_SWITCHED, (event, data) => {
4948
5017
  this.player.log("HLS level switched to " + data.level);
@@ -5101,6 +5170,287 @@
5101
5170
  }
5102
5171
  });
5103
5172
 
5173
+ // src/renderers/SoundCloudRenderer.js
5174
+ var SoundCloudRenderer_exports = {};
5175
+ __export(SoundCloudRenderer_exports, {
5176
+ SoundCloudRenderer: () => SoundCloudRenderer,
5177
+ default: () => SoundCloudRenderer_default
5178
+ });
5179
+ var SoundCloudRenderer, SoundCloudRenderer_default;
5180
+ var init_SoundCloudRenderer = __esm({
5181
+ "src/renderers/SoundCloudRenderer.js"() {
5182
+ SoundCloudRenderer = class {
5183
+ constructor(player) {
5184
+ this.player = player;
5185
+ this.widget = null;
5186
+ this.trackUrl = null;
5187
+ this.isReady = false;
5188
+ this.iframe = null;
5189
+ this.iframeId = null;
5190
+ }
5191
+ async init() {
5192
+ var _a;
5193
+ this.trackUrl = this.player.currentSource || this.player.element.src || ((_a = this.player.element.querySelector("source")) == null ? void 0 : _a.src);
5194
+ if (!this.trackUrl || !this.isValidSoundCloudUrl(this.trackUrl)) {
5195
+ throw new Error("Invalid SoundCloud URL");
5196
+ }
5197
+ await this.loadSoundCloudAPI();
5198
+ this.createIframe();
5199
+ await this.initializeWidget();
5200
+ }
5201
+ /**
5202
+ * Validate SoundCloud URL
5203
+ * @param {string} url
5204
+ * @returns {boolean}
5205
+ */
5206
+ isValidSoundCloudUrl(url) {
5207
+ return url.includes("soundcloud.com") || url.includes("api.soundcloud.com");
5208
+ }
5209
+ /**
5210
+ * Check if URL is a playlist/set
5211
+ */
5212
+ isPlaylist() {
5213
+ return this.trackUrl && this.trackUrl.includes("/sets/");
5214
+ }
5215
+ /**
5216
+ * Extract track/playlist info from URL for embed
5217
+ * SoundCloud URLs can be:
5218
+ * - https://soundcloud.com/artist/track
5219
+ * - https://soundcloud.com/artist/sets/playlist
5220
+ * - https://api.soundcloud.com/tracks/123456
5221
+ */
5222
+ getEmbedUrl() {
5223
+ const encodedUrl = encodeURIComponent(this.trackUrl);
5224
+ const params = new URLSearchParams({
5225
+ url: this.trackUrl,
5226
+ auto_play: this.player.options.autoplay ? "true" : "false",
5227
+ hide_related: "true",
5228
+ show_comments: "false",
5229
+ show_user: "true",
5230
+ show_reposts: "false",
5231
+ show_teaser: "false",
5232
+ visual: "false",
5233
+ // Use classic player for better control
5234
+ color: "%23007bff"
5235
+ });
5236
+ return "https://w.soundcloud.com/player/?".concat(params.toString());
5237
+ }
5238
+ async loadSoundCloudAPI() {
5239
+ if (window.SC && window.SC.Widget) {
5240
+ return Promise.resolve();
5241
+ }
5242
+ return new Promise((resolve, reject) => {
5243
+ const script = document.createElement("script");
5244
+ script.src = "https://w.soundcloud.com/player/api.js";
5245
+ script.onload = () => {
5246
+ setTimeout(() => {
5247
+ if (window.SC && window.SC.Widget) {
5248
+ resolve();
5249
+ } else {
5250
+ reject(new Error("SoundCloud Widget API not available"));
5251
+ }
5252
+ }, 100);
5253
+ };
5254
+ script.onerror = () => reject(new Error("Failed to load SoundCloud Widget API"));
5255
+ document.head.appendChild(script);
5256
+ });
5257
+ }
5258
+ createIframe() {
5259
+ this.player.element.style.display = "none";
5260
+ this.player.element.removeAttribute("poster");
5261
+ if (this.player.videoWrapper) {
5262
+ this.player.videoWrapper.classList.remove("vidply-forced-poster");
5263
+ this.player.videoWrapper.style.removeProperty("--vidply-poster-image");
5264
+ }
5265
+ this.iframeId = "soundcloud-player-".concat(Math.random().toString(36).substr(2, 9));
5266
+ this.iframe = document.createElement("iframe");
5267
+ this.iframe.id = this.iframeId;
5268
+ this.iframe.scrolling = "no";
5269
+ this.iframe.frameBorder = "no";
5270
+ this.iframe.allow = "autoplay";
5271
+ this.iframe.src = this.getEmbedUrl();
5272
+ this.iframe.style.width = "100%";
5273
+ this.iframe.style.display = "block";
5274
+ if (this.isPlaylist()) {
5275
+ this.iframe.style.aspectRatio = "16 / 9";
5276
+ this.iframe.classList.add("vidply-soundcloud-iframe", "vidply-soundcloud-playlist");
5277
+ } else {
5278
+ this.iframe.style.aspectRatio = "16 / 3";
5279
+ this.iframe.classList.add("vidply-soundcloud-iframe");
5280
+ }
5281
+ this.iframe.style.maxHeight = "100%";
5282
+ this.player.element.parentNode.insertBefore(this.iframe, this.player.element);
5283
+ }
5284
+ async initializeWidget() {
5285
+ return new Promise((resolve, reject) => {
5286
+ this.iframe.addEventListener("load", () => {
5287
+ try {
5288
+ this.widget = window.SC.Widget(this.iframe);
5289
+ this.widget.bind(window.SC.Widget.Events.READY, () => {
5290
+ this.isReady = true;
5291
+ this.attachEvents();
5292
+ if (this.player.container) {
5293
+ this.player.container.classList.add("vidply-external-controls");
5294
+ }
5295
+ this.widget.getCurrentSound((sound) => {
5296
+ if (sound) {
5297
+ this.player.state.duration = sound.duration / 1e3;
5298
+ this.player.emit("loadedmetadata");
5299
+ }
5300
+ });
5301
+ resolve();
5302
+ });
5303
+ this.widget.bind(window.SC.Widget.Events.ERROR, (error) => {
5304
+ this.player.handleError(new Error("SoundCloud error: ".concat(error.message || "Unknown error")));
5305
+ });
5306
+ } catch (error) {
5307
+ reject(error);
5308
+ }
5309
+ });
5310
+ setTimeout(() => {
5311
+ if (!this.isReady) {
5312
+ reject(new Error("SoundCloud widget initialization timeout"));
5313
+ }
5314
+ }, 1e4);
5315
+ });
5316
+ }
5317
+ attachEvents() {
5318
+ if (!this.widget) return;
5319
+ const Events = window.SC.Widget.Events;
5320
+ this.widget.bind(Events.PLAY, () => {
5321
+ this.player.state.playing = true;
5322
+ this.player.state.paused = false;
5323
+ this.player.state.ended = false;
5324
+ this.player.emit("play");
5325
+ if (this.player.options.onPlay) {
5326
+ this.player.options.onPlay.call(this.player);
5327
+ }
5328
+ });
5329
+ this.widget.bind(Events.PAUSE, () => {
5330
+ this.player.state.playing = false;
5331
+ this.player.state.paused = true;
5332
+ this.player.emit("pause");
5333
+ if (this.player.options.onPause) {
5334
+ this.player.options.onPause.call(this.player);
5335
+ }
5336
+ });
5337
+ this.widget.bind(Events.FINISH, () => {
5338
+ this.player.state.playing = false;
5339
+ this.player.state.paused = true;
5340
+ this.player.state.ended = true;
5341
+ this.player.emit("ended");
5342
+ if (this.player.options.onEnded) {
5343
+ this.player.options.onEnded.call(this.player);
5344
+ }
5345
+ if (this.player.options.loop) {
5346
+ this.seek(0);
5347
+ this.play();
5348
+ }
5349
+ });
5350
+ this.widget.bind(Events.PLAY_PROGRESS, (data) => {
5351
+ const currentTime = data.currentPosition / 1e3;
5352
+ this.player.state.currentTime = currentTime;
5353
+ this.player.emit("timeupdate", currentTime);
5354
+ if (this.player.options.onTimeUpdate) {
5355
+ this.player.options.onTimeUpdate.call(this.player, currentTime);
5356
+ }
5357
+ });
5358
+ this.widget.bind(Events.SEEK, (data) => {
5359
+ this.player.state.currentTime = data.currentPosition / 1e3;
5360
+ this.player.emit("seeked");
5361
+ });
5362
+ this.widget.bind(Events.LOAD_PROGRESS, (data) => {
5363
+ if (this.player.state.duration) {
5364
+ const buffered = data.loadedProgress * this.player.state.duration;
5365
+ this.player.emit("progress", buffered);
5366
+ }
5367
+ });
5368
+ }
5369
+ play() {
5370
+ if (this.isReady && this.widget) {
5371
+ const scrollX = window.scrollX;
5372
+ const scrollY = window.scrollY;
5373
+ this.widget.play();
5374
+ window.scrollTo(scrollX, scrollY);
5375
+ }
5376
+ }
5377
+ pause() {
5378
+ if (this.isReady && this.widget) {
5379
+ this.widget.pause();
5380
+ }
5381
+ }
5382
+ seek(time) {
5383
+ if (this.isReady && this.widget) {
5384
+ this.widget.seekTo(time * 1e3);
5385
+ this.player.state.currentTime = time;
5386
+ }
5387
+ }
5388
+ setVolume(volume) {
5389
+ if (this.isReady && this.widget) {
5390
+ this.widget.setVolume(volume * 100);
5391
+ this.player.state.volume = volume;
5392
+ }
5393
+ }
5394
+ setMuted(muted) {
5395
+ if (this.isReady && this.widget) {
5396
+ if (muted) {
5397
+ this.widget.getVolume((vol) => {
5398
+ this._previousVolume = vol;
5399
+ this.widget.setVolume(0);
5400
+ });
5401
+ } else {
5402
+ this.widget.setVolume(this._previousVolume || 100);
5403
+ }
5404
+ this.player.state.muted = muted;
5405
+ }
5406
+ }
5407
+ setPlaybackSpeed(speed) {
5408
+ this.player.log("SoundCloud does not support playback speed control", "warn");
5409
+ }
5410
+ /**
5411
+ * Get current track info
5412
+ * @returns {Promise<Object>}
5413
+ */
5414
+ getCurrentSound() {
5415
+ return new Promise((resolve) => {
5416
+ if (this.isReady && this.widget) {
5417
+ this.widget.getCurrentSound((sound) => {
5418
+ resolve(sound);
5419
+ });
5420
+ } else {
5421
+ resolve(null);
5422
+ }
5423
+ });
5424
+ }
5425
+ destroy() {
5426
+ if (this.widget) {
5427
+ const Events = window.SC.Widget.Events;
5428
+ try {
5429
+ this.widget.unbind(Events.READY);
5430
+ this.widget.unbind(Events.PLAY);
5431
+ this.widget.unbind(Events.PAUSE);
5432
+ this.widget.unbind(Events.FINISH);
5433
+ this.widget.unbind(Events.PLAY_PROGRESS);
5434
+ this.widget.unbind(Events.SEEK);
5435
+ this.widget.unbind(Events.LOAD_PROGRESS);
5436
+ this.widget.unbind(Events.ERROR);
5437
+ } catch (e) {
5438
+ }
5439
+ }
5440
+ if (this.iframe && this.iframe.parentNode) {
5441
+ this.iframe.parentNode.removeChild(this.iframe);
5442
+ }
5443
+ if (this.player.element) {
5444
+ this.player.element.style.display = "";
5445
+ }
5446
+ this.widget = null;
5447
+ this.isReady = false;
5448
+ }
5449
+ };
5450
+ SoundCloudRenderer_default = SoundCloudRenderer;
5451
+ }
5452
+ });
5453
+
5104
5454
  // src/utils/EventEmitter.js
5105
5455
  var EventEmitter = class {
5106
5456
  constructor() {
@@ -8180,6 +8530,7 @@
8180
8530
  this.element.appendChild(mediaElement);
8181
8531
  this.element = mediaElement;
8182
8532
  }
8533
+ this._originalElement = this.element;
8183
8534
  this.options = __spreadValues({
8184
8535
  // Display
8185
8536
  width: null,
@@ -8594,10 +8945,12 @@
8594
8945
  }
8595
8946
  async initializeRenderer() {
8596
8947
  var _a;
8597
- const src = this.element.src || ((_a = this.element.querySelector("source")) == null ? void 0 : _a.src);
8948
+ let src = this._pendingSource || this.element.src || ((_a = this.element.querySelector("source")) == null ? void 0 : _a.src);
8598
8949
  if (!src) {
8599
8950
  throw new Error("No media source found");
8600
8951
  }
8952
+ this.currentSource = src;
8953
+ this._pendingSource = null;
8601
8954
  const sourceElements = this.sourceElements;
8602
8955
  for (const sourceEl of sourceElements) {
8603
8956
  const descSrc = sourceEl.getAttribute("data-desc-src");
@@ -8658,6 +9011,9 @@
8658
9011
  } else if (src.includes(".m3u8")) {
8659
9012
  const module = await Promise.resolve().then(() => (init_HLSRenderer(), HLSRenderer_exports));
8660
9013
  rendererClass = module.HLSRenderer || module.default;
9014
+ } else if (src.includes("soundcloud.com") || src.includes("api.soundcloud.com")) {
9015
+ const module = await Promise.resolve().then(() => (init_SoundCloudRenderer(), SoundCloudRenderer_exports));
9016
+ rendererClass = module.SoundCloudRenderer || module.default;
8661
9017
  }
8662
9018
  this.log("Using ".concat((rendererClass == null ? void 0 : rendererClass.name) || "HTML5Renderer", " renderer"));
8663
9019
  this.renderer = new rendererClass(this);
@@ -8769,6 +9125,11 @@
8769
9125
  const resolvedPoster = this.resolvePosterPath(poster);
8770
9126
  this.videoWrapper.style.setProperty("--vidply-poster-image", 'url("'.concat(resolvedPoster, '")'));
8771
9127
  this.videoWrapper.classList.add("vidply-forced-poster");
9128
+ if (this._isAudioContent && this.container) {
9129
+ this.container.classList.add("vidply-audio-content");
9130
+ } else if (this.container) {
9131
+ this.container.classList.remove("vidply-audio-content");
9132
+ }
8772
9133
  }
8773
9134
  hidePosterOverlay() {
8774
9135
  if (!this.videoWrapper) {
@@ -8811,6 +9172,15 @@
8811
9172
  * @param {string} [config.audioDescriptionSrc] - Audio description video URL
8812
9173
  * @param {string} [config.signLanguageSrc] - Sign language video URL
8813
9174
  */
9175
+ /**
9176
+ * Check if a source URL requires an external renderer (YouTube, Vimeo, SoundCloud, HLS)
9177
+ * @param {string} src - Source URL
9178
+ * @returns {boolean}
9179
+ */
9180
+ isExternalRendererUrl(src) {
9181
+ if (!src) return false;
9182
+ 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");
9183
+ }
8814
9184
  async load(config) {
8815
9185
  var _a;
8816
9186
  try {
@@ -8823,12 +9193,44 @@
8823
9193
  const existingTracks = this.trackElements;
8824
9194
  existingTracks.forEach((track) => track.remove());
8825
9195
  this.invalidateTrackCache();
8826
- this.element.src = config.src;
8827
- if (config.type) {
8828
- this.element.type = config.type;
9196
+ const isExternalRenderer = this.isExternalRendererUrl(config.src);
9197
+ if (isExternalRenderer) {
9198
+ this._switchingRenderer = true;
9199
+ }
9200
+ if (!isExternalRenderer) {
9201
+ this.element.src = config.src;
9202
+ if (config.type) {
9203
+ this.element.type = config.type;
9204
+ }
9205
+ } else {
9206
+ this.element.removeAttribute("src");
9207
+ const sources = this.element.querySelectorAll("source");
9208
+ sources.forEach((s) => s.removeAttribute("src"));
9209
+ }
9210
+ this._pendingSource = config.src;
9211
+ this._isAudioContent = config.type && config.type.startsWith("audio/");
9212
+ if (this.container) {
9213
+ if (this._isAudioContent) {
9214
+ this.container.classList.add("vidply-audio-content");
9215
+ } else {
9216
+ this.container.classList.remove("vidply-audio-content");
9217
+ }
8829
9218
  }
8830
9219
  if (config.poster && this.element.tagName === "VIDEO") {
8831
- this.element.poster = this.resolvePosterPath(config.poster);
9220
+ if (this._isAudioContent) {
9221
+ this.element.removeAttribute("poster");
9222
+ if (this.videoWrapper) {
9223
+ const resolvedPoster = this.resolvePosterPath(config.poster);
9224
+ this.videoWrapper.style.setProperty("--vidply-poster-image", 'url("'.concat(resolvedPoster, '")'));
9225
+ this.videoWrapper.classList.add("vidply-forced-poster");
9226
+ }
9227
+ } else {
9228
+ this.element.poster = this.resolvePosterPath(config.poster);
9229
+ if (this.videoWrapper) {
9230
+ this.videoWrapper.classList.remove("vidply-forced-poster");
9231
+ this.videoWrapper.style.removeProperty("--vidply-poster-image");
9232
+ }
9233
+ }
8832
9234
  }
8833
9235
  if (config.tracks && config.tracks.length > 0) {
8834
9236
  config.tracks.forEach((trackConfig) => {
@@ -8871,6 +9273,13 @@
8871
9273
  this.renderer.media = this.element;
8872
9274
  this.element.load();
8873
9275
  }
9276
+ if (isExternalRenderer) {
9277
+ setTimeout(() => {
9278
+ this._switchingRenderer = false;
9279
+ }, 500);
9280
+ } else {
9281
+ this._switchingRenderer = false;
9282
+ }
8874
9283
  window.scrollTo(scrollX, scrollY);
8875
9284
  if (this.captionManager) {
8876
9285
  this.captionManager.destroy();
@@ -8929,11 +9338,13 @@
8929
9338
  const isYouTube = src.includes("youtube.com") || src.includes("youtu.be");
8930
9339
  const isVimeo = src.includes("vimeo.com");
8931
9340
  const isHLS = src.includes(".m3u8");
9341
+ const isSoundCloud = src.includes("soundcloud.com") || src.includes("api.soundcloud.com");
8932
9342
  const currentRendererName = this.renderer.constructor.name;
8933
9343
  if (isYouTube && currentRendererName !== "YouTubeRenderer") return true;
8934
9344
  if (isVimeo && currentRendererName !== "VimeoRenderer") return true;
8935
9345
  if (isHLS && currentRendererName !== "HLSRenderer") return true;
8936
- if (!isYouTube && !isVimeo && !isHLS && currentRendererName !== "HTML5Renderer") return true;
9346
+ if (isSoundCloud && currentRendererName !== "SoundCloudRenderer") return true;
9347
+ if (!isYouTube && !isVimeo && !isHLS && !isSoundCloud && currentRendererName !== "HTML5Renderer") return true;
8937
9348
  return false;
8938
9349
  }
8939
9350
  // Playback controls
@@ -11148,6 +11559,10 @@
11148
11559
  }
11149
11560
  // Error handling
11150
11561
  handleError(error) {
11562
+ if (this._switchingRenderer) {
11563
+ this.log("Suppressing error during renderer switch:", error, "debug");
11564
+ return;
11565
+ }
11151
11566
  this.log("Error:", error, "error");
11152
11567
  this.emit("error", error);
11153
11568
  if (this.options.onError) {
@@ -11691,7 +12106,9 @@
11691
12106
  autoPlayFirst: options.autoPlayFirst !== false,
11692
12107
  // Default true - auto-play first track on load
11693
12108
  loop: options.loop || false,
11694
- showPanel: options.showPanel !== false
12109
+ showPanel: options.showPanel !== false,
12110
+ // Default true
12111
+ recreatePlayers: options.recreatePlayers || false
11695
12112
  }, options);
11696
12113
  this.container = null;
11697
12114
  this.playlistPanel = null;
@@ -11699,6 +12116,8 @@
11699
12116
  this.navigationFeedback = null;
11700
12117
  this.isPanelVisible = this.options.showPanel !== false;
11701
12118
  this.isChangingTrack = false;
12119
+ this.hostElement = options.hostElement || null;
12120
+ this.PlayerClass = options.PlayerClass || null;
11702
12121
  this.handleTrackEnd = this.handleTrackEnd.bind(this);
11703
12122
  this.handleTrackError = this.handleTrackError.bind(this);
11704
12123
  this.player.playlistManager = this;
@@ -11708,6 +12127,159 @@
11708
12127
  this.loadPlaylist(this.initialTracks);
11709
12128
  }
11710
12129
  }
12130
+ /**
12131
+ * Determine the media type for a track
12132
+ * @param {Object} track - Track object
12133
+ * @returns {string} - 'audio', 'video', 'youtube', 'vimeo', 'soundcloud', 'hls'
12134
+ */
12135
+ getTrackMediaType(track) {
12136
+ const src = track.src || "";
12137
+ if (src.includes("youtube.com") || src.includes("youtu.be")) {
12138
+ return "youtube";
12139
+ }
12140
+ if (src.includes("vimeo.com")) {
12141
+ return "vimeo";
12142
+ }
12143
+ if (src.includes("soundcloud.com") || src.includes("api.soundcloud.com")) {
12144
+ return "soundcloud";
12145
+ }
12146
+ if (src.includes(".m3u8")) {
12147
+ return "hls";
12148
+ }
12149
+ if (track.type && track.type.startsWith("audio/")) {
12150
+ return "audio";
12151
+ }
12152
+ return "video";
12153
+ }
12154
+ /**
12155
+ * Recreate the player with the appropriate element type for the track
12156
+ * @param {Object} track - Track to load
12157
+ * @param {boolean} autoPlay - Whether to auto-play after creation
12158
+ */
12159
+ async recreatePlayerForTrack(track, autoPlay = false) {
12160
+ if (!this.hostElement || !this.PlayerClass) {
12161
+ console.warn("VidPly Playlist: Cannot recreate player - missing hostElement or PlayerClass");
12162
+ return false;
12163
+ }
12164
+ const mediaType = this.getTrackMediaType(track);
12165
+ const elementType = mediaType === "audio" ? "audio" : "video";
12166
+ const wasVisible = this.isPanelVisible;
12167
+ const savedTracks = [...this.tracks];
12168
+ const savedIndex = this.currentIndex;
12169
+ if (this.trackArtworkElement && this.trackArtworkElement.parentNode) {
12170
+ this.trackArtworkElement.parentNode.removeChild(this.trackArtworkElement);
12171
+ }
12172
+ if (this.trackInfoElement && this.trackInfoElement.parentNode) {
12173
+ this.trackInfoElement.parentNode.removeChild(this.trackInfoElement);
12174
+ }
12175
+ if (this.navigationFeedback && this.navigationFeedback.parentNode) {
12176
+ this.navigationFeedback.parentNode.removeChild(this.navigationFeedback);
12177
+ }
12178
+ if (this.playlistPanel && this.playlistPanel.parentNode) {
12179
+ this.playlistPanel.parentNode.removeChild(this.playlistPanel);
12180
+ }
12181
+ if (this.player) {
12182
+ this.player.off("ended", this.handleTrackEnd);
12183
+ this.player.off("error", this.handleTrackError);
12184
+ this.player.destroy();
12185
+ }
12186
+ this.hostElement.innerHTML = "";
12187
+ const mediaElement = document.createElement(elementType);
12188
+ mediaElement.setAttribute("preload", "metadata");
12189
+ if (elementType === "video" && track.poster && (mediaType === "video" || mediaType === "hls")) {
12190
+ mediaElement.setAttribute("poster", track.poster);
12191
+ }
12192
+ const isExternalRenderer = ["youtube", "vimeo", "soundcloud", "hls"].includes(mediaType);
12193
+ if (!isExternalRenderer) {
12194
+ const source = document.createElement("source");
12195
+ source.src = track.src;
12196
+ if (track.type) {
12197
+ source.type = track.type;
12198
+ }
12199
+ mediaElement.appendChild(source);
12200
+ if (track.tracks && track.tracks.length > 0) {
12201
+ track.tracks.forEach((trackConfig) => {
12202
+ const trackEl = document.createElement("track");
12203
+ trackEl.src = trackConfig.src;
12204
+ trackEl.kind = trackConfig.kind || "captions";
12205
+ trackEl.srclang = trackConfig.srclang || "en";
12206
+ trackEl.label = trackConfig.label || trackConfig.srclang;
12207
+ if (trackConfig.default) {
12208
+ trackEl.default = true;
12209
+ }
12210
+ mediaElement.appendChild(trackEl);
12211
+ });
12212
+ }
12213
+ }
12214
+ this.hostElement.appendChild(mediaElement);
12215
+ const playerOptions = {
12216
+ mediaType: elementType,
12217
+ poster: track.poster,
12218
+ audioDescriptionSrc: track.audioDescriptionSrc || null,
12219
+ audioDescriptionDuration: track.audioDescriptionDuration || null,
12220
+ signLanguageSrc: track.signLanguageSrc || null
12221
+ };
12222
+ this.player = new this.PlayerClass(mediaElement, playerOptions);
12223
+ this.player.playlistManager = this;
12224
+ await new Promise((resolve) => {
12225
+ this.player.on("ready", resolve);
12226
+ });
12227
+ this.player.on("ended", this.handleTrackEnd);
12228
+ this.player.on("error", this.handleTrackError);
12229
+ if (this.player.container) {
12230
+ if (this.trackArtworkElement) {
12231
+ const videoWrapper = this.player.container.querySelector(".vidply-video-wrapper");
12232
+ if (videoWrapper) {
12233
+ this.player.container.insertBefore(this.trackArtworkElement, videoWrapper);
12234
+ } else {
12235
+ this.player.container.appendChild(this.trackArtworkElement);
12236
+ }
12237
+ }
12238
+ if (this.trackInfoElement) {
12239
+ this.player.container.appendChild(this.trackInfoElement);
12240
+ }
12241
+ if (this.navigationFeedback) {
12242
+ this.player.container.appendChild(this.navigationFeedback);
12243
+ }
12244
+ if (this.playlistPanel) {
12245
+ this.player.container.appendChild(this.playlistPanel);
12246
+ }
12247
+ }
12248
+ this.container = this.player.container;
12249
+ this.updatePlayerControls();
12250
+ this.tracks = savedTracks;
12251
+ this.currentIndex = savedIndex;
12252
+ this.updatePlaylistUI();
12253
+ this.isPanelVisible = wasVisible;
12254
+ if (this.playlistPanel) {
12255
+ this.playlistPanel.style.display = wasVisible ? "" : "none";
12256
+ }
12257
+ if (isExternalRenderer) {
12258
+ this.player.load({
12259
+ src: track.src,
12260
+ type: track.type,
12261
+ poster: track.poster,
12262
+ tracks: track.tracks || [],
12263
+ audioDescriptionSrc: track.audioDescriptionSrc || null,
12264
+ signLanguageSrc: track.signLanguageSrc || null
12265
+ });
12266
+ } else {
12267
+ this.player.load({
12268
+ src: track.src,
12269
+ type: track.type,
12270
+ poster: track.poster,
12271
+ tracks: track.tracks || [],
12272
+ audioDescriptionSrc: track.audioDescriptionSrc || null,
12273
+ signLanguageSrc: track.signLanguageSrc || null
12274
+ });
12275
+ }
12276
+ if (autoPlay) {
12277
+ setTimeout(() => {
12278
+ this.player.play();
12279
+ }, 100);
12280
+ }
12281
+ return true;
12282
+ }
11711
12283
  init() {
11712
12284
  this.player.on("ended", this.handleTrackEnd);
11713
12285
  this.player.on("error", this.handleTrackError);
@@ -11818,7 +12390,7 @@
11818
12390
  * Load a track without playing
11819
12391
  * @param {number} index - Track index
11820
12392
  */
11821
- loadTrack(index) {
12393
+ async loadTrack(index) {
11822
12394
  if (index < 0 || index >= this.tracks.length) {
11823
12395
  console.warn("VidPly Playlist: Invalid track index", index);
11824
12396
  return;
@@ -11826,6 +12398,25 @@
11826
12398
  const track = this.tracks[index];
11827
12399
  this.isChangingTrack = true;
11828
12400
  this.currentIndex = index;
12401
+ if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {
12402
+ const currentMediaType = this.player ? this.player.element.tagName === "AUDIO" ? "audio" : "video" : null;
12403
+ const newMediaType = this.getTrackMediaType(track);
12404
+ const newElementType = newMediaType === "audio" || newMediaType === "soundcloud" ? "audio" : "video";
12405
+ if (currentMediaType !== newElementType) {
12406
+ await this.recreatePlayerForTrack(track, false);
12407
+ this.updateTrackInfo(track);
12408
+ this.updatePlaylistUI();
12409
+ this.player.emit("playlisttrackchange", {
12410
+ index,
12411
+ item: track,
12412
+ total: this.tracks.length
12413
+ });
12414
+ setTimeout(() => {
12415
+ this.isChangingTrack = false;
12416
+ }, 150);
12417
+ return;
12418
+ }
12419
+ }
11829
12420
  this.player.load({
11830
12421
  src: track.src,
11831
12422
  type: track.type,
@@ -11850,7 +12441,7 @@
11850
12441
  * @param {number} index - Track index
11851
12442
  * @param {boolean} userInitiated - Whether this was triggered by user action (default: false)
11852
12443
  */
11853
- play(index, userInitiated = false) {
12444
+ async play(index, userInitiated = false) {
11854
12445
  if (index < 0 || index >= this.tracks.length) {
11855
12446
  console.warn("VidPly Playlist: Invalid track index", index);
11856
12447
  return;
@@ -11858,6 +12449,25 @@
11858
12449
  const track = this.tracks[index];
11859
12450
  this.isChangingTrack = true;
11860
12451
  this.currentIndex = index;
12452
+ if (this.options.recreatePlayers && this.hostElement && this.PlayerClass) {
12453
+ const currentMediaType = this.player ? this.player.element.tagName === "AUDIO" ? "audio" : "video" : null;
12454
+ const newMediaType = this.getTrackMediaType(track);
12455
+ const newElementType = newMediaType === "audio" || newMediaType === "soundcloud" ? "audio" : "video";
12456
+ if (currentMediaType !== newElementType) {
12457
+ await this.recreatePlayerForTrack(track, true);
12458
+ this.updateTrackInfo(track);
12459
+ this.updatePlaylistUI();
12460
+ this.player.emit("playlisttrackchange", {
12461
+ index,
12462
+ item: track,
12463
+ total: this.tracks.length
12464
+ });
12465
+ setTimeout(() => {
12466
+ this.isChangingTrack = false;
12467
+ }, 150);
12468
+ return;
12469
+ }
12470
+ }
11861
12471
  this.player.load({
11862
12472
  src: track.src,
11863
12473
  type: track.type,
@@ -11919,10 +12529,26 @@
11919
12529
  this.next();
11920
12530
  }
11921
12531
  }
12532
+ /**
12533
+ * Check if a source URL requires an external renderer
12534
+ * @param {string} src - Source URL
12535
+ * @returns {boolean}
12536
+ */
12537
+ isExternalRendererUrl(src) {
12538
+ if (!src) return false;
12539
+ 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");
12540
+ }
11922
12541
  /**
11923
12542
  * Handle track error
11924
12543
  */
11925
12544
  handleTrackError(e) {
12545
+ const currentTrack = this.getCurrentTrack();
12546
+ if (currentTrack && currentTrack.src && this.isExternalRendererUrl(currentTrack.src)) {
12547
+ return;
12548
+ }
12549
+ if (this.isChangingTrack) {
12550
+ return;
12551
+ }
11926
12552
  console.error("VidPly Playlist: Track error", e);
11927
12553
  if (this.options.autoAdvance) {
11928
12554
  setTimeout(() => {
@@ -12135,7 +12761,7 @@
12135
12761
  id: "".concat(this.uniqueId, "-keyboard-instructions")
12136
12762
  }
12137
12763
  });
12138
- instructions.textContent = "Playlist navigation: Use Up and Down arrow keys to move between tracks. Press Page Up or Page Down to skip 5 tracks. Press Home to go to first track, End to go to last track. Press Enter or Space to play the selected track.";
12764
+ instructions.textContent = i18n.t("playlist.keyboardInstructions");
12139
12765
  this.playlistPanel.appendChild(instructions);
12140
12766
  const list = DOMUtils.createElement("ul", {
12141
12767
  className: "vidply-playlist-list",
@@ -12291,7 +12917,7 @@
12291
12917
  if (index < buttons.length - 1) {
12292
12918
  newIndex = index + 1;
12293
12919
  } else {
12294
- announcement = "End of playlist. ".concat(buttons.length, " of ").concat(buttons.length, ".");
12920
+ announcement = i18n.t("playlist.endOfPlaylist", { current: buttons.length, total: buttons.length });
12295
12921
  }
12296
12922
  break;
12297
12923
  case "ArrowUp":
@@ -12300,7 +12926,7 @@
12300
12926
  if (index > 0) {
12301
12927
  newIndex = index - 1;
12302
12928
  } else {
12303
- announcement = "Beginning of playlist. 1 of " + buttons.length + ".";
12929
+ announcement = i18n.t("playlist.beginningOfPlaylist", { total: buttons.length });
12304
12930
  }
12305
12931
  break;
12306
12932
  case "PageDown":
@@ -12308,7 +12934,7 @@
12308
12934
  e.stopPropagation();
12309
12935
  newIndex = Math.min(index + 5, buttons.length - 1);
12310
12936
  if (newIndex === buttons.length - 1 && index !== newIndex) {
12311
- announcement = "Jumped to last track. ".concat(newIndex + 1, " of ").concat(buttons.length, ".");
12937
+ announcement = i18n.t("playlist.jumpedToLastTrack", { current: newIndex + 1, total: buttons.length });
12312
12938
  }
12313
12939
  break;
12314
12940
  case "PageUp":
@@ -12316,7 +12942,7 @@
12316
12942
  e.stopPropagation();
12317
12943
  newIndex = Math.max(index - 5, 0);
12318
12944
  if (newIndex === 0 && index !== newIndex) {
12319
- announcement = "Jumped to first track. 1 of ".concat(buttons.length, ".");
12945
+ announcement = i18n.t("playlist.jumpedToFirstTrack", { total: buttons.length });
12320
12946
  }
12321
12947
  break;
12322
12948
  case "Home":
@@ -12324,7 +12950,7 @@
12324
12950
  e.stopPropagation();
12325
12951
  newIndex = 0;
12326
12952
  if (index !== 0) {
12327
- announcement = "First track. 1 of ".concat(buttons.length, ".");
12953
+ announcement = i18n.t("playlist.firstTrack", { total: buttons.length });
12328
12954
  }
12329
12955
  break;
12330
12956
  case "End":
@@ -12332,7 +12958,7 @@
12332
12958
  e.stopPropagation();
12333
12959
  newIndex = buttons.length - 1;
12334
12960
  if (index !== buttons.length - 1) {
12335
- announcement = "Last track. ".concat(buttons.length, " of ").concat(buttons.length, ".");
12961
+ announcement = i18n.t("playlist.lastTrack", { current: buttons.length, total: buttons.length });
12336
12962
  }
12337
12963
  break;
12338
12964
  }