@arraypress/waveform-player 1.3.0 → 1.3.2

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
@@ -20,6 +20,32 @@ A lightweight, customizable audio player with waveform visualization. Under 8KB
20
20
  - **Works Everywhere** - WordPress, Shopify, React, Vue, or plain HTML
21
21
  - **Ecosystem** - Optional playlist and analytics addons available
22
22
 
23
+ ## What's New in 1.3.1
24
+
25
+ ### 🐛 Bug Fix
26
+
27
+ - Fixed uncaught `NotAllowedError` when `loadTrack()` triggers autoplay before user interaction (e.g. on session restore)
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
+
23
49
  ## What's New in 1.2.2
24
50
 
25
51
  ### 🐛 Bug Fix
@@ -64,7 +90,6 @@ WaveformPlayer now automatically adapts to your website's color scheme - no conf
64
90
  ## Quick Start
65
91
 
66
92
  Simplest possible usage:
67
-
68
93
  ```html
69
94
  <!-- Just this. That's it. -->
70
95
  <div data-waveform-player data-url="song.mp3"></div>
@@ -73,13 +98,11 @@ Simplest possible usage:
73
98
  ## Installation
74
99
 
75
100
  ### NPM
76
-
77
101
  ```bash
78
102
  npm install @arraypress/waveform-player
79
103
  ```
80
104
 
81
105
  ### CDN
82
-
83
106
  ```html
84
107
 
85
108
  <link rel="stylesheet" href="https://unpkg.com/@arraypress/waveform-player@latest/dist/waveform-player.css">
@@ -87,7 +110,6 @@ npm install @arraypress/waveform-player
87
110
  ```
88
111
 
89
112
  ### Download
90
-
91
113
  ```html
92
114
 
93
115
  <link rel="stylesheet" href="waveform-player.css">
@@ -100,6 +122,7 @@ npm install @arraypress/waveform-player
100
122
  - 🎯 **Tiny Footprint** - Under 8KB gzipped
101
123
  - ⚡ **Zero Dependencies** - Pure JavaScript
102
124
  - 🎭 **Fully Customizable** - Colors, sizes, styles
125
+ - 🎛️ **Show/Hide Controls** - Hide button and info bar for custom UIs
103
126
  - 📱 **Responsive** - Works on all devices
104
127
  - 🎵 **BPM Detection** - Automatic tempo detection (optional)
105
128
  - 💾 **Waveform Caching** - Pre-generate waveforms for performance
@@ -115,7 +138,6 @@ npm install @arraypress/waveform-player
115
138
  ### WaveformPlaylist (Optional Addon)
116
139
 
117
140
  Add playlist and chapter support with zero JavaScript:
118
-
119
141
  ```html
120
142
 
121
143
  <div data-waveform-playlist data-continuous="true">
@@ -132,7 +154,6 @@ Add playlist and chapter support with zero JavaScript:
132
154
  ### WaveformTracker (Optional Addon)
133
155
 
134
156
  Track meaningful audio engagement:
135
-
136
157
  ```javascript
137
158
  WaveformTracker.init({
138
159
  endpoint: '/api/analytics',
@@ -161,7 +182,6 @@ WaveformTracker.init({
161
182
  ## Usage
162
183
 
163
184
  ### HTML (Zero JavaScript)
164
-
165
185
  ```html
166
186
 
167
187
  <div data-waveform-player
@@ -175,7 +195,6 @@ WaveformTracker.init({
175
195
  ```
176
196
 
177
197
  ### JavaScript API
178
-
179
198
  ```javascript
180
199
  import WaveformPlayer from '@arraypress/waveform-player';
181
200
 
@@ -216,6 +235,8 @@ Choose from 6 built-in styles:
216
235
  | `waveformColor` | string | `'rgba(255,255,255,0.3)'` | Waveform color |
217
236
  | `progressColor` | string | `'rgba(255,255,255,0.9)'` | Progress color |
218
237
  | `buttonColor` | string | `'rgba(255,255,255,0.9)'` | Play button color |
238
+ | `showControls` | boolean | `true` | Show play/pause button |
239
+ | `showInfo` | boolean | `true` | Show title, subtitle, time, and metadata |
219
240
  | `showTime` | boolean | `true` | Show time display |
220
241
  | `showBPM` | boolean | `false` | Enable BPM detection |
221
242
  | `showPlaybackSpeed` | boolean | `false` | Show speed control menu |
@@ -228,7 +249,6 @@ Choose from 6 built-in styles:
228
249
  | `enableMediaSession` | boolean | `true` | Enable system media controls |
229
250
 
230
251
  ## API Methods
231
-
232
252
  ```javascript
233
253
  // Control playback
234
254
  player.play();
@@ -253,7 +273,6 @@ player.destroy();
253
273
  ```
254
274
 
255
275
  ## Events
256
-
257
276
  ```javascript
258
277
  new WaveformPlayer('#player', {
259
278
  url: 'audio.mp3',
@@ -282,7 +301,6 @@ When a player is focused (click on it):
282
301
  ### Pre-generated Waveforms
283
302
 
284
303
  For better performance, generate waveform data server-side:
285
-
286
304
  ```javascript
287
305
  // Generate waveform data once
288
306
  const waveformData = await WaveformPlayer.generateWaveformData('audio.mp3');
@@ -295,7 +313,6 @@ new WaveformPlayer('#player', {
295
313
  ```
296
314
 
297
315
  ### Multiple Players
298
-
299
316
  ```javascript
300
317
  // Get all instances
301
318
  const players = WaveformPlayer.getAllInstances();
@@ -308,7 +325,6 @@ WaveformPlayer.destroyAll();
308
325
  ```
309
326
 
310
327
  ### Custom Styling
311
-
312
328
  ```css
313
329
  .waveform-player {
314
330
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -323,7 +339,6 @@ WaveformPlayer.destroyAll();
323
339
  ## Framework Integration
324
340
 
325
341
  ### React
326
-
327
342
  ```jsx
328
343
  import {useEffect, useRef} from 'react';
329
344
  import WaveformPlayer from '@arraypress/waveform-player';
@@ -341,7 +356,6 @@ function AudioPlayer({url}) {
341
356
  ```
342
357
 
343
358
  ### Vue
344
-
345
359
  ```vue
346
360
 
347
361
  <template>
@@ -388,7 +402,6 @@ See the [live demo](https://waveformplayer.com) for:
388
402
  - Analytics tracking
389
403
 
390
404
  ## Development
391
-
392
405
  ```bash
393
406
  # Install dependencies
394
407
  npm install
@@ -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),s.markers&&(this.options.markers=s.markers),await this.load(e),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||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.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 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=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),s.markers&&(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.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 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};
@@ -1078,7 +1078,8 @@
1078
1078
  this.options.markers = options.markers;
1079
1079
  }
1080
1080
  await this.load(url);
1081
- this.play();
1081
+ this.play().catch(() => {
1082
+ });
1082
1083
  }
1083
1084
  // ============================================
1084
1085
  // Visualization
@@ -1122,7 +1123,7 @@
1122
1123
  return;
1123
1124
  }
1124
1125
  const dpr = window.devicePixelRatio || 1;
1125
- const rect = this.canvas.getBoundingClientRect();
1126
+ const rect = this.canvas.parentElement.getBoundingClientRect();
1126
1127
  this.canvas.width = rect.width * dpr;
1127
1128
  this.canvas.height = this.options.height * dpr;
1128
1129
  this.canvas.style.height = this.options.height + "px";
@@ -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),s.markers&&(this.options.markers=s.markers),await this.load(e),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||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.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 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=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),s.markers&&(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.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 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;})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arraypress/waveform-player",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
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
@@ -595,7 +595,7 @@ export class WaveformPlayer {
595
595
  await this.load(url);
596
596
 
597
597
  // Auto-play the new track
598
- this.play();
598
+ this.play().catch(() => {});
599
599
  }
600
600
 
601
601
  // ============================================
@@ -646,7 +646,7 @@ export class WaveformPlayer {
646
646
  }
647
647
 
648
648
  const dpr = window.devicePixelRatio || 1;
649
- const rect = this.canvas.getBoundingClientRect();
649
+ const rect = this.canvas.parentElement.getBoundingClientRect();
650
650
 
651
651
  this.canvas.width = rect.width * dpr;
652
652
  this.canvas.height = this.options.height * dpr;