@arraypress/waveform-player 1.3.4 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,74 +18,19 @@ A lightweight, customizable audio player with waveform visualization. Under 8KB
18
18
  - **Real Waveforms** - Actual audio analysis, not fake waves
19
19
  - **No Dependencies** - No jQuery, no bloat, pure vanilla JS
20
20
  - **Works Everywhere** - WordPress, Shopify, React, Vue, or plain HTML
21
- - **Ecosystem** - Optional playlist and analytics addons available
21
+ - **Ecosystem** - Optional [WaveformBar](https://github.com/arraypress/waveform-bar) persistent player addon available
22
22
 
23
- ## What's New in 1.3.1
23
+ ## What's New
24
24
 
25
- ### 🐛 Bug Fix
25
+ For detailed release notes, see the [Changelog](CHANGELOG.md).
26
26
 
27
- - Fixed uncaught `NotAllowedError` when `loadTrack()` triggers autoplay before user interaction (e.g. on session restore)
27
+ ### Recent Changes (v1.3.x)
28
28
 
29
- ## What's New in 1.3.0
30
-
31
- ### 🎛️ Show/Hide Controls & Info
32
-
33
- You can now hide the play/pause button and info bar independently, making it easy to build custom UIs around the waveform.
34
- ```html
35
- <!-- Waveform only — no button, no info -->
36
- <div data-waveform-player
37
- data-url="song.mp3"
38
- data-show-controls="false"
39
- data-show-info="false">
40
- </div>
41
- ```
42
-
43
- - `showControls` — toggle the play/pause button (default: `true`)
44
- - `showInfo` — toggle the title, subtitle, time, BPM, and speed controls (default: `true`)
45
- - Waveform automatically fills the full width when controls are hidden
46
-
47
- Thanks to [@mulhoon](https://github.com/mulhoon) for suggesting this feature.
48
-
49
- ## What's New in 1.2.2
50
-
51
- ### 🐛 Bug Fix
52
-
53
- - `play()` now returns the Promise from `HTMLMediaElement.play()`, allowing callers to handle errors like `AbortError`
54
-
55
- Thanks to [@scruffian](https://github.com/scruffian) for the contribution.
56
-
57
- ## What's New in 1.2.1
58
-
59
- ### 🐛 Bug Fixes
60
-
61
- - Fixed null reference error when `destroy()` is called during resize events
62
- - Cleaned up window resize listener on destroy to prevent memory leaks
63
- - Added destruction guards to all event handlers to prevent race conditions
64
- - Added `bubbles: true` to all custom events for better framework integration
65
-
66
- Thanks to [@scruffian](https://github.com/scruffian) for contributing these fixes.
67
-
68
- ## What's New in 1.2.0
69
-
70
- ### 🎨 Automatic Theme Detection
71
-
72
- WaveformPlayer now automatically adapts to your website's color scheme - no configuration needed!
73
-
74
- **Features:**
75
-
76
- - Detects light/dark themes automatically
77
- - Checks background brightness, theme classes, and system preferences
78
- - Works seamlessly on WordPress, Shopify, and all platforms
79
- - Override with explicit `data-color-preset="light"` or `"dark"` if needed
80
-
81
- **How it works:**
82
-
83
- 1. Checks for explicit theme classes (`.dark-mode`, `.light-mode`, etc.)
84
- 2. Analyzes background brightness
85
- 3. Respects system color preferences (`prefers-color-scheme`)
86
- 4. Falls back to sensible defaults
87
-
88
- [View live examples →](https://waveformplayer.com/modes/dark.html)
29
+ - Fixed markers from previous track persisting when loading a new track
30
+ - Removed inline canvas height for reliable width sizing in flex containers
31
+ - Fixed waveform canvas reading width from container instead of canvas element
32
+ - Fixed uncaught `NotAllowedError` on autoplay
33
+ - Added `showControls` and `showInfo` options for waveform-only display
89
34
 
90
35
  ## Quick Start
91
36
 
@@ -104,14 +49,12 @@ npm install @arraypress/waveform-player
104
49
 
105
50
  ### CDN
106
51
  ```html
107
-
108
52
  <link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.css">
109
53
  <script src="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.min.js"></script>
110
54
  ```
111
55
 
112
56
  ### Download
113
57
  ```html
114
-
115
58
  <link rel="stylesheet" href="waveform-player.css">
116
59
  <script src="waveform-player.js"></script>
117
60
  ```
@@ -132,14 +75,34 @@ npm install @arraypress/waveform-player
132
75
  - ⏩ **Speed Control** - Adjustable playback rate for podcasts/audiobooks
133
76
  - 📍 **Chapter Markers** - Add clickable markers for navigation
134
77
  - 🔄 **Dynamic Loading** - Load new tracks without page refresh
78
+ - 🎨 **Auto Theme Detection** - Adapts to light/dark themes automatically
135
79
 
136
80
  ## Ecosystem
137
81
 
82
+ ### WaveformBar (Optional Addon)
83
+
84
+ A persistent bottom-bar audio player with queue, favorites, cart, volume popup, DJ mode with markers, and cross-page session persistence:
85
+
86
+ ```html
87
+ <div data-wb-play
88
+ data-url="song.mp3"
89
+ data-title="My Track"
90
+ data-artist="Artist"
91
+ data-bpm="128"
92
+ data-key="Am">
93
+ </div>
94
+
95
+ <script>
96
+ WaveformBar.init();
97
+ </script>
98
+ ```
99
+
100
+ [Learn more →](https://github.com/arraypress/waveform-bar)
101
+
138
102
  ### WaveformPlaylist (Optional Addon)
139
103
 
140
104
  Add playlist and chapter support with zero JavaScript:
141
105
  ```html
142
-
143
106
  <div data-waveform-playlist data-continuous="true">
144
107
  <div data-track data-url="song1.mp3" data-title="Track 1">
145
108
  <div data-chapter data-time="0:00">Intro</div>
@@ -183,7 +146,6 @@ WaveformTracker.init({
183
146
 
184
147
  ### HTML (Zero JavaScript)
185
148
  ```html
186
-
187
149
  <div data-waveform-player
188
150
  data-url="audio.mp3"
189
151
  data-title="My Song"
@@ -194,6 +156,16 @@ WaveformTracker.init({
194
156
  </div>
195
157
  ```
196
158
 
159
+ ### Waveform Only (Custom UI)
160
+ ```html
161
+ <!-- Hide built-in controls for custom UI integration -->
162
+ <div data-waveform-player
163
+ data-url="audio.mp3"
164
+ data-show-controls="false"
165
+ data-show-info="false">
166
+ </div>
167
+ ```
168
+
197
169
  ### JavaScript API
198
170
  ```javascript
199
171
  import WaveformPlayer from '@arraypress/waveform-player';
@@ -246,6 +218,7 @@ Choose from 6 built-in styles:
246
218
  | `subtitle` | string | `''` | Track subtitle |
247
219
  | `artwork` | string | `''` | Album artwork URL |
248
220
  | `markers` | array | `[]` | Chapter markers array |
221
+ | `waveform` | array | `null` | Pre-generated waveform data |
249
222
  | `enableMediaSession` | boolean | `true` | Enable system media controls |
250
223
 
251
224
  ## API Methods
@@ -266,7 +239,11 @@ player.setVolume(0.8); // 80% volume
266
239
  player.setPlaybackRate(1.5); // 1.5x speed
267
240
 
268
241
  // Dynamic loading
269
- await player.loadTrack('new-song.mp3', 'New Title', 'New Artist');
242
+ await player.loadTrack('new-song.mp3', 'New Title', 'New Artist', {
243
+ markers: [{time: 30, label: 'Chorus'}],
244
+ artwork: 'cover.jpg',
245
+ waveform: [0.2, 0.5, 0.8, 0.3] // Optional pre-generated data
246
+ });
270
247
 
271
248
  // Destroy
272
249
  player.destroy();
@@ -357,24 +334,23 @@ function AudioPlayer({url}) {
357
334
 
358
335
  ### Vue
359
336
  ```vue
360
-
361
337
  <template>
362
338
  <div ref="player"></div>
363
339
  </template>
364
340
 
365
341
  <script>
366
- import WaveformPlayer from '@arraypress/waveform-player';
342
+ import WaveformPlayer from '@arraypress/waveform-player';
367
343
 
368
- export default {
344
+ export default {
369
345
  mounted() {
370
- this.player = new WaveformPlayer(this.$refs.player, {
371
- url: this.audioUrl
372
- });
346
+ this.player = new WaveformPlayer(this.$refs.player, {
347
+ url: this.audioUrl
348
+ });
373
349
  },
374
350
  beforeDestroy() {
375
- this.player?.destroy();
351
+ this.player?.destroy();
376
352
  }
377
- }
353
+ }
378
354
  </script>
379
355
  ```
380
356
 
@@ -398,8 +374,6 @@ See the [live demo](https://waveformplayer.com) for:
398
374
  - Keyboard navigation
399
375
  - Speed controls
400
376
  - Chapter markers
401
- - Playlist support
402
- - Analytics tracking
403
377
 
404
378
  ## Development
405
379
  ```bash
@@ -1,4 +1,4 @@
1
- function L(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function C(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function A(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function R(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function P(...t){let e={};for(let i of t)for(let o in i)i[o]!==null&&i[o]!==void 0&&(e[o]=i[o]);return e}function B(t,e){let i;return function(...s){let n=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(n,e)}}function S(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let o=(t.length-1)/(e-1);for(let s=0;s<e;s++){let n=s*o,r=Math.floor(n),a=Math.ceil(n),h=n-r;if(a>=t.length)i.push(t[t.length-1]);else if(r===a)i.push(t[r]);else{let l=t[r]*(1-h)+t[a]*h;i.push(l)}}}else{let o=t.length/e;for(let s=0;s<e;s++){let n=Math.floor(s*o),r=Math.floor((s+1)*o),a=0,h=0;for(let l=n;l<=r&&l<t.length;l++)t[l]>a&&(a=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*o),t.length-1);a=t[l]}i.push(a)}}return i}function I(t,e,i,o,s){let n=window.devicePixelRatio||1,r=s.barWidth*n,a=s.barSpacing*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=o*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(r+a);if(f+r>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,r,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(r+a);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,r,c)}t.restore()}function $(t,e,i,o,s){let n=window.devicePixelRatio||1,r=s.barWidth*n,a=s.barSpacing*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=d/2,y=o*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(r+a);if(c+r>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(r+a);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.restore()}function q(t,e,i,o,s){let n=e.width,r=e.height,a=r/2,h=r*.35;t.clearRect(0,0,n,r);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,a);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*n,k=i[u],b=Math.sin(u*.1)*k,w=a+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,a),t.lineTo(n,a),t.stroke();for(let d=0;d<=10;d++){let p=n/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,r),t.stroke()}l(s.color,2,1,!1),o>0&&l(s.progressColor,3,o,!0)}function U(t,e,i,o,s){let n=window.devicePixelRatio||1,r=(s.barWidth||3)*n,a=(s.barSpacing||1)*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=4*n,y=2*n,f=o*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(r+a);if(u+r>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?s.progressColor:s.color;for(let b=0;b<k;b++){let w=b*(p+y);t.fillRect(u,c-w-p,r,p),b>0&&t.fillRect(u,c+w,r,p)}}}function F(t,e,i,o,s){let n=window.devicePixelRatio||1,r=(s.barWidth||2)*n,a=(s.barSpacing||3)*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=Math.max(1.5*n,r/2),y=o*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(r+a)+r/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,o,s){let n=e.width,r=e.height,a=r/2,h=4,l=h/2;if(t.clearRect(0,0,n,r),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(n-l,a-h/2),t.arc(n-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),o>0){let d=Math.max(l*2,o*n);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(d-l,a-h/2),t.arc(d-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,a,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,a,p*.4,0,Math.PI*2),t.fill()}}var Y={bars:I,mirror:$,line:q,blocks:U,dots:F,seekbar:N};function x(t,e,i,o,s){(Y[s.waveformStyle]||I)(t,e,i,o,s)}function W(t){try{let e=t.getChannelData(0),i=t.sampleRate,o=j(e,i);if(o.length<2)return 120;let s=[];for(let h=1;h<o.length;h++)s.push((o[h]-o[h-1])/i);let n={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(n[d]=(n[d]||0)+1)});let r=0,a=120;for(let[h,l]of Object.entries(n))l>r&&(r=l,a=parseInt(h));return a<70&&n[a*2]?a*=2:a>160&&n[Math.round(a/2)]&&(a=Math.round(a/2)),a-1}catch(e){return console.warn("BPM detection failed:",e),null}}function j(t,e){let s=[],n=0;for(let r=0;r<t.length-2048;r+=1024){let a=0;for(let d=r;d<r+2048;d++)a+=t[d]*t[d];a=a/2048;let h=a-n,l=n*1.8+.01;if(h>l&&a>.01){let d=s[s.length-1]||0,p=e*.15;r-d>p&&s.push(r)}n=a*.8+n*.2}return s}function V(t,e=200){let i=t.length/e,o=~~(i/10)||1,s=t.numberOfChannels,n=[];for(let a=0;a<s;a++){let h=t.getChannelData(a);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=o){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(a===0||c>n[l])&&(n[l]=c)}}let r=Math.max(...n);return r>0?n.map(a=>a/r):n}async function M(t,e=200,i=!1){try{let o=new(window.AudioContext||window.webkitAudioContext),n=await(await fetch(t)).arrayBuffer(),r=await o.decodeAudioData(n),a=V(r,e);a=J(a);let h=null;return i&&(h=await W(r)),o.close(),{peaks:a,bpm:h}}catch(o){throw console.error("Failed to generate waveform:",o),o}}function D(t=200){let e=[];for(let i=0;i<t;i++){let o=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,o+s)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let o=e/i;return t.map(s=>s*o)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let o=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(o&&o.length>=3){let[s,n,r]=o.map(Number),a=(s*299+n*587+r*114)/1e3;if(a>128)return"light";if(a<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var E={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function z(t){if(t&&E[t])return E[t];let e=G();return E[e]}var O={url:"",height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},H={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let o=L(this.container);this.options=P(O,o,i);let s=z(this.options.colorPreset);for(let[r,a]of Object.entries(s))(this.options[r]===null||this.options[r]===void 0)&&(this.options[r]=a);let n=H[this.options.waveformStyle];n&&(o.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=n.barWidth),o.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=n.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||A(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
1
+ function B(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function M(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function S(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function I(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function P(...t){let e={};for(let i of t)for(let o in i)i[o]!==null&&i[o]!==void 0&&(e[o]=i[o]);return e}function x(t,e){let i;return function(...s){let r=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(r,e)}}function C(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let o=(t.length-1)/(e-1);for(let s=0;s<e;s++){let r=s*o,n=Math.floor(r),a=Math.ceil(r),h=r-n;if(a>=t.length)i.push(t[t.length-1]);else if(n===a)i.push(t[n]);else{let l=t[n]*(1-h)+t[a]*h;i.push(l)}}}else{let o=t.length/e;for(let s=0;s<e;s++){let r=Math.floor(s*o),n=Math.floor((s+1)*o),a=0,h=0;for(let l=r;l<=n&&l<t.length;l++)t[l]>a&&(a=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*o),t.length-1);a=t[l]}i.push(a)}}return i}function W(t,e,i,o,s){let r=window.devicePixelRatio||1,n=s.barWidth*r,a=s.barSpacing*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=o*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(n+a);if(f+n>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,n,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(n+a);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,n,c)}t.restore()}function $(t,e,i,o,s){let r=window.devicePixelRatio||1,n=s.barWidth*r,a=s.barSpacing*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=d/2,y=o*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(n+a);if(c+n>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(n+a);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.restore()}function q(t,e,i,o,s){let r=e.width,n=e.height,a=n/2,h=n*.35;t.clearRect(0,0,r,n);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,a);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*r,k=i[u],g=Math.sin(u*.1)*k,w=a+g*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,g=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,g,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,a),t.lineTo(r,a),t.stroke();for(let d=0;d<=10;d++){let p=r/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,n),t.stroke()}l(s.color,2,1,!1),o>0&&l(s.progressColor,3,o,!0)}function F(t,e,i,o,s){let r=window.devicePixelRatio||1,n=(s.barWidth||3)*r,a=(s.barSpacing||1)*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=4*r,y=2*r,f=o*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(n+a);if(u+n>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?s.progressColor:s.color;for(let g=0;g<k;g++){let w=g*(p+y);t.fillRect(u,c-w-p,n,p),g>0&&t.fillRect(u,c+w,n,p)}}}function U(t,e,i,o,s){let r=window.devicePixelRatio||1,n=(s.barWidth||2)*r,a=(s.barSpacing||3)*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=Math.max(1.5*r,n/2),y=o*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(n+a)+n/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,o,s){let r=e.width,n=e.height,a=n/2,h=4,l=h/2;if(t.clearRect(0,0,r,n),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(r-l,a-h/2),t.arc(r-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),o>0){let d=Math.max(l*2,o*r);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(d-l,a-h/2),t.arc(d-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,a,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,a,p*.4,0,Math.PI*2),t.fill()}}var j={bars:W,mirror:$,line:q,blocks:F,dots:U,seekbar:N};function D(t,e,i,o,s){(j[s.waveformStyle]||W)(t,e,i,o,s)}function z(t){try{let e=t.getChannelData(0),i=t.sampleRate,o=Y(e,i);if(o.length<2)return 120;let s=[];for(let h=1;h<o.length;h++)s.push((o[h]-o[h-1])/i);let r={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(r[d]=(r[d]||0)+1)});let n=0,a=120;for(let[h,l]of Object.entries(r))l>n&&(n=l,a=parseInt(h));return a<70&&r[a*2]?a*=2:a>160&&r[Math.round(a/2)]&&(a=Math.round(a/2)),a-1}catch(e){return console.warn("BPM detection failed:",e),null}}function Y(t,e){let s=[],r=0;for(let n=0;n<t.length-2048;n+=1024){let a=0;for(let d=n;d<n+2048;d++)a+=t[d]*t[d];a=a/2048;let h=a-r,l=r*1.8+.01;if(h>l&&a>.01){let d=s[s.length-1]||0,p=e*.15;n-d>p&&s.push(n)}r=a*.8+r*.2}return s}function V(t,e=200){let i=t.length/e,o=~~(i/10)||1,s=t.numberOfChannels,r=[];for(let a=0;a<s;a++){let h=t.getChannelData(a);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=o){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(a===0||c>r[l])&&(r[l]=c)}}let n=Math.max(...r);return n>0?r.map(a=>a/n):r}async function E(t,e=200,i=!1){try{let o=new(window.AudioContext||window.webkitAudioContext),r=await(await fetch(t)).arrayBuffer(),n=await o.decodeAudioData(r),a=V(n,e);a=J(a);let h=null;return i&&(h=await z(n)),o.close(),{peaks:a,bpm:h}}catch(o){throw console.error("Failed to generate waveform:",o),o}}function O(t=200){let e=[];for(let i=0;i<t;i++){let o=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,o+s)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let o=e/i;return t.map(s=>s*o)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let o=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(o&&o.length>=3){let[s,r,n]=o.map(Number),a=(s*299+r*587+n*114)/1e3;if(a>128)return"light";if(a<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var T={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function L(t){if(t&&T[t])return T[t];let e=G();return T[e]}var A={url:"",config:null,height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},H={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var b=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let o=B(this.container);this.options=P(A,o,i);let s=L(this.options.colorPreset);for(let[n,a]of Object.entries(s))(this.options[n]===null||this.options[n]===void 0)&&(this.options[n]=a);let r=H[this.options.waveformStyle];r&&(o.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=r.barWidth),o.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=r.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.meta={},this.options.config?(this.id=this.container.id||S("pending-"+Math.random()),t.instances.set(this.id,this),this._loadConfig(this.options.config,o,i).then(()=>{!this.container.id&&this.options.url&&(t.instances.delete(this.id),this.id=S(this.options.url),t.instances.set(this.id,this)),this.init(),this._dispatchReady()})):(this.id=this.container.id||S(this.options.url),t.instances.set(this.id,this),this.init(),this._dispatchReady())}async _loadConfig(e,i={},o={}){try{t._configCache=t._configCache||{};let s;if(t._configCache[e])s=t._configCache[e];else{let a=await fetch(e);if(!a.ok)throw new Error(`HTTP ${a.status}`);s=await a.json(),t._configCache[e]=s}let r={};s.url&&(r.url=s.url),s.title&&(r.title=s.title),s.subtitle&&(r.subtitle=s.subtitle),s.artwork&&(r.artwork=s.artwork),s.album&&(r.album=s.album),s.samples&&(r.samples=s.samples),s.markers&&(r.markers=s.markers),s.peaks&&(r.waveform=s.peaks),s.meta&&(this.meta=s.meta),this.options=P(A,r,i,o);let n=L(this.options.colorPreset);for(let[a,h]of Object.entries(n))(this.options[a]===null||this.options[a]===void 0)&&(this.options[a]=h)}catch(s){console.warn("WaveformPlayer: Failed to load config:",e,s)}}_dispatchReady(){setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url,meta:this.meta}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
2
2
  <button class="waveform-btn" aria-label="Play/Pause" style="
3
3
  border-color: ${this.options.buttonColor};
4
4
  color: ${this.options.buttonColor};
@@ -63,4 +63,4 @@ function L(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&
63
63
  ${o}
64
64
  </div>
65
65
  </div>
66
- `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",o=>{o.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",o=>{if(o.stopPropagation(),o.target.classList.contains("speed-option")){let s=parseFloat(o.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,o=this.audio.currentTime;if(i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let s={" ":()=>this.togglePlay(),ArrowLeft:()=>this.seekTo(Math.max(0,o-5)),ArrowRight:()=>this.seekTo(Math.min(this.audio.duration,o+5)),ArrowUp:()=>this.setVolume(Math.min(1,this.audio.volume+.1)),ArrowDown:()=>this.setVolume(Math.max(0,this.audio.volume-.1)),m:()=>this.audio.muted=!this.audio.muted,M:()=>this.audio.muted=!this.audio.muted};s[i]&&(e.preventDefault(),s[i]())})}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e)),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=B(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio.src=e,await new Promise((o,s)=>{let n=()=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),o()},r=a=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),s(a)};this.audio.addEventListener("loadedmetadata",n),this.audio.addEventListener("error",r)});let i=this.options.title||R(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let o=await M(e,this.options.samples,this.options.showBPM);this.waveformData=o.peaks,o.bpm&&(this.detectedBPM=o.bpm,this.updateBPMDisplay())}catch(o){console.warn("Using placeholder waveform:",o),this.waveformData=D(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,o=null,s={}){this.isPlaying&&this.pause(),this.audio.src="",this.audio.load(),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[],this.options=P(this.options,{url:e,title:i||this.options.title,subtitle:o||this.options.subtitle,...s}),s.preload&&(this.audio.preload=s.preload),this.subtitleEl&&(o?(this.subtitleEl.textContent=o,this.subtitleEl.style.display=""):o===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),this.options.markers=s.markers||[],await this.load(e),this.play().catch(()=>{})}setWaveformData(e){if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||x(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){!this.options.showMarkers||!this.options.markers?.length||!this.markersContainer||(this.markersContainer.innerHTML="",!(!this.audio||!this.audio.duration||this.audio.duration===0)&&this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let o=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${o}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=e.label,s.appendChild(n),s.addEventListener("click",r=>{r.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),o=e.clientX-i.left,s=Math.max(0,Math.min(1,o/i.width));this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=C(this.audio.duration)),this.renderMarkers())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=C(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this)}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){return this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),t.currentlyPlaying=this,this.audio.play()}pause(){t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio.pause()}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let o=document.getElementById(e);if(o)return Array.from(this.instances.values()).find(s=>s.container===o)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await M(e,i)).peaks}catch(o){throw console.error("Failed to generate waveform:",o),o}}};function T(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new g(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",T):T());g.init=T;typeof window<"u"&&(window.WaveformPlayer=g);var lt=g;export{g as WaveformPlayer,lt as default};
66
+ `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",o=>{o.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",o=>{if(o.stopPropagation(),o.target.classList.contains("speed-option")){let s=parseFloat(o.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,o=this.audio.currentTime;if(i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let s={" ":()=>this.togglePlay(),ArrowLeft:()=>this.seekTo(Math.max(0,o-5)),ArrowRight:()=>this.seekTo(Math.min(this.audio.duration,o+5)),ArrowUp:()=>this.setVolume(Math.min(1,this.audio.volume+.1)),ArrowDown:()=>this.setVolume(Math.max(0,this.audio.volume-.1)),m:()=>this.audio.muted=!this.audio.muted,M:()=>this.audio.muted=!this.audio.muted};s[i]&&(e.preventDefault(),s[i]())})}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e)),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=x(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio.src=e,await new Promise((o,s)=>{let r=()=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),o()},n=a=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),s(a)};this.audio.addEventListener("loadedmetadata",r),this.audio.addEventListener("error",n)});let i=this.options.title||I(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.config&&!this.options.waveform&&(await this._loadConfig(this.options.config),this.options.title&&this.titleEl&&(this.titleEl.textContent=this.options.title)),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let o=await E(e,this.options.samples,this.options.showBPM);this.waveformData=o.peaks,o.bpm&&(this.detectedBPM=o.bpm,this.updateBPMDisplay())}catch(o){console.warn("Using placeholder waveform:",o),this.waveformData=O(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,o=null,s={}){this.isPlaying&&this.pause(),s.config&&(await this._loadConfig(s.config),!e&&this.options.url&&(e=this.options.url),i&&(this.options.title=i),o&&(this.options.subtitle=o)),this.audio.src="",this.audio.load(),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[];let r=e||this.options.url;this.options=P(this.options,{url:r,title:i||this.options.title,subtitle:o||this.options.subtitle,...s}),s.preload&&(this.audio.preload=s.preload),this.subtitleEl&&(o?(this.subtitleEl.textContent=o,this.subtitleEl.style.display=""):o===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),s.markers&&(this.options.markers=s.markers),await this.load(r),this.play()}setWaveformData(e){if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||D(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.style.height=this.options.height+"px",this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){!this.options.showMarkers||!this.options.markers?.length||!this.markersContainer||(this.markersContainer.innerHTML="",!(!this.audio||!this.audio.duration||this.audio.duration===0)&&this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let o=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${o}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let r=document.createElement("span");r.className="waveform-marker-tooltip",r.textContent=e.label,s.appendChild(r),s.addEventListener("click",n=>{n.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),o=e.clientX-i.left,s=Math.max(0,Math.min(1,o/i.width));this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=M(this.audio.duration)),this.renderMarkers())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=M(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this)}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){return this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),t.currentlyPlaying=this,this.audio.play()}pause(){t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio.pause()}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let o=document.getElementById(e);if(o)return Array.from(this.instances.values()).find(s=>s.container===o)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await E(e,i)).peaks}catch(o){throw console.error("Failed to generate waveform:",o),o}}static async loadConfig(e){if(t._configCache=t._configCache||{},t._configCache[e])return t._configCache[e];let i=await fetch(e);if(!i.ok)throw new Error(`HTTP ${i.status}`);let o=await i.json();return t._configCache[e]=o,o}};function R(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new b(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",R):R());b.init=R;typeof window<"u"&&(window.WaveformPlayer=b);var lt=b;export{b as WaveformPlayer,lt as default};
@@ -571,6 +571,8 @@
571
571
  var DEFAULT_OPTIONS = {
572
572
  // Core settings
573
573
  url: "",
574
+ config: null,
575
+ // URL to JSON config file (loads title, peaks, meta, etc.)
574
576
  height: 60,
575
577
  samples: 200,
576
578
  preload: "metadata",
@@ -676,13 +678,76 @@
676
678
  this.hasError = false;
677
679
  this.updateTimer = null;
678
680
  this.resizeObserver = null;
679
- this.id = this.container.id || generateId(this.options.url);
680
- _WaveformPlayer.instances.set(this.id, this);
681
- this.init();
681
+ this.meta = {};
682
+ if (this.options.config) {
683
+ this.id = this.container.id || generateId("pending-" + Math.random());
684
+ _WaveformPlayer.instances.set(this.id, this);
685
+ this._loadConfig(this.options.config, dataOptions, options).then(() => {
686
+ if (!this.container.id && this.options.url) {
687
+ _WaveformPlayer.instances.delete(this.id);
688
+ this.id = generateId(this.options.url);
689
+ _WaveformPlayer.instances.set(this.id, this);
690
+ }
691
+ this.init();
692
+ this._dispatchReady();
693
+ });
694
+ } else {
695
+ this.id = this.container.id || generateId(this.options.url);
696
+ _WaveformPlayer.instances.set(this.id, this);
697
+ this.init();
698
+ this._dispatchReady();
699
+ }
700
+ }
701
+ /**
702
+ * Load external JSON config file
703
+ * Config values are base — data attributes and JS options override them
704
+ * @param {string} configUrl - URL to JSON config file
705
+ * @param {Object} dataOptions - Parsed data attributes (for override check)
706
+ * @param {Object} jsOptions - JS constructor options (for override check)
707
+ * @private
708
+ */
709
+ async _loadConfig(configUrl, dataOptions = {}, jsOptions = {}) {
710
+ try {
711
+ _WaveformPlayer._configCache = _WaveformPlayer._configCache || {};
712
+ let config;
713
+ if (_WaveformPlayer._configCache[configUrl]) {
714
+ config = _WaveformPlayer._configCache[configUrl];
715
+ } else {
716
+ const response = await fetch(configUrl);
717
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
718
+ config = await response.json();
719
+ _WaveformPlayer._configCache[configUrl] = config;
720
+ }
721
+ const configOptions = {};
722
+ if (config.url) configOptions.url = config.url;
723
+ if (config.title) configOptions.title = config.title;
724
+ if (config.subtitle) configOptions.subtitle = config.subtitle;
725
+ if (config.artwork) configOptions.artwork = config.artwork;
726
+ if (config.album) configOptions.album = config.album;
727
+ if (config.samples) configOptions.samples = config.samples;
728
+ if (config.markers) configOptions.markers = config.markers;
729
+ if (config.peaks) configOptions.waveform = config.peaks;
730
+ if (config.meta) this.meta = config.meta;
731
+ this.options = mergeOptions(DEFAULT_OPTIONS, configOptions, dataOptions, jsOptions);
732
+ const preset = getColorPreset(this.options.colorPreset);
733
+ for (const [key, value] of Object.entries(preset)) {
734
+ if (this.options[key] === null || this.options[key] === void 0) {
735
+ this.options[key] = value;
736
+ }
737
+ }
738
+ } catch (error) {
739
+ console.warn("WaveformPlayer: Failed to load config:", configUrl, error);
740
+ }
741
+ }
742
+ /**
743
+ * Dispatch ready event
744
+ * @private
745
+ */
746
+ _dispatchReady() {
682
747
  setTimeout(() => {
683
748
  this.container.dispatchEvent(new CustomEvent("waveformplayer:ready", {
684
749
  bubbles: true,
685
- detail: { player: this, url: this.options.url }
750
+ detail: { player: this, url: this.options.url, meta: this.meta }
686
751
  }));
687
752
  }, 100);
688
753
  }
@@ -1000,6 +1065,12 @@
1000
1065
  if (this.titleEl) {
1001
1066
  this.titleEl.textContent = title;
1002
1067
  }
1068
+ if (this.options.config && !this.options.waveform) {
1069
+ await this._loadConfig(this.options.config);
1070
+ if (this.options.title && this.titleEl) {
1071
+ this.titleEl.textContent = this.options.title;
1072
+ }
1073
+ }
1003
1074
  if (this.options.waveform) {
1004
1075
  this.setWaveformData(this.options.waveform);
1005
1076
  } else {
@@ -1040,6 +1111,12 @@
1040
1111
  if (this.isPlaying) {
1041
1112
  this.pause();
1042
1113
  }
1114
+ if (options.config) {
1115
+ await this._loadConfig(options.config);
1116
+ if (!url && this.options.url) url = this.options.url;
1117
+ if (title) this.options.title = title;
1118
+ if (subtitle) this.options.subtitle = subtitle;
1119
+ }
1043
1120
  this.audio.src = "";
1044
1121
  this.audio.load();
1045
1122
  this.hasError = false;
@@ -1054,8 +1131,9 @@
1054
1131
  }
1055
1132
  this.progress = 0;
1056
1133
  this.waveformData = [];
1134
+ const trackUrl = url || this.options.url;
1057
1135
  this.options = mergeOptions(this.options, {
1058
- url,
1136
+ url: trackUrl,
1059
1137
  title: title || this.options.title,
1060
1138
  subtitle: subtitle || this.options.subtitle,
1061
1139
  ...options
@@ -1074,16 +1152,18 @@
1074
1152
  if (options.artwork && this.artworkEl) {
1075
1153
  this.artworkEl.src = options.artwork;
1076
1154
  }
1077
- this.options.markers = options.markers || [];
1078
- await this.load(url);
1079
- this.play().catch(() => {
1080
- });
1155
+ if (options.markers) {
1156
+ this.options.markers = options.markers;
1157
+ }
1158
+ await this.load(trackUrl);
1159
+ this.play();
1081
1160
  }
1082
1161
  // ============================================
1083
1162
  // Visualization
1084
1163
  // ============================================
1085
1164
  /**
1086
- * Set waveform data
1165
+ * Set waveform data from inline data or array
1166
+ * @param {string|Array} data - Waveform peaks as array, JSON string, or CSV string
1087
1167
  * @private
1088
1168
  */
1089
1169
  setWaveformData(data) {
@@ -1121,9 +1201,10 @@
1121
1201
  return;
1122
1202
  }
1123
1203
  const dpr = window.devicePixelRatio || 1;
1124
- const rect = this.canvas.parentElement.getBoundingClientRect();
1204
+ const rect = this.canvas.getBoundingClientRect();
1125
1205
  this.canvas.width = rect.width * dpr;
1126
1206
  this.canvas.height = this.options.height * dpr;
1207
+ this.canvas.style.height = this.options.height + "px";
1127
1208
  this.canvas.parentElement.style.height = this.options.height + "px";
1128
1209
  this.drawWaveform();
1129
1210
  }
@@ -1524,6 +1605,22 @@
1524
1605
  throw error;
1525
1606
  }
1526
1607
  }
1608
+ /**
1609
+ * Load a config JSON file (useful for bar/external consumers)
1610
+ * @param {string} configUrl - URL to JSON config file
1611
+ * @returns {Promise<Object>} Parsed config with { title, subtitle, artwork, samples, peaks, markers, meta }
1612
+ */
1613
+ static async loadConfig(configUrl) {
1614
+ _WaveformPlayer._configCache = _WaveformPlayer._configCache || {};
1615
+ if (_WaveformPlayer._configCache[configUrl]) {
1616
+ return _WaveformPlayer._configCache[configUrl];
1617
+ }
1618
+ const response = await fetch(configUrl);
1619
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
1620
+ const config = await response.json();
1621
+ _WaveformPlayer._configCache[configUrl] = config;
1622
+ return config;
1623
+ }
1527
1624
  };
1528
1625
 
1529
1626
  // src/js/index.js
@@ -1,4 +1,4 @@
1
- (()=>{function L(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function C(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function A(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function R(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function P(...t){let e={};for(let i of t)for(let o in i)i[o]!==null&&i[o]!==void 0&&(e[o]=i[o]);return e}function B(t,e){let i;return function(...s){let n=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(n,e)}}function S(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let o=(t.length-1)/(e-1);for(let s=0;s<e;s++){let n=s*o,r=Math.floor(n),a=Math.ceil(n),h=n-r;if(a>=t.length)i.push(t[t.length-1]);else if(r===a)i.push(t[r]);else{let l=t[r]*(1-h)+t[a]*h;i.push(l)}}}else{let o=t.length/e;for(let s=0;s<e;s++){let n=Math.floor(s*o),r=Math.floor((s+1)*o),a=0,h=0;for(let l=n;l<=r&&l<t.length;l++)t[l]>a&&(a=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*o),t.length-1);a=t[l]}i.push(a)}}return i}function I(t,e,i,o,s){let n=window.devicePixelRatio||1,r=s.barWidth*n,a=s.barSpacing*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=o*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(r+a);if(f+r>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,r,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(r+a);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,r,c)}t.restore()}function $(t,e,i,o,s){let n=window.devicePixelRatio||1,r=s.barWidth*n,a=s.barSpacing*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=d/2,y=o*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(r+a);if(c+r>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(r+a);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.restore()}function q(t,e,i,o,s){let n=e.width,r=e.height,a=r/2,h=r*.35;t.clearRect(0,0,n,r);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,a);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*n,k=i[u],b=Math.sin(u*.1)*k,w=a+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,a),t.lineTo(n,a),t.stroke();for(let d=0;d<=10;d++){let p=n/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,r),t.stroke()}l(s.color,2,1,!1),o>0&&l(s.progressColor,3,o,!0)}function U(t,e,i,o,s){let n=window.devicePixelRatio||1,r=(s.barWidth||3)*n,a=(s.barSpacing||1)*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=4*n,y=2*n,f=o*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(r+a);if(u+r>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?s.progressColor:s.color;for(let b=0;b<k;b++){let w=b*(p+y);t.fillRect(u,c-w-p,r,p),b>0&&t.fillRect(u,c+w,r,p)}}}function F(t,e,i,o,s){let n=window.devicePixelRatio||1,r=(s.barWidth||2)*n,a=(s.barSpacing||3)*n,h=Math.floor(e.width/(r+a)),l=S(i,h),d=e.height,p=Math.max(1.5*n,r/2),y=o*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(r+a)+r/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,o,s){let n=e.width,r=e.height,a=r/2,h=4,l=h/2;if(t.clearRect(0,0,n,r),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(n-l,a-h/2),t.arc(n-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),o>0){let d=Math.max(l*2,o*n);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(d-l,a-h/2),t.arc(d-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,a,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,a,p*.4,0,Math.PI*2),t.fill()}}var Y={bars:I,mirror:$,line:q,blocks:U,dots:F,seekbar:N};function x(t,e,i,o,s){(Y[s.waveformStyle]||I)(t,e,i,o,s)}function W(t){try{let e=t.getChannelData(0),i=t.sampleRate,o=j(e,i);if(o.length<2)return 120;let s=[];for(let h=1;h<o.length;h++)s.push((o[h]-o[h-1])/i);let n={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(n[d]=(n[d]||0)+1)});let r=0,a=120;for(let[h,l]of Object.entries(n))l>r&&(r=l,a=parseInt(h));return a<70&&n[a*2]?a*=2:a>160&&n[Math.round(a/2)]&&(a=Math.round(a/2)),a-1}catch(e){return console.warn("BPM detection failed:",e),null}}function j(t,e){let s=[],n=0;for(let r=0;r<t.length-2048;r+=1024){let a=0;for(let d=r;d<r+2048;d++)a+=t[d]*t[d];a=a/2048;let h=a-n,l=n*1.8+.01;if(h>l&&a>.01){let d=s[s.length-1]||0,p=e*.15;r-d>p&&s.push(r)}n=a*.8+n*.2}return s}function V(t,e=200){let i=t.length/e,o=~~(i/10)||1,s=t.numberOfChannels,n=[];for(let a=0;a<s;a++){let h=t.getChannelData(a);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=o){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(a===0||c>n[l])&&(n[l]=c)}}let r=Math.max(...n);return r>0?n.map(a=>a/r):n}async function M(t,e=200,i=!1){try{let o=new(window.AudioContext||window.webkitAudioContext),n=await(await fetch(t)).arrayBuffer(),r=await o.decodeAudioData(n),a=V(r,e);a=J(a);let h=null;return i&&(h=await W(r)),o.close(),{peaks:a,bpm:h}}catch(o){throw console.error("Failed to generate waveform:",o),o}}function D(t=200){let e=[];for(let i=0;i<t;i++){let o=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,o+s)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let o=e/i;return t.map(s=>s*o)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let o=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(o&&o.length>=3){let[s,n,r]=o.map(Number),a=(s*299+n*587+r*114)/1e3;if(a>128)return"light";if(a<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var E={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function z(t){if(t&&E[t])return E[t];let e=G();return E[e]}var O={url:"",height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},H={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let o=L(this.container);this.options=P(O,o,i);let s=z(this.options.colorPreset);for(let[r,a]of Object.entries(s))(this.options[r]===null||this.options[r]===void 0)&&(this.options[r]=a);let n=H[this.options.waveformStyle];n&&(o.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=n.barWidth),o.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=n.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||A(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
1
+ (()=>{function B(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showControls!==void 0&&(e.showControls=t.dataset.showControls==="true"),t.dataset.showInfo!==void 0&&(e.showInfo=t.dataset.showInfo==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function M(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function S(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function I(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,s=>s.toUpperCase())}function P(...t){let e={};for(let i of t)for(let o in i)i[o]!==null&&i[o]!==void 0&&(e[o]=i[o]);return e}function x(t,e){let i;return function(...s){let r=()=>{clearTimeout(i),t(...s)};clearTimeout(i),i=setTimeout(r,e)}}function C(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let o=(t.length-1)/(e-1);for(let s=0;s<e;s++){let r=s*o,n=Math.floor(r),a=Math.ceil(r),h=r-n;if(a>=t.length)i.push(t[t.length-1]);else if(n===a)i.push(t[n]);else{let l=t[n]*(1-h)+t[a]*h;i.push(l)}}}else{let o=t.length/e;for(let s=0;s<e;s++){let r=Math.floor(s*o),n=Math.floor((s+1)*o),a=0,h=0;for(let l=r;l<=n&&l<t.length;l++)t[l]>a&&(a=t[l]),h++;if(h===0){let l=Math.min(Math.round(s*o),t.length-1);a=t[l]}i.push(a)}}return i}function W(t,e,i,o,s){let r=window.devicePixelRatio||1,n=s.barWidth*r,a=s.barSpacing*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=o*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(n+a);if(f+n>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.color,t.fillRect(f,m,n,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(n+a);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=s.progressColor,t.fillRect(f,m,n,c)}t.restore()}function $(t,e,i,o,s){let r=window.devicePixelRatio||1,n=s.barWidth*r,a=s.barSpacing*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=d/2,y=o*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(n+a);if(c+n>e.width)break;let m=l[f]*d*.45;t.fillStyle=s.color,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(n+a);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=s.progressColor,t.fillRect(c,p-m,n,m),t.fillRect(c,p,n,m)}t.restore()}function q(t,e,i,o,s){let r=e.width,n=e.height,a=n/2,h=n*.35;t.clearRect(0,0,r,n);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,a);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*r,k=i[u],g=Math.sin(u*.1)*k,w=a+g*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,g=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,g,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,a),t.lineTo(r,a),t.stroke();for(let d=0;d<=10;d++){let p=r/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,n),t.stroke()}l(s.color,2,1,!1),o>0&&l(s.progressColor,3,o,!0)}function F(t,e,i,o,s){let r=window.devicePixelRatio||1,n=(s.barWidth||3)*r,a=(s.barSpacing||1)*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=4*r,y=2*r,f=o*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(n+a);if(u+n>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?s.progressColor:s.color;for(let g=0;g<k;g++){let w=g*(p+y);t.fillRect(u,c-w-p,n,p),g>0&&t.fillRect(u,c+w,n,p)}}}function U(t,e,i,o,s){let r=window.devicePixelRatio||1,n=(s.barWidth||2)*r,a=(s.barSpacing||3)*r,h=Math.floor(e.width/(n+a)),l=C(i,h),d=e.height,p=Math.max(1.5*r,n/2),y=o*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(n+a)+n/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?s.progressColor:s.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,o,s){let r=e.width,n=e.height,a=n/2,h=4,l=h/2;if(t.clearRect(0,0,r,n),t.fillStyle=s.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(r-l,a-h/2),t.arc(r-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),o>0){let d=Math.max(l*2,o*r);t.shadowBlur=8,t.shadowColor=s.progressColor,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,a-h/2),t.lineTo(d-l,a-h/2),t.arc(d-l,a,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,a+h/2),t.arc(l,a,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,a,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=s.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,a,p*.4,0,Math.PI*2),t.fill()}}var j={bars:W,mirror:$,line:q,blocks:F,dots:U,seekbar:N};function D(t,e,i,o,s){(j[s.waveformStyle]||W)(t,e,i,o,s)}function z(t){try{let e=t.getChannelData(0),i=t.sampleRate,o=Y(e,i);if(o.length<2)return 120;let s=[];for(let h=1;h<o.length;h++)s.push((o[h]-o[h-1])/i);let r={};s.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(r[d]=(r[d]||0)+1)});let n=0,a=120;for(let[h,l]of Object.entries(r))l>n&&(n=l,a=parseInt(h));return a<70&&r[a*2]?a*=2:a>160&&r[Math.round(a/2)]&&(a=Math.round(a/2)),a-1}catch(e){return console.warn("BPM detection failed:",e),null}}function Y(t,e){let s=[],r=0;for(let n=0;n<t.length-2048;n+=1024){let a=0;for(let d=n;d<n+2048;d++)a+=t[d]*t[d];a=a/2048;let h=a-r,l=r*1.8+.01;if(h>l&&a>.01){let d=s[s.length-1]||0,p=e*.15;n-d>p&&s.push(n)}r=a*.8+r*.2}return s}function V(t,e=200){let i=t.length/e,o=~~(i/10)||1,s=t.numberOfChannels,r=[];for(let a=0;a<s;a++){let h=t.getChannelData(a);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=o){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(a===0||c>r[l])&&(r[l]=c)}}let n=Math.max(...r);return n>0?r.map(a=>a/n):r}async function E(t,e=200,i=!1){try{let o=new(window.AudioContext||window.webkitAudioContext),r=await(await fetch(t)).arrayBuffer(),n=await o.decodeAudioData(r),a=V(n,e);a=J(a);let h=null;return i&&(h=await z(n)),o.close(),{peaks:a,bpm:h}}catch(o){throw console.error("Failed to generate waveform:",o),o}}function O(t=200){let e=[];for(let i=0;i<t;i++){let o=Math.random()*.5+.3,s=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,o+s)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let o=e/i;return t.map(s=>s*o)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let o=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(o&&o.length>=3){let[s,r,n]=o.map(Number),a=(s*299+r*587+n*114)/1e3;if(a>128)return"light";if(a<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var T={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function L(t){if(t&&T[t])return T[t];let e=G();return T[e]}var A={url:"",config:null,height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showControls:!0,showInfo:!0,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},H={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var b=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let o=B(this.container);this.options=P(A,o,i);let s=L(this.options.colorPreset);for(let[n,a]of Object.entries(s))(this.options[n]===null||this.options[n]===void 0)&&(this.options[n]=a);let r=H[this.options.waveformStyle];r&&(o.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=r.barWidth),o.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=r.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.meta={},this.options.config?(this.id=this.container.id||S("pending-"+Math.random()),t.instances.set(this.id,this),this._loadConfig(this.options.config,o,i).then(()=>{!this.container.id&&this.options.url&&(t.instances.delete(this.id),this.id=S(this.options.url),t.instances.set(this.id,this)),this.init(),this._dispatchReady()})):(this.id=this.container.id||S(this.options.url),t.instances.set(this.id,this),this.init(),this._dispatchReady())}async _loadConfig(e,i={},o={}){try{t._configCache=t._configCache||{};let s;if(t._configCache[e])s=t._configCache[e];else{let a=await fetch(e);if(!a.ok)throw new Error(`HTTP ${a.status}`);s=await a.json(),t._configCache[e]=s}let r={};s.url&&(r.url=s.url),s.title&&(r.title=s.title),s.subtitle&&(r.subtitle=s.subtitle),s.artwork&&(r.artwork=s.artwork),s.album&&(r.album=s.album),s.samples&&(r.samples=s.samples),s.markers&&(r.markers=s.markers),s.peaks&&(r.waveform=s.peaks),s.meta&&(this.meta=s.meta),this.options=P(A,r,i,o);let n=L(this.options.colorPreset);for(let[a,h]of Object.entries(n))(this.options[a]===null||this.options[a]===void 0)&&(this.options[a]=h)}catch(s){console.warn("WaveformPlayer: Failed to load config:",e,s)}}_dispatchReady(){setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{bubbles:!0,detail:{player:this,url:this.options.url,meta:this.meta}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center");let i=this.options.showControls?`
2
2
  <button class="waveform-btn" aria-label="Play/Pause" style="
3
3
  border-color: ${this.options.buttonColor};
4
4
  color: ${this.options.buttonColor};
@@ -63,4 +63,4 @@
63
63
  ${o}
64
64
  </div>
65
65
  </div>
66
- `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",o=>{o.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",o=>{if(o.stopPropagation(),o.target.classList.contains("speed-option")){let s=parseFloat(o.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,o=this.audio.currentTime;if(i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let s={" ":()=>this.togglePlay(),ArrowLeft:()=>this.seekTo(Math.max(0,o-5)),ArrowRight:()=>this.seekTo(Math.min(this.audio.duration,o+5)),ArrowUp:()=>this.setVolume(Math.min(1,this.audio.volume+.1)),ArrowDown:()=>this.setVolume(Math.max(0,this.audio.volume-.1)),m:()=>this.audio.muted=!this.audio.muted,M:()=>this.audio.muted=!this.audio.muted};s[i]&&(e.preventDefault(),s[i]())})}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e)),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=B(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio.src=e,await new Promise((o,s)=>{let n=()=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),o()},r=a=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),s(a)};this.audio.addEventListener("loadedmetadata",n),this.audio.addEventListener("error",r)});let i=this.options.title||R(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let o=await M(e,this.options.samples,this.options.showBPM);this.waveformData=o.peaks,o.bpm&&(this.detectedBPM=o.bpm,this.updateBPMDisplay())}catch(o){console.warn("Using placeholder waveform:",o),this.waveformData=D(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,o=null,s={}){this.isPlaying&&this.pause(),this.audio.src="",this.audio.load(),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[],this.options=P(this.options,{url:e,title:i||this.options.title,subtitle:o||this.options.subtitle,...s}),s.preload&&(this.audio.preload=s.preload),this.subtitleEl&&(o?(this.subtitleEl.textContent=o,this.subtitleEl.style.display=""):o===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),this.options.markers=s.markers||[],await this.load(e),this.play().catch(()=>{})}setWaveformData(e){if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||x(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){!this.options.showMarkers||!this.options.markers?.length||!this.markersContainer||(this.markersContainer.innerHTML="",!(!this.audio||!this.audio.duration||this.audio.duration===0)&&this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let o=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${o}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=e.label,s.appendChild(n),s.addEventListener("click",r=>{r.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),o=e.clientX-i.left,s=Math.max(0,Math.min(1,o/i.width));this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=C(this.audio.duration)),this.renderMarkers())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=C(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this)}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){return this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),t.currentlyPlaying=this,this.audio.play()}pause(){t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio.pause()}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let o=document.getElementById(e);if(o)return Array.from(this.instances.values()).find(s=>s.container===o)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await M(e,i)).peaks}catch(o){throw console.error("Failed to generate waveform:",o),o}}};function T(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new g(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",T):T());g.init=T;typeof window<"u"&&(window.WaveformPlayer=g);var lt=g;})();
66
+ `,this.playBtn=this.container.querySelector(".waveform-btn"),this.canvas=this.container.querySelector("canvas"),this.ctx=this.canvas.getContext("2d"),this.titleEl=this.container.querySelector(".waveform-title"),this.subtitleEl=this.container.querySelector(".waveform-subtitle"),this.artworkEl=this.container.querySelector(".waveform-artwork"),this.currentTimeEl=this.container.querySelector(".time-current"),this.totalTimeEl=this.container.querySelector(".time-total"),this.bpmEl=this.container.querySelector(".waveform-bpm"),this.bpmValueEl=this.container.querySelector(".bpm-value"),this.loadingEl=this.container.querySelector(".waveform-loading"),this.errorEl=this.container.querySelector(".waveform-error"),this.markersContainer=this.container.querySelector(".waveform-markers"),this.speedBtn=this.container.querySelector(".speed-btn"),this.speedMenu=this.container.querySelector(".speed-menu"),this.resizeCanvas()}createAudio(){this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let e=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!e||!i||(e.addEventListener("click",o=>{o.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",o=>{if(o.stopPropagation(),o.target.classList.contains("speed-option")){let s=parseFloat(o.target.dataset.rate);this.setPlaybackRate(s),i.style.display="none"}}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{t.getAllInstances().forEach(e=>{e!==this&&e.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()}),this.container.addEventListener("keydown",e=>{if(document.activeElement!==this.container)return;let i=e.key,o=this.audio.currentTime;if(i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let s={" ":()=>this.togglePlay(),ArrowLeft:()=>this.seekTo(Math.max(0,o-5)),ArrowRight:()=>this.seekTo(Math.min(this.audio.duration,o+5)),ArrowUp:()=>this.setVolume(Math.min(1,this.audio.volume+.1)),ArrowDown:()=>this.setVolume(Math.max(0,this.audio.volume-.1)),m:()=>this.audio.muted=!this.audio.muted,M:()=>this.audio.muted=!this.audio.muted};s[i]&&(e.preventDefault(),s[i]())})}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||(navigator.mediaSession.metadata=new MediaMetadata({title:this.options.title||"Unknown Track",artist:this.options.subtitle||"",album:this.options.album||"",artwork:this.options.artwork?[{src:this.options.artwork,sizes:"512x512",type:"image/jpeg"}]:[]}),navigator.mediaSession.setActionHandler("play",()=>this.play()),navigator.mediaSession.setActionHandler("pause",()=>this.pause()),navigator.mediaSession.setActionHandler("seekbackward",()=>{this.seekTo(Math.max(0,this.audio.currentTime-10))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(Math.min(this.audio.duration,this.audio.currentTime+10))}),navigator.mediaSession.setActionHandler("seekto",e=>{e.seekTime!==null&&this.seekTo(e.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio.addEventListener("loadstart",()=>this.setLoading(!0)),this.audio.addEventListener("loadedmetadata",()=>this.onMetadataLoaded()),this.audio.addEventListener("canplay",()=>this.setLoading(!1)),this.audio.addEventListener("play",()=>this.onPlay()),this.audio.addEventListener("pause",()=>this.onPause()),this.audio.addEventListener("ended",()=>this.onEnded()),this.audio.addEventListener("error",e=>this.onError(e)),this.canvas.addEventListener("click",e=>this.handleCanvasClick(e)),this.resizeHandler=x(()=>this.resizeCanvas(),100),window.addEventListener("resize",this.resizeHandler)}setupResizeObserver(){"ResizeObserver"in window&&(this.resizeObserver=new ResizeObserver(()=>{this.resizeCanvas()}),this.canvas?.parentElement&&this.resizeObserver.observe(this.canvas.parentElement))}async load(e){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio.src=e,await new Promise((o,s)=>{let r=()=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),o()},n=a=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),s(a)};this.audio.addEventListener("loadedmetadata",r),this.audio.addEventListener("error",n)});let i=this.options.title||I(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.config&&!this.options.waveform&&(await this._loadConfig(this.options.config),this.options.title&&this.titleEl&&(this.titleEl.textContent=this.options.title)),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let o=await E(e,this.options.samples,this.options.showBPM);this.waveformData=o.peaks,o.bpm&&(this.detectedBPM=o.bpm,this.updateBPMDisplay())}catch(o){console.warn("Using placeholder waveform:",o),this.waveformData=O(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){console.error("Failed to load audio:",i),this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(e,i=null,o=null,s={}){this.isPlaying&&this.pause(),s.config&&(await this._loadConfig(s.config),!e&&this.options.url&&(e=this.options.url),i&&(this.options.title=i),o&&(this.options.subtitle=o)),this.audio.src="",this.audio.load(),this.hasError=!1,this.errorEl&&(this.errorEl.style.display="none"),this.canvas&&(this.canvas.style.opacity="1"),this.playBtn&&(this.playBtn.disabled=!1),this.progress=0,this.waveformData=[];let r=e||this.options.url;this.options=P(this.options,{url:r,title:i||this.options.title,subtitle:o||this.options.subtitle,...s}),s.preload&&(this.audio.preload=s.preload),this.subtitleEl&&(o?(this.subtitleEl.textContent=o,this.subtitleEl.style.display=""):o===""&&(this.subtitleEl.style.display="none")),s.artwork&&this.artworkEl&&(this.artworkEl.src=s.artwork),s.markers&&(this.options.markers=s.markers),await this.load(r),this.play()}setWaveformData(e){if(typeof e=="string")try{let i=JSON.parse(e);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=e.split(",").map(Number)}else this.waveformData=Array.isArray(e)?e:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||D(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){if(!this.canvas||this.isDestroying)return;let e=window.devicePixelRatio||1,i=this.canvas.getBoundingClientRect();this.canvas.width=i.width*e,this.canvas.height=this.options.height*e,this.canvas.style.height=this.options.height+"px",this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){!this.options.showMarkers||!this.options.markers?.length||!this.markersContainer||(this.markersContainer.innerHTML="",!(!this.audio||!this.audio.duration||this.audio.duration===0)&&this.options.markers.forEach((e,i)=>{if(e.time>this.audio.duration){console.warn(`Marker "${e.label}" at ${e.time}s exceeds audio duration of ${this.audio.duration}s`);return}let o=e.time/this.audio.duration*100,s=document.createElement("button");s.className="waveform-marker",s.style.left=`${o}%`,s.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",s.setAttribute("aria-label",e.label),s.setAttribute("data-time",e.time);let r=document.createElement("span");r.className="waveform-marker-tooltip",r.textContent=e.label,s.appendChild(r),s.addEventListener("click",n=>{n.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(s)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),o=e.clientX-i.left,s=Math.max(0,Math.min(1,o/i.width));this.seekToPercent(s)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=M(this.audio.duration)),this.renderMarkers())}onPlay(){if(!this.isDestroying){if(this.isPlaying=!0,this.playBtn){this.playBtn.classList.add("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="none"),i&&(i.style.display="flex")}this.startSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:play",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}}onPause(){if(!this.isDestroying){if(this.isPlaying=!1,this.playBtn){this.playBtn.classList.remove("playing");let e=this.playBtn.querySelector(".waveform-icon-play"),i=this.playBtn.querySelector(".waveform-icon-pause");e&&(e.style.display="flex"),i&&(i.style.display="none")}this.stopSmoothUpdate(),this.container.dispatchEvent(new CustomEvent("waveformplayer:pause",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}}onEnded(){this.isDestroying||(this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{bubbles:!0,detail:{player:this,url:this.options.url}})),this.onPause(),this.options.onEnd&&this.options.onEnd(this))}onError(e){this.isDestroying||(console.error("Audio error:",e),this.hasError=!0,this.setLoading(!1),this.errorEl&&(this.errorEl.style.display="flex"),this.canvas&&(this.canvas.style.opacity="0.2"),this.playBtn&&(this.playBtn.disabled=!0),this.options.onError&&this.options.onError(e,this))}startSmoothUpdate(){this.stopSmoothUpdate();let e=()=>{this.isPlaying&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(e))};this.updateTimer=requestAnimationFrame(e)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio.duration)return;let e=this.audio.currentTime/this.audio.duration;Math.abs(e-this.progress)>.001&&(this.progress=e,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=M(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{bubbles:!0,detail:{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,url:this.options.url}})),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this)}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){let e=this.container.querySelector(".speed-value");if(e){let i=this.audio.playbackRate;e.textContent=i===1?"1x":`${i}x`}this.container.querySelectorAll(".speed-option").forEach(i=>{i.classList.toggle("active",parseFloat(i.dataset.rate)===this.audio.playbackRate)})}play(){return this.options.singlePlay&&t.currentlyPlaying&&t.currentlyPlaying!==this&&t.currentlyPlaying.pause(),t.currentlyPlaying=this,this.audio.play()}pause(){t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio.pause()}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(e){this.audio&&this.audio.duration&&(this.audio.currentTime=Math.max(0,Math.min(e,this.audio.duration)),this.updateProgress())}seekToPercent(e){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*Math.max(0,Math.min(1,e)),this.updateProgress())}setVolume(e){this.audio&&(this.audio.volume=Math.max(0,Math.min(1,e)))}setPlaybackRate(e){if(!this.audio)return;let i=Math.max(.5,Math.min(2,e));this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this.pause(),this.stopSmoothUpdate(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),t.instances.delete(this.id),t.currentlyPlaying===this&&(t.currentlyPlaying=null),this.audio&&(this.audio.pause(),this.audio.src="",this.audio.load(),this.audio=null),this.container.innerHTML="",this.canvas=null,this.ctx=null,this.playBtn=null,this.waveformData=[]}static getInstance(e){if(typeof e=="string"){let i=this.instances.get(e);if(i)return i;let o=document.getElementById(e);if(o)return Array.from(this.instances.values()).find(s=>s.container===o)}if(e instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===e)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(e=>e.destroy()),this.instances.clear()}static async generateWaveformData(e,i=200){try{return(await E(e,i)).peaks}catch(o){throw console.error("Failed to generate waveform:",o),o}}static async loadConfig(e){if(t._configCache=t._configCache||{},t._configCache[e])return t._configCache[e];let i=await fetch(e);if(!i.ok)throw new Error(`HTTP ${i.status}`);let o=await i.json();return t._configCache[e]=o,o}};function R(){if(typeof document>"u")return;document.querySelectorAll("[data-waveform-player]").forEach(e=>{if(e.dataset.waveformInitialized!=="true")try{new b(e),e.dataset.waveformInitialized="true"}catch(i){console.error("Failed to initialize WaveformPlayer:",i,e)}})}typeof document<"u"&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",R):R());b.init=R;typeof window<"u"&&(window.WaveformPlayer=b);var lt=b;})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arraypress/waveform-player",
3
- "version": "1.3.4",
3
+ "version": "1.4.0",
4
4
  "description": "Lightweight, customizable audio player with waveform visualization",
5
5
  "main": "dist/waveform-player.js",
6
6
  "module": "dist/waveform-player.esm.js",
package/src/js/core.js CHANGED
@@ -80,21 +80,95 @@ export class WaveformPlayer {
80
80
  this.hasError = false;
81
81
  this.updateTimer = null;
82
82
  this.resizeObserver = null;
83
+ this.meta = {};
84
+
85
+ // If config URL provided, fetch it then initialize
86
+ if (this.options.config) {
87
+ // Temporary ID until config loads
88
+ this.id = this.container.id || generateId('pending-' + Math.random());
89
+ WaveformPlayer.instances.set(this.id, this);
90
+
91
+ this._loadConfig(this.options.config, dataOptions, options).then(() => {
92
+ // Update ID now that we have the real URL
93
+ if (!this.container.id && this.options.url) {
94
+ WaveformPlayer.instances.delete(this.id);
95
+ this.id = generateId(this.options.url);
96
+ WaveformPlayer.instances.set(this.id, this);
97
+ }
98
+ this.init();
99
+ this._dispatchReady();
100
+ });
101
+ } else {
102
+ this.id = this.container.id || generateId(this.options.url);
103
+ WaveformPlayer.instances.set(this.id, this);
104
+ this.init();
105
+ this._dispatchReady();
106
+ }
107
+ }
83
108
 
84
- // Generate unique ID
85
- this.id = this.container.id || generateId(this.options.url);
109
+ /**
110
+ * Load external JSON config file
111
+ * Config values are base — data attributes and JS options override them
112
+ * @param {string} configUrl - URL to JSON config file
113
+ * @param {Object} dataOptions - Parsed data attributes (for override check)
114
+ * @param {Object} jsOptions - JS constructor options (for override check)
115
+ * @private
116
+ */
117
+ async _loadConfig(configUrl, dataOptions = {}, jsOptions = {}) {
118
+ try {
119
+ // Check cache
120
+ WaveformPlayer._configCache = WaveformPlayer._configCache || {};
121
+ let config;
86
122
 
87
- // Add to instances
88
- WaveformPlayer.instances.set(this.id, this);
123
+ if (WaveformPlayer._configCache[configUrl]) {
124
+ config = WaveformPlayer._configCache[configUrl];
125
+ } else {
126
+ const response = await fetch(configUrl);
127
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
128
+ config = await response.json();
129
+ WaveformPlayer._configCache[configUrl] = config;
130
+ }
89
131
 
90
- // Initialize
91
- this.init();
132
+ // Map config fields to player options (config is the base layer)
133
+ const configOptions = {};
134
+ if (config.url) configOptions.url = config.url;
135
+ if (config.title) configOptions.title = config.title;
136
+ if (config.subtitle) configOptions.subtitle = config.subtitle;
137
+ if (config.artwork) configOptions.artwork = config.artwork;
138
+ if (config.album) configOptions.album = config.album;
139
+ if (config.samples) configOptions.samples = config.samples;
140
+ if (config.markers) configOptions.markers = config.markers;
141
+
142
+ // peaks → waveform option
143
+ if (config.peaks) configOptions.waveform = config.peaks;
144
+
145
+ // Store meta for pass-through (bar, events, etc.)
146
+ if (config.meta) this.meta = config.meta;
147
+
148
+ // Re-merge: defaults < config < data attributes < JS options
149
+ this.options = mergeOptions(DEFAULT_OPTIONS, configOptions, dataOptions, jsOptions);
150
+
151
+ // Re-apply color preset after merge
152
+ const preset = getColorPreset(this.options.colorPreset);
153
+ for (const [key, value] of Object.entries(preset)) {
154
+ if (this.options[key] === null || this.options[key] === undefined) {
155
+ this.options[key] = value;
156
+ }
157
+ }
158
+ } catch (error) {
159
+ console.warn('WaveformPlayer: Failed to load config:', configUrl, error);
160
+ }
161
+ }
92
162
 
93
- // Dispatch ready event after initialization
163
+ /**
164
+ * Dispatch ready event
165
+ * @private
166
+ */
167
+ _dispatchReady() {
94
168
  setTimeout(() => {
95
169
  this.container.dispatchEvent(new CustomEvent('waveformplayer:ready', {
96
170
  bubbles: true,
97
- detail: {player: this, url: this.options.url}
171
+ detail: {player: this, url: this.options.url, meta: this.meta}
98
172
  }));
99
173
  }, 100);
100
174
  }
@@ -488,11 +562,20 @@ export class WaveformPlayer {
488
562
  this.titleEl.textContent = title;
489
563
  }
490
564
 
565
+ // If config is set but hasn't been loaded yet, fetch it now
566
+ if (this.options.config && !this.options.waveform) {
567
+ await this._loadConfig(this.options.config);
568
+ // Update title if config provided one and none was set explicitly
569
+ if (this.options.title && this.titleEl) {
570
+ this.titleEl.textContent = this.options.title;
571
+ }
572
+ }
573
+
491
574
  // Load or generate waveform
492
575
  if (this.options.waveform) {
493
576
  this.setWaveformData(this.options.waveform);
494
577
  } else {
495
- // Generate waveform
578
+ // Generate waveform from audio
496
579
  try {
497
580
  const result = await generateWaveform(url, this.options.samples, this.options.showBPM);
498
581
  this.waveformData = result.peaks;
@@ -538,6 +621,16 @@ export class WaveformPlayer {
538
621
  this.pause();
539
622
  }
540
623
 
624
+ // If a config URL is provided, load it first
625
+ if (options.config) {
626
+ await this._loadConfig(options.config);
627
+ // Config may have set title, subtitle, url, waveform, etc.
628
+ // Explicit params still override
629
+ if (!url && this.options.url) url = this.options.url;
630
+ if (title) this.options.title = title;
631
+ if (subtitle) this.options.subtitle = subtitle;
632
+ }
633
+
541
634
  // Reset audio element completely
542
635
  this.audio.src = '';
543
636
  this.audio.load();
@@ -559,8 +652,9 @@ export class WaveformPlayer {
559
652
  this.waveformData = [];
560
653
 
561
654
  // Update options (including preload if specified)
655
+ const trackUrl = url || this.options.url;
562
656
  this.options = mergeOptions(this.options, {
563
- url,
657
+ url: trackUrl,
564
658
  title: title || this.options.title,
565
659
  subtitle: subtitle || this.options.subtitle,
566
660
  ...options
@@ -586,14 +680,16 @@ export class WaveformPlayer {
586
680
  this.artworkEl.src = options.artwork;
587
681
  }
588
682
 
589
- // Clear or update markers
590
- this.options.markers = options.markers || [];
683
+ // Clear markers if new markers provided
684
+ if (options.markers) {
685
+ this.options.markers = options.markers;
686
+ }
591
687
 
592
688
  // Load the new track
593
- await this.load(url);
689
+ await this.load(trackUrl);
594
690
 
595
691
  // Auto-play the new track
596
- this.play().catch(() => {});
692
+ this.play();
597
693
  }
598
694
 
599
695
  // ============================================
@@ -601,7 +697,8 @@ export class WaveformPlayer {
601
697
  // ============================================
602
698
 
603
699
  /**
604
- * Set waveform data
700
+ * Set waveform data from inline data or array
701
+ * @param {string|Array} data - Waveform peaks as array, JSON string, or CSV string
605
702
  * @private
606
703
  */
607
704
  setWaveformData(data) {
@@ -644,10 +741,11 @@ export class WaveformPlayer {
644
741
  }
645
742
 
646
743
  const dpr = window.devicePixelRatio || 1;
647
- const rect = this.canvas.parentElement.getBoundingClientRect();
744
+ const rect = this.canvas.getBoundingClientRect();
648
745
 
649
746
  this.canvas.width = rect.width * dpr;
650
747
  this.canvas.height = this.options.height * dpr;
748
+ this.canvas.style.height = this.options.height + 'px';
651
749
  this.canvas.parentElement.style.height = this.options.height + 'px';
652
750
 
653
751
  this.drawWaveform();
@@ -1163,4 +1261,22 @@ export class WaveformPlayer {
1163
1261
  }
1164
1262
  }
1165
1263
 
1264
+ /**
1265
+ * Load a config JSON file (useful for bar/external consumers)
1266
+ * @param {string} configUrl - URL to JSON config file
1267
+ * @returns {Promise<Object>} Parsed config with { title, subtitle, artwork, samples, peaks, markers, meta }
1268
+ */
1269
+ static async loadConfig(configUrl) {
1270
+ WaveformPlayer._configCache = WaveformPlayer._configCache || {};
1271
+ if (WaveformPlayer._configCache[configUrl]) {
1272
+ return WaveformPlayer._configCache[configUrl];
1273
+ }
1274
+
1275
+ const response = await fetch(configUrl);
1276
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
1277
+ const config = await response.json();
1278
+ WaveformPlayer._configCache[configUrl] = config;
1279
+ return config;
1280
+ }
1281
+
1166
1282
  }
package/src/js/themes.js CHANGED
@@ -121,6 +121,7 @@ export function getColorPreset(presetName) {
121
121
  export const DEFAULT_OPTIONS = {
122
122
  // Core settings
123
123
  url: '',
124
+ config: null, // URL to JSON config file (loads title, peaks, meta, etc.)
124
125
  height: 60,
125
126
  samples: 200,
126
127
  preload: 'metadata',