@arraypress/waveform-player 1.8.0 → 1.8.1
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/dist/waveform-player.cjs +1 -0
- package/dist/waveform-player.cjs.map +2 -2
- package/dist/waveform-player.esm.js +1 -1
- package/dist/waveform-player.esm.js.map +2 -2
- package/dist/waveform-player.js +1 -0
- package/dist/waveform-player.min.js +1 -1
- package/dist/waveform-player.min.js.map +2 -2
- package/package.json +1 -1
- package/src/js/core.js +8 -0
package/dist/waveform-player.js
CHANGED
|
@@ -1415,6 +1415,7 @@
|
|
|
1415
1415
|
this.artworkEl.src = options.artwork;
|
|
1416
1416
|
}
|
|
1417
1417
|
this.options.markers = options.markers || [];
|
|
1418
|
+
this.options.waveform = options.waveform || null;
|
|
1418
1419
|
await this.load(url);
|
|
1419
1420
|
if (options.autoplay !== false) {
|
|
1420
1421
|
this.play()?.catch(() => {
|
|
@@ -63,4 +63,4 @@
|
|
|
63
63
|
${s}
|
|
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(){if(this.options.audioMode==="external"){this.audio=null;return}this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.audio&&this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let t=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!t||!i||(t.addEventListener("click",s=>{s.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"},{signal:this._ac.signal}),document.addEventListener("click",()=>{i.style.display="none"},{signal:this._ac.signal}),i.addEventListener("click",s=>{if(s.stopPropagation(),s.target.classList.contains("speed-option")){let o=parseFloat(s.target.dataset.rate);this.setPlaybackRate(o),i.style.display="none"}},{signal:this._ac.signal}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{e.getAllInstances().forEach(t=>{t!==this&&t.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()},{signal:this._ac.signal}),this.container.addEventListener("keydown",t=>{if(document.activeElement!==this.container)return;let i=t.key,s=!!this.audio,o=s?this.audio.currentTime:0;if(s&&i>="0"&&i<="9"){t.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let r={" ":()=>this.togglePlay()};s&&(r.ArrowLeft=()=>this.seekTo(w(o-5,0,this.audio.duration)),r.ArrowRight=()=>this.seekTo(w(o+5,0,this.audio.duration)),r.ArrowUp=()=>this.setVolume(w(this.audio.volume+.1)),r.ArrowDown=()=>this.setVolume(w(this.audio.volume-.1)),r.m=r.M=()=>this.audio.muted=!this.audio.muted),r[i]&&(t.preventDefault(),r[i]())},{signal:this._ac.signal})}initSeekControl(){this.options.accessibleSeek&&(this.seekEl=this.container.querySelector(".waveform-container"),this.seekEl&&(this.seekEl.setAttribute("role","slider"),this.seekEl.setAttribute("tabindex","0"),this.seekEl.setAttribute("aria-valuemin","0"),this.applySeekLabel(),this.updateSeekAccessibility(),this.seekEl.addEventListener("keydown",t=>{let i=this.getSeekDuration();if(!i)return;let s=this.getSeekCurrentTime(),o;switch(t.key){case"ArrowLeft":case"ArrowDown":o=s-tt;break;case"ArrowRight":case"ArrowUp":o=s+tt;break;case"PageDown":o=s-et;break;case"PageUp":o=s+et;break;case"Home":o=0;break;case"End":o=i;break;default:return}t.preventDefault(),t.stopPropagation(),this.seekToSeconds(o)},{signal:this._ac.signal})))}getSeekDuration(){return this.options.audioMode==="external"?this._extDuration||0:this.audio&&Number.isFinite(this.audio.duration)?this.audio.duration:0}getSeekCurrentTime(){return this.options.audioMode==="external"?this.progress*(this._extDuration||0):this.audio&&Number.isFinite(this.audio.currentTime)?this.audio.currentTime:0}seekToSeconds(t){let i=this.getSeekDuration();if(!i)return;let s=w(t,0,i);if(this.options.audioMode==="external"){this._requestSeek(s/i),this.updateSeekAccessibility();return}this.seekTo(s)}applySeekLabel(t=this.options.title){if(!this.seekEl)return;let i=this.options.seekLabel||t||"Seek";this.seekEl.setAttribute("aria-label",i)}updateSeekAccessibility(){if(!this.seekEl)return;let t=this.getSeekDuration(),i=Math.min(this.getSeekCurrentTime(),t);this.seekEl.setAttribute("aria-valuemax",String(Math.round(t))),this.seekEl.setAttribute("aria-valuenow",String(Math.round(i))),this.seekEl.setAttribute("aria-valuetext",`${S(i)} of ${S(t)}`)}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||this.audio&&(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(w(this.audio.currentTime-10,0,this.audio.duration))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(w(this.audio.currentTime+10,0,this.audio.duration))}),navigator.mediaSession.setActionHandler("seekto",t=>{t.seekTime!==null&&this.seekTo(t.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio&&(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",t=>this.onError(t))),this.canvas.addEventListener("click",t=>this.handleCanvasClick(t)),this.resizeHandler=F(()=>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(t){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio&&(this.audio.src=t,await new Promise((s,o)=>{let r=()=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),s()},n=a=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),o(a)};this.audio.addEventListener("loadedmetadata",r),this.audio.addEventListener("error",n)}));let i=this.options.title||L(t);if(this.titleEl&&(this.titleEl.textContent=i),this.applySeekLabel(i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let s=await R(t,this.options.samples,this.options.showBPM);this.waveformData=s.peaks,s.bpm&&(this.detectedBPM=s.bpm,this.updateBPMDisplay())}catch(s){console.warn("[WaveformPlayer] Using placeholder waveform:",s),this.waveformData=G(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(t,i=null,s=null,o={}){this.isPlaying&&this.pause(),this.audio&&(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=x(this.options,{url:t,title:i||this.options.title,subtitle:s||this.options.subtitle,...o}),o.preload&&this.audio&&(this.audio.preload=o.preload),this.subtitleEl&&(s?(this.subtitleEl.textContent=s,this.subtitleEl.style.display=""):s===""&&(this.subtitleEl.style.display="none")),o.artwork&&this.artworkEl&&(this.artworkEl.src=o.artwork),this.options.markers=o.markers||[],await this.load(t),o.autoplay!==!1&&this.play()?.catch(()=>{})}setWaveformData(t){if(typeof t=="string"&&t.trim().endsWith(".json")){fetch(t.trim()).then(i=>i.json()).then(i=>{this.waveformData=Array.isArray(i)?i:i.peaks||[],i.markers&&!this.options.markers?.length&&(this.options.markers=i.markers,this.renderMarkers()),this.drawWaveform()}).catch(()=>{});return}if(typeof t=="string")try{let i=JSON.parse(t);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=t.split(",").map(Number)}else this.waveformData=Array.isArray(t)?t:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||J(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 t=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*t,this.canvas.height=this.options.height*t,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){if(!this.markersContainer||(this.markersContainer.innerHTML="",!this.options.showMarkers||!this.options.markers?.length))return;let t=this.getSeekDuration();t&&this.options.markers.forEach((i,s)=>{if(i.time>t){console.warn(`[WaveformPlayer] Marker "${i.label}" at ${i.time}s exceeds audio duration of ${t}s`);return}let o=i.time/t*100,r=document.createElement("button");r.className="waveform-marker",r.style.left=`${o}%`,r.style.backgroundColor=i.color||"rgba(255, 255, 255, 0.5)",r.setAttribute("aria-label",i.label),r.setAttribute("data-time",i.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=i.label,r.appendChild(n),r.addEventListener("click",a=>{a.stopPropagation(),this.seekTo(i.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(r)})}setActiveMarker(t){if(!this.markersContainer)return;this.markersContainer.querySelectorAll(".waveform-marker").forEach((s,o)=>s.classList.toggle("active",o===t))}handleCanvasClick(t){let i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,o=w(s/i.width);if(this.options.audioMode==="external"){this._requestSeek(o);return}!this.audio||!this.audio.duration||this.seekToPercent(o)}setLoading(t){this.isLoading=t,this.loadingEl&&(this.loadingEl.style.display=t?"block":"none"),this.seekEl&&this.seekEl.setAttribute("aria-busy",t?"true":"false")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=S(this.audio.duration)),this.renderMarkers(),this.updateSeekAccessibility())}setPlayButtonState(t){if(!this.playBtn)return;this.playBtn.classList.toggle("playing",t);let i=this.playBtn.querySelector(".waveform-icon-play"),s=this.playBtn.querySelector(".waveform-icon-pause");i&&(i.style.display=t?"none":"flex"),s&&(s.style.display=t?"flex":"none")}onPlay(){this.isDestroying||(this.isPlaying=!0,this.setPlayButtonState(!0),this.startSmoothUpdate(),this._emit("waveformplayer:play",{player:this,url:this.options.url}),this.options.onPlay&&this.options.onPlay(this))}onPause(){this.isDestroying||(this.isPlaying=!1,this.setPlayButtonState(!1),this.stopSmoothUpdate(),this._emit("waveformplayer:pause",{player:this,url:this.options.url}),this.options.onPause&&this.options.onPause(this))}onEnded(){if(this.isDestroying)return;let t=this.audio.duration;this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this._emit("waveformplayer:ended",{player:this,url:this.options.url,currentTime:t,duration:t}),this.onPause(),this.options.onEnd&&this.options.onEnd(this)}onError(t){this.isDestroying||(console.error("[WaveformPlayer] Audio error:",t),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(t,this))}startSmoothUpdate(){this.stopSmoothUpdate();let t=()=>{this.isPlaying&&this.audio&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(t))};this.updateTimer=requestAnimationFrame(t)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio||!this.audio.duration)return;let t=this.audio.currentTime/this.audio.duration;Math.abs(t-this.progress)>.001&&(this.progress=t,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=S(this.audio.currentTime)),this._emit("waveformplayer:timeupdate",{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,progress:this.progress,url:this.options.url}),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this),this.updateSeekAccessibility()}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){if(!this.audio)return;let t=this.container.querySelector(".speed-value");if(t){let i=this.audio.playbackRate;t.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(){if(this.options.singlePlay&&e.currentlyPlaying&&e.currentlyPlaying!==this&&e.currentlyPlaying.pause(),this.options.audioMode==="external"){this._emit("waveformplayer:request-play",this._buildTrackDetail(),!0).defaultPrevented||(e.currentlyPlaying=this);return}return e.currentlyPlaying=this,this.audio.play()}pause(){if(e.currentlyPlaying===this&&(e.currentlyPlaying=null),this.options.audioMode==="external"){this._emit("waveformplayer:request-pause",this._buildTrackDetail(),!0);return}this.audio.pause()}_buildTrackDetail(){return{url:this.options.url,title:this.options.title,subtitle:this.options.subtitle,artist:this.options.artist||this.options.subtitle,artwork:this.options.artwork,markers:this.options.markers,waveform:this.options.waveform,id:this.id,player:this}}setPlayingState(t){let i=this.isPlaying;this.isPlaying=!!t,this.setPlayButtonState(this.isPlaying),this.isPlaying&&!i?(this.startSmoothUpdate?.(),this._emit("waveformplayer:play",{player:this,url:this.options.url}),this.options.onPlay&&this.options.onPlay(this)):!this.isPlaying&&i&&(this.stopSmoothUpdate?.(),this._emit("waveformplayer:pause",{player:this,url:this.options.url}),this.options.onPause&&this.options.onPause(this))}setProgress(t,i){!i||i<=0||(this.progress=w(t/i),this.currentTimeEl&&(this.currentTimeEl.textContent=S(t)),this._extDuration=i,this.totalTimeEl&&(!this.totalTimeEl.dataset._extSet||this.totalTimeEl.dataset._extDur!==String(i))&&(this.totalTimeEl.textContent=S(i),this.totalTimeEl.dataset._extSet="1",this.totalTimeEl.dataset._extDur=String(i)),this.drawWaveform?.(),this._emit("waveformplayer:timeupdate",{player:this,currentTime:t,duration:i,progress:this.progress,url:this.options.url}),this.options.onTimeUpdate&&this.options.onTimeUpdate(t,i,this),this.progress>=1?this._extEnded||(this._extEnded=!0,this._emit("waveformplayer:ended",{player:this,url:this.options.url,currentTime:i,duration:i}),this.options.onEnd&&this.options.onEnd(this)):this._extEnded=!1,this.updateSeekAccessibility())}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(t){this.audio&&this.audio.duration&&(this.audio.currentTime=w(t,0,this.audio.duration),this.updateProgress())}seekToPercent(t){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*w(t),this.updateProgress())}setVolume(t){let i=Number(t);this.audio&&Number.isFinite(i)&&(this.audio.volume=w(i))}setPlaybackRate(t){if(!this.audio)return;let i=w(t,.5,2);this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this._emit("waveformplayer:destroy",{player:this,url:this.options.url}),this.pause(),this.stopSmoothUpdate(),this._ac?.abort(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),e.instances.delete(this.id),e.currentlyPlaying===this&&(e.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(t){if(typeof t=="string"){let i=this.instances.get(t);if(i)return i;let s=document.getElementById(t);if(s)return Array.from(this.instances.values()).find(o=>o.container===s)}if(t instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===t)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(t=>t.destroy()),this.instances.clear()}static async generateWaveformData(t,i=200){try{return(await R(t,i)).peaks}catch(s){throw console.error("[WaveformPlayer] Failed to generate waveform:",s),s}}static getPeaksUrl(t){if(!t)return;let i=t.replace(/\.(mp3|wav|ogg|flac|m4a|aac)(\?[^#]*)?(#.*)?$/i,".json$2$3");return i===t?void 0:i}};C.utils={formatTime:S,extractTitleFromUrl:L,escapeHtml:A,isSafeHref:z};var I=()=>typeof window<"u"&&typeof document<"u";function W(){if(!I())return;document.querySelectorAll("[data-waveform-player]").forEach(t=>{if(t.dataset.waveformInitialized!=="true")try{new C(t),t.dataset.waveformInitialized="true"}catch(i){console.error("[WaveformPlayer] Failed to initialize:",i,t)}})}I()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",W):W());C.init=W;I()&&(window.WaveformPlayer=C);var Lt=C;})();
|
|
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(){if(this.options.audioMode==="external"){this.audio=null;return}this.audio=new Audio,this.audio.preload=this.options.preload||"metadata",this.audio.crossOrigin="anonymous"}initPlaybackSpeed(){this.audio&&this.options.playbackRate&&this.options.playbackRate!==1&&(this.audio.playbackRate=this.options.playbackRate),this.options.showPlaybackSpeed&&this.initSpeedControls()}initSpeedControls(){let t=this.container.querySelector(".speed-btn"),i=this.container.querySelector(".speed-menu");!t||!i||(t.addEventListener("click",s=>{s.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"},{signal:this._ac.signal}),document.addEventListener("click",()=>{i.style.display="none"},{signal:this._ac.signal}),i.addEventListener("click",s=>{if(s.stopPropagation(),s.target.classList.contains("speed-option")){let o=parseFloat(s.target.dataset.rate);this.setPlaybackRate(o),i.style.display="none"}},{signal:this._ac.signal}),this.updateSpeedUI())}initKeyboardControls(){this.container.setAttribute("tabindex","-1"),this.container.addEventListener("click",()=>{e.getAllInstances().forEach(t=>{t!==this&&t.container.setAttribute("tabindex","-1")}),this.container.setAttribute("tabindex","0"),this.container.focus()},{signal:this._ac.signal}),this.container.addEventListener("keydown",t=>{if(document.activeElement!==this.container)return;let i=t.key,s=!!this.audio,o=s?this.audio.currentTime:0;if(s&&i>="0"&&i<="9"){t.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let r={" ":()=>this.togglePlay()};s&&(r.ArrowLeft=()=>this.seekTo(w(o-5,0,this.audio.duration)),r.ArrowRight=()=>this.seekTo(w(o+5,0,this.audio.duration)),r.ArrowUp=()=>this.setVolume(w(this.audio.volume+.1)),r.ArrowDown=()=>this.setVolume(w(this.audio.volume-.1)),r.m=r.M=()=>this.audio.muted=!this.audio.muted),r[i]&&(t.preventDefault(),r[i]())},{signal:this._ac.signal})}initSeekControl(){this.options.accessibleSeek&&(this.seekEl=this.container.querySelector(".waveform-container"),this.seekEl&&(this.seekEl.setAttribute("role","slider"),this.seekEl.setAttribute("tabindex","0"),this.seekEl.setAttribute("aria-valuemin","0"),this.applySeekLabel(),this.updateSeekAccessibility(),this.seekEl.addEventListener("keydown",t=>{let i=this.getSeekDuration();if(!i)return;let s=this.getSeekCurrentTime(),o;switch(t.key){case"ArrowLeft":case"ArrowDown":o=s-tt;break;case"ArrowRight":case"ArrowUp":o=s+tt;break;case"PageDown":o=s-et;break;case"PageUp":o=s+et;break;case"Home":o=0;break;case"End":o=i;break;default:return}t.preventDefault(),t.stopPropagation(),this.seekToSeconds(o)},{signal:this._ac.signal})))}getSeekDuration(){return this.options.audioMode==="external"?this._extDuration||0:this.audio&&Number.isFinite(this.audio.duration)?this.audio.duration:0}getSeekCurrentTime(){return this.options.audioMode==="external"?this.progress*(this._extDuration||0):this.audio&&Number.isFinite(this.audio.currentTime)?this.audio.currentTime:0}seekToSeconds(t){let i=this.getSeekDuration();if(!i)return;let s=w(t,0,i);if(this.options.audioMode==="external"){this._requestSeek(s/i),this.updateSeekAccessibility();return}this.seekTo(s)}applySeekLabel(t=this.options.title){if(!this.seekEl)return;let i=this.options.seekLabel||t||"Seek";this.seekEl.setAttribute("aria-label",i)}updateSeekAccessibility(){if(!this.seekEl)return;let t=this.getSeekDuration(),i=Math.min(this.getSeekCurrentTime(),t);this.seekEl.setAttribute("aria-valuemax",String(Math.round(t))),this.seekEl.setAttribute("aria-valuenow",String(Math.round(i))),this.seekEl.setAttribute("aria-valuetext",`${S(i)} of ${S(t)}`)}initMediaSession(){!("mediaSession"in navigator)||!this.options.enableMediaSession||this.audio&&(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(w(this.audio.currentTime-10,0,this.audio.duration))}),navigator.mediaSession.setActionHandler("seekforward",()=>{this.seekTo(w(this.audio.currentTime+10,0,this.audio.duration))}),navigator.mediaSession.setActionHandler("seekto",t=>{t.seekTime!==null&&this.seekTo(t.seekTime)}))}bindEvents(){this.playBtn&&this.playBtn.addEventListener("click",()=>this.togglePlay()),this.audio&&(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",t=>this.onError(t))),this.canvas.addEventListener("click",t=>this.handleCanvasClick(t)),this.resizeHandler=F(()=>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(t){try{this.setLoading(!0),this.progress=0,this.hasError=!1,this.audio&&(this.audio.src=t,await new Promise((s,o)=>{let r=()=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),s()},n=a=>{this.audio.removeEventListener("loadedmetadata",r),this.audio.removeEventListener("error",n),o(a)};this.audio.addEventListener("loadedmetadata",r),this.audio.addEventListener("error",n)}));let i=this.options.title||L(t);if(this.titleEl&&(this.titleEl.textContent=i),this.applySeekLabel(i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let s=await R(t,this.options.samples,this.options.showBPM);this.waveformData=s.peaks,s.bpm&&(this.detectedBPM=s.bpm,this.updateBPMDisplay())}catch(s){console.warn("[WaveformPlayer] Using placeholder waveform:",s),this.waveformData=G(this.options.samples)}this.drawWaveform(),this.renderMarkers(),this.initMediaSession(),this.options.onLoad&&this.options.onLoad(this)}catch(i){this.onError(i)}finally{this.setLoading(!1)}}async loadTrack(t,i=null,s=null,o={}){this.isPlaying&&this.pause(),this.audio&&(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=x(this.options,{url:t,title:i||this.options.title,subtitle:s||this.options.subtitle,...o}),o.preload&&this.audio&&(this.audio.preload=o.preload),this.subtitleEl&&(s?(this.subtitleEl.textContent=s,this.subtitleEl.style.display=""):s===""&&(this.subtitleEl.style.display="none")),o.artwork&&this.artworkEl&&(this.artworkEl.src=o.artwork),this.options.markers=o.markers||[],this.options.waveform=o.waveform||null,await this.load(t),o.autoplay!==!1&&this.play()?.catch(()=>{})}setWaveformData(t){if(typeof t=="string"&&t.trim().endsWith(".json")){fetch(t.trim()).then(i=>i.json()).then(i=>{this.waveformData=Array.isArray(i)?i:i.peaks||[],i.markers&&!this.options.markers?.length&&(this.options.markers=i.markers,this.renderMarkers()),this.drawWaveform()}).catch(()=>{});return}if(typeof t=="string")try{let i=JSON.parse(t);this.waveformData=Array.isArray(i)?i:[]}catch{this.waveformData=t.split(",").map(Number)}else this.waveformData=Array.isArray(t)?t:[];this.drawWaveform()}drawWaveform(){!this.ctx||this.waveformData.length===0||J(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 t=window.devicePixelRatio||1,i=this.canvas.parentElement.getBoundingClientRect();this.canvas.width=i.width*t,this.canvas.height=this.options.height*t,this.canvas.parentElement.style.height=this.options.height+"px",this.drawWaveform()}renderMarkers(){if(!this.markersContainer||(this.markersContainer.innerHTML="",!this.options.showMarkers||!this.options.markers?.length))return;let t=this.getSeekDuration();t&&this.options.markers.forEach((i,s)=>{if(i.time>t){console.warn(`[WaveformPlayer] Marker "${i.label}" at ${i.time}s exceeds audio duration of ${t}s`);return}let o=i.time/t*100,r=document.createElement("button");r.className="waveform-marker",r.style.left=`${o}%`,r.style.backgroundColor=i.color||"rgba(255, 255, 255, 0.5)",r.setAttribute("aria-label",i.label),r.setAttribute("data-time",i.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=i.label,r.appendChild(n),r.addEventListener("click",a=>{a.stopPropagation(),this.seekTo(i.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(r)})}setActiveMarker(t){if(!this.markersContainer)return;this.markersContainer.querySelectorAll(".waveform-marker").forEach((s,o)=>s.classList.toggle("active",o===t))}handleCanvasClick(t){let i=this.canvas.getBoundingClientRect(),s=t.clientX-i.left,o=w(s/i.width);if(this.options.audioMode==="external"){this._requestSeek(o);return}!this.audio||!this.audio.duration||this.seekToPercent(o)}setLoading(t){this.isLoading=t,this.loadingEl&&(this.loadingEl.style.display=t?"block":"none"),this.seekEl&&this.seekEl.setAttribute("aria-busy",t?"true":"false")}onMetadataLoaded(){this.isDestroying||(this.totalTimeEl&&(this.totalTimeEl.textContent=S(this.audio.duration)),this.renderMarkers(),this.updateSeekAccessibility())}setPlayButtonState(t){if(!this.playBtn)return;this.playBtn.classList.toggle("playing",t);let i=this.playBtn.querySelector(".waveform-icon-play"),s=this.playBtn.querySelector(".waveform-icon-pause");i&&(i.style.display=t?"none":"flex"),s&&(s.style.display=t?"flex":"none")}onPlay(){this.isDestroying||(this.isPlaying=!0,this.setPlayButtonState(!0),this.startSmoothUpdate(),this._emit("waveformplayer:play",{player:this,url:this.options.url}),this.options.onPlay&&this.options.onPlay(this))}onPause(){this.isDestroying||(this.isPlaying=!1,this.setPlayButtonState(!1),this.stopSmoothUpdate(),this._emit("waveformplayer:pause",{player:this,url:this.options.url}),this.options.onPause&&this.options.onPause(this))}onEnded(){if(this.isDestroying)return;let t=this.audio.duration;this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this._emit("waveformplayer:ended",{player:this,url:this.options.url,currentTime:t,duration:t}),this.onPause(),this.options.onEnd&&this.options.onEnd(this)}onError(t){this.isDestroying||(console.error("[WaveformPlayer] Audio error:",t),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(t,this))}startSmoothUpdate(){this.stopSmoothUpdate();let t=()=>{this.isPlaying&&this.audio&&this.audio.duration&&(this.updateProgress(),this.updateTimer=requestAnimationFrame(t))};this.updateTimer=requestAnimationFrame(t)}stopSmoothUpdate(){this.updateTimer&&(cancelAnimationFrame(this.updateTimer),this.updateTimer=null)}updateProgress(){if(!this.audio||!this.audio.duration)return;let t=this.audio.currentTime/this.audio.duration;Math.abs(t-this.progress)>.001&&(this.progress=t,this.drawWaveform()),this.currentTimeEl&&(this.currentTimeEl.textContent=S(this.audio.currentTime)),this._emit("waveformplayer:timeupdate",{player:this,currentTime:this.audio.currentTime,duration:this.audio.duration,progress:this.progress,url:this.options.url}),this.options.onTimeUpdate&&this.options.onTimeUpdate(this.audio.currentTime,this.audio.duration,this),this.updateSeekAccessibility()}updateBPMDisplay(){this.bpmEl&&this.bpmValueEl&&this.detectedBPM&&(this.bpmValueEl.textContent=Math.round(this.detectedBPM),this.bpmEl.style.display="inline-flex")}updateSpeedUI(){if(!this.audio)return;let t=this.container.querySelector(".speed-value");if(t){let i=this.audio.playbackRate;t.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(){if(this.options.singlePlay&&e.currentlyPlaying&&e.currentlyPlaying!==this&&e.currentlyPlaying.pause(),this.options.audioMode==="external"){this._emit("waveformplayer:request-play",this._buildTrackDetail(),!0).defaultPrevented||(e.currentlyPlaying=this);return}return e.currentlyPlaying=this,this.audio.play()}pause(){if(e.currentlyPlaying===this&&(e.currentlyPlaying=null),this.options.audioMode==="external"){this._emit("waveformplayer:request-pause",this._buildTrackDetail(),!0);return}this.audio.pause()}_buildTrackDetail(){return{url:this.options.url,title:this.options.title,subtitle:this.options.subtitle,artist:this.options.artist||this.options.subtitle,artwork:this.options.artwork,markers:this.options.markers,waveform:this.options.waveform,id:this.id,player:this}}setPlayingState(t){let i=this.isPlaying;this.isPlaying=!!t,this.setPlayButtonState(this.isPlaying),this.isPlaying&&!i?(this.startSmoothUpdate?.(),this._emit("waveformplayer:play",{player:this,url:this.options.url}),this.options.onPlay&&this.options.onPlay(this)):!this.isPlaying&&i&&(this.stopSmoothUpdate?.(),this._emit("waveformplayer:pause",{player:this,url:this.options.url}),this.options.onPause&&this.options.onPause(this))}setProgress(t,i){!i||i<=0||(this.progress=w(t/i),this.currentTimeEl&&(this.currentTimeEl.textContent=S(t)),this._extDuration=i,this.totalTimeEl&&(!this.totalTimeEl.dataset._extSet||this.totalTimeEl.dataset._extDur!==String(i))&&(this.totalTimeEl.textContent=S(i),this.totalTimeEl.dataset._extSet="1",this.totalTimeEl.dataset._extDur=String(i)),this.drawWaveform?.(),this._emit("waveformplayer:timeupdate",{player:this,currentTime:t,duration:i,progress:this.progress,url:this.options.url}),this.options.onTimeUpdate&&this.options.onTimeUpdate(t,i,this),this.progress>=1?this._extEnded||(this._extEnded=!0,this._emit("waveformplayer:ended",{player:this,url:this.options.url,currentTime:i,duration:i}),this.options.onEnd&&this.options.onEnd(this)):this._extEnded=!1,this.updateSeekAccessibility())}togglePlay(){this.isPlaying?this.pause():this.play()}seekTo(t){this.audio&&this.audio.duration&&(this.audio.currentTime=w(t,0,this.audio.duration),this.updateProgress())}seekToPercent(t){this.audio&&this.audio.duration&&(this.audio.currentTime=this.audio.duration*w(t),this.updateProgress())}setVolume(t){let i=Number(t);this.audio&&Number.isFinite(i)&&(this.audio.volume=w(i))}setPlaybackRate(t){if(!this.audio)return;let i=w(t,.5,2);this.audio.playbackRate=i,this.options.playbackRate=i,this.updateSpeedUI()}destroy(){this.isDestroying=!0,this._emit("waveformplayer:destroy",{player:this,url:this.options.url}),this.pause(),this.stopSmoothUpdate(),this._ac?.abort(),this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.resizeHandler&&(window.removeEventListener("resize",this.resizeHandler),this.resizeHandler=null),e.instances.delete(this.id),e.currentlyPlaying===this&&(e.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(t){if(typeof t=="string"){let i=this.instances.get(t);if(i)return i;let s=document.getElementById(t);if(s)return Array.from(this.instances.values()).find(o=>o.container===s)}if(t instanceof HTMLElement)return Array.from(this.instances.values()).find(i=>i.container===t)}static getAllInstances(){return Array.from(this.instances.values())}static destroyAll(){this.instances.forEach(t=>t.destroy()),this.instances.clear()}static async generateWaveformData(t,i=200){try{return(await R(t,i)).peaks}catch(s){throw console.error("[WaveformPlayer] Failed to generate waveform:",s),s}}static getPeaksUrl(t){if(!t)return;let i=t.replace(/\.(mp3|wav|ogg|flac|m4a|aac)(\?[^#]*)?(#.*)?$/i,".json$2$3");return i===t?void 0:i}};C.utils={formatTime:S,extractTitleFromUrl:L,escapeHtml:A,isSafeHref:z};var I=()=>typeof window<"u"&&typeof document<"u";function W(){if(!I())return;document.querySelectorAll("[data-waveform-player]").forEach(t=>{if(t.dataset.waveformInitialized!=="true")try{new C(t),t.dataset.waveformInitialized="true"}catch(i){console.error("[WaveformPlayer] Failed to initialize:",i,t)}})}I()&&(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",W):W());C.init=W;I()&&(window.WaveformPlayer=C);var Lt=C;})();
|