@arraypress/waveform-player 1.1.4 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -15
- package/dist/waveform-player.css +1 -1
- package/dist/waveform-player.esm.js +2 -2
- package/dist/waveform-player.js +70 -9
- package/dist/waveform-player.min.js +2 -2
- package/package.json +24 -3
- package/src/css/waveform-player.css +0 -5
- package/src/js/core.js +15 -12
- package/src/js/themes.js +96 -10
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# WaveformPlayer
|
|
2
2
|
|
|
3
3
|
A lightweight, customizable audio player with waveform visualization. Under 6KB gzipped.
|
|
4
4
|
|
|
@@ -14,10 +14,11 @@ A lightweight, customizable audio player with waveform visualization. Under 6KB
|
|
|
14
14
|
## Why WaveformPlayer?
|
|
15
15
|
|
|
16
16
|
- **Zero Config** - Just add `data-waveform-player` to any div. No JavaScript required.
|
|
17
|
-
- **Tiny** -
|
|
17
|
+
- **Tiny** - 8KB gzipped vs 40KB+ for alternatives
|
|
18
18
|
- **Real Waveforms** - Actual audio analysis, not fake waves
|
|
19
19
|
- **No Dependencies** - No jQuery, no bloat, pure vanilla JS
|
|
20
20
|
- **Works Everywhere** - WordPress, Shopify, React, Vue, or plain HTML
|
|
21
|
+
- **Ecosystem** - Optional playlist and analytics addons available
|
|
21
22
|
|
|
22
23
|
## Quick Start
|
|
23
24
|
|
|
@@ -50,24 +51,63 @@ npm install @arraypress/waveform-player
|
|
|
50
51
|
## Features
|
|
51
52
|
|
|
52
53
|
- 🎨 **6 Visual Styles** - Bars, mirror, line, blocks, dots, seekbar
|
|
53
|
-
- 🎯 **Tiny Footprint** - Under
|
|
54
|
+
- 🎯 **Tiny Footprint** - Under 8KB gzipped
|
|
54
55
|
- ⚡ **Zero Dependencies** - Pure JavaScript
|
|
55
56
|
- 🎭 **Fully Customizable** - Colors, sizes, styles
|
|
56
57
|
- 📱 **Responsive** - Works on all devices
|
|
57
58
|
- 🎵 **BPM Detection** - Automatic tempo detection (optional)
|
|
58
59
|
- 💾 **Waveform Caching** - Pre-generate waveforms for performance
|
|
59
60
|
- 🌐 **Framework Agnostic** - Works with React, Vue, Angular, or vanilla JS
|
|
61
|
+
- ⌨️ **Keyboard Controls** - Full keyboard navigation support
|
|
62
|
+
- 📱 **Media Session API** - System media controls, lock screen integration
|
|
63
|
+
- ⏩ **Speed Control** - Adjustable playback rate for podcasts/audiobooks
|
|
64
|
+
- 📍 **Chapter Markers** - Add clickable markers for navigation
|
|
65
|
+
- 🔄 **Dynamic Loading** - Load new tracks without page refresh
|
|
66
|
+
|
|
67
|
+
## Ecosystem
|
|
68
|
+
|
|
69
|
+
### WaveformPlaylist (Optional Addon)
|
|
70
|
+
Add playlist and chapter support with zero JavaScript:
|
|
71
|
+
|
|
72
|
+
```html
|
|
73
|
+
<div data-waveform-playlist data-continuous="true">
|
|
74
|
+
<div data-track data-url="song1.mp3" data-title="Track 1">
|
|
75
|
+
<div data-chapter data-time="0:00">Intro</div>
|
|
76
|
+
<div data-chapter data-time="2:30">Verse</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div data-track data-url="song2.mp3" data-title="Track 2"></div>
|
|
79
|
+
</div>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
[Learn more →](https://github.com/arraypress/waveform-playlist)
|
|
83
|
+
|
|
84
|
+
### WaveformTracker (Optional Addon)
|
|
85
|
+
Track meaningful audio engagement:
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
WaveformTracker.init({
|
|
89
|
+
endpoint: '/api/analytics',
|
|
90
|
+
events: {
|
|
91
|
+
listen: 30 // Track after 30 seconds of listening
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
[Learn more →](https://github.com/arraypress/waveform-tracker)
|
|
60
97
|
|
|
61
98
|
## Comparison
|
|
62
99
|
|
|
63
100
|
| Feature | WaveformPlayer | WaveSurfer.js | Amplitude.js |
|
|
64
|
-
|
|
65
|
-
| Size (gzipped) |
|
|
66
|
-
| Zero Config | ✅
|
|
67
|
-
| Dependencies | None
|
|
68
|
-
| Waveform Styles | 6
|
|
69
|
-
| Setup Time | 30 seconds
|
|
70
|
-
| Real Waveforms | ✅
|
|
101
|
+
|---------|----------------|---------------|--------------|
|
|
102
|
+
| Size (gzipped) | 8KB | 40KB+ | 35KB+ |
|
|
103
|
+
| Zero Config | ✅ | ❌ | ❌ |
|
|
104
|
+
| Dependencies | None | None | None |
|
|
105
|
+
| Waveform Styles | 6 | 3 | N/A |
|
|
106
|
+
| Setup Time | 30 seconds | 5+ minutes | 5+ minutes |
|
|
107
|
+
| Real Waveforms | ✅ | ✅ | ❌ |
|
|
108
|
+
| Keyboard Controls | ✅ | ✅ | ❌ |
|
|
109
|
+
| Media Session API | ✅ | ❌ | ❌ |
|
|
110
|
+
| Speed Control | ✅ | ✅ | ❌ |
|
|
71
111
|
|
|
72
112
|
## Usage
|
|
73
113
|
|
|
@@ -77,7 +117,9 @@ npm install @arraypress/waveform-player
|
|
|
77
117
|
data-url="audio.mp3"
|
|
78
118
|
data-title="My Song"
|
|
79
119
|
data-subtitle="Artist Name"
|
|
80
|
-
data-waveform-style="mirror"
|
|
120
|
+
data-waveform-style="mirror"
|
|
121
|
+
data-show-playback-speed="true"
|
|
122
|
+
data-markers='[{"time": 30, "label": "Chorus"}]'>
|
|
81
123
|
</div>
|
|
82
124
|
```
|
|
83
125
|
|
|
@@ -90,7 +132,11 @@ const player = new WaveformPlayer('#player', {
|
|
|
90
132
|
waveformStyle: 'mirror',
|
|
91
133
|
height: 80,
|
|
92
134
|
barWidth: 2,
|
|
93
|
-
barSpacing: 1
|
|
135
|
+
barSpacing: 1,
|
|
136
|
+
markers: [
|
|
137
|
+
{time: 30, label: 'Verse', color: '#4ade80'},
|
|
138
|
+
{time: 60, label: 'Chorus', color: '#f59e0b'}
|
|
139
|
+
]
|
|
94
140
|
});
|
|
95
141
|
```
|
|
96
142
|
|
|
@@ -120,9 +166,14 @@ Choose from 6 built-in styles:
|
|
|
120
166
|
| `buttonColor` | string | `'rgba(255,255,255,0.9)'` | Play button color |
|
|
121
167
|
| `showTime` | boolean | `true` | Show time display |
|
|
122
168
|
| `showBPM` | boolean | `false` | Enable BPM detection |
|
|
169
|
+
| `showPlaybackSpeed` | boolean | `false` | Show speed control menu |
|
|
170
|
+
| `playbackRate` | number | `1` | Initial playback speed (0.5-2) |
|
|
123
171
|
| `autoplay` | boolean | `false` | Autoplay on load |
|
|
124
172
|
| `title` | string | `''` | Track title |
|
|
125
173
|
| `subtitle` | string | `''` | Track subtitle |
|
|
174
|
+
| `artwork` | string | `''` | Album artwork URL |
|
|
175
|
+
| `markers` | array | `[]` | Chapter markers array |
|
|
176
|
+
| `enableMediaSession` | boolean | `true` | Enable system media controls |
|
|
126
177
|
|
|
127
178
|
## API Methods
|
|
128
179
|
|
|
@@ -139,6 +190,12 @@ player.seekToPercent(0.5); // Seek to 50%
|
|
|
139
190
|
// Volume
|
|
140
191
|
player.setVolume(0.8); // 80% volume
|
|
141
192
|
|
|
193
|
+
// Speed
|
|
194
|
+
player.setPlaybackRate(1.5); // 1.5x speed
|
|
195
|
+
|
|
196
|
+
// Dynamic loading
|
|
197
|
+
await player.loadTrack('new-song.mp3', 'New Title', 'New Artist');
|
|
198
|
+
|
|
142
199
|
// Destroy
|
|
143
200
|
player.destroy();
|
|
144
201
|
```
|
|
@@ -158,6 +215,16 @@ new WaveformPlayer('#player', {
|
|
|
158
215
|
});
|
|
159
216
|
```
|
|
160
217
|
|
|
218
|
+
## Keyboard Controls
|
|
219
|
+
|
|
220
|
+
When a player is focused (click on it):
|
|
221
|
+
|
|
222
|
+
- `Space` - Play/pause
|
|
223
|
+
- `←/→` - Seek backward/forward 5 seconds
|
|
224
|
+
- `↑/↓` - Volume up/down
|
|
225
|
+
- `M` - Mute/unmute
|
|
226
|
+
- `0-9` - Jump to 0%-90% of track
|
|
227
|
+
|
|
161
228
|
## Advanced Usage
|
|
162
229
|
|
|
163
230
|
### Pre-generated Waveforms
|
|
@@ -178,14 +245,14 @@ new WaveformPlayer('#player', {
|
|
|
178
245
|
### Multiple Players
|
|
179
246
|
|
|
180
247
|
```javascript
|
|
181
|
-
// Pause all players
|
|
182
|
-
WaveformPlayer.pauseAll();
|
|
183
|
-
|
|
184
248
|
// Get all instances
|
|
185
249
|
const players = WaveformPlayer.getAllInstances();
|
|
186
250
|
|
|
187
251
|
// Find specific player
|
|
188
252
|
const player = WaveformPlayer.getInstance('my-player');
|
|
253
|
+
|
|
254
|
+
// Destroy all players
|
|
255
|
+
WaveformPlayer.destroyAll();
|
|
189
256
|
```
|
|
190
257
|
|
|
191
258
|
### Custom Styling
|
|
@@ -258,6 +325,11 @@ See the [live demo](https://waveformplayer.com) for:
|
|
|
258
325
|
- Player builder
|
|
259
326
|
- BPM detection
|
|
260
327
|
- Pre-generated waveforms
|
|
328
|
+
- Keyboard navigation
|
|
329
|
+
- Speed controls
|
|
330
|
+
- Chapter markers
|
|
331
|
+
- Playlist support
|
|
332
|
+
- Analytics tracking
|
|
261
333
|
|
|
262
334
|
## Development
|
|
263
335
|
|
package/dist/waveform-player.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.waveform-player{font-family:inherit;color:inherit;line-height:1.4}.waveform-player *{box-sizing:border-box}.waveform-
|
|
1
|
+
.waveform-player{font-family:inherit;color:inherit;line-height:1.4}.waveform-player *{box-sizing:border-box}.waveform-body{display:flex;flex-direction:column;gap:8px}.waveform-track{display:flex;align-items:center;gap:12px;position:relative}.waveform-btn{width:36px;height:36px;min-width:36px;border-radius:50%;border:2px solid currentColor;background:transparent;color:inherit;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .2s ease;padding:0;opacity:.9;flex-shrink:0}.waveform-btn:hover:not(:disabled){opacity:1;transform:scale(1.05)}.waveform-btn:disabled{cursor:not-allowed;opacity:.3}.waveform-btn>*{display:flex;align-items:center;justify-content:center;width:100%;height:100%}.waveform-btn svg{width:16px;height:16px;fill:currentColor;display:block}.waveform-icon-play svg{margin-left:1px}.waveform-container{flex:1;position:relative;min-height:60px;cursor:pointer;min-width:0;width:100%}.waveform-container canvas{display:block;width:100%;height:100%;max-width:100%;transition:opacity .3s ease;position:relative;z-index:1}.waveform-info{display:flex;align-items:center;gap:8px;font-size:13px;min-height:20px}.waveform-text{flex:1;display:flex;flex-direction:column;gap:2px;min-width:0}.waveform-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:500}.waveform-subtitle{font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.waveform-time{font-size:11px;white-space:nowrap;flex-shrink:0}.waveform-bpm{font-size:11px;white-space:nowrap;flex-shrink:0;display:inline-flex;align-items:center;gap:4px}.waveform-loading{position:absolute;inset:0;background:#0000001a;z-index:10}.waveform-error{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#0003;z-index:10}.waveform-error-text{font-size:12px;opacity:.7;text-align:center;padding:0 20px}.waveform-markers{position:absolute;inset:0;pointer-events:none;z-index:5}.waveform-marker{position:absolute;top:0;width:2px;height:100%;background:#ffffff80;border:none;padding:0;cursor:pointer;pointer-events:all;transition:all .2s}.waveform-marker:hover{width:4px;z-index:20}.waveform-marker-tooltip{position:absolute;bottom:calc(100% + 4px);left:50%;transform:translate(-50%);background:#000000e6;color:#fff;padding:4px 8px;border-radius:4px;font-size:11px;white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .2s;z-index:1000}.waveform-marker:hover .waveform-marker-tooltip{opacity:1}.waveform-btn:focus-visible{outline:2px solid currentColor;outline-offset:2px}.waveform-marker:focus-visible{outline:2px solid currentColor;outline-offset:1px;width:4px}.waveform-speed{position:relative;flex-shrink:0}.speed-btn{background:transparent;border:1px solid rgba(255,255,255,.2);border-radius:4px;padding:4px 8px;color:inherit;font-size:11px;cursor:pointer;transition:all .2s;min-width:40px}.speed-btn:hover{background:#ffffff0d;border-color:#ffffff4d}.speed-value{font-weight:600}.speed-menu{position:absolute;bottom:100%;right:0;margin-bottom:4px;background:#000000f2;border:1px solid rgba(255,255,255,.2);border-radius:6px;padding:4px;z-index:100;min-width:60px}.speed-option{display:block;width:100%;background:transparent;border:none;color:#ffffffb3;padding:6px 12px;font-size:12px;cursor:pointer;transition:all .2s;text-align:left;border-radius:4px}.speed-option:hover{background:#ffffff1a;color:#fff}.speed-option.active{background:#a855f733;color:#a855f7;font-weight:600}.waveform-player.waveform-focused{outline:2px solid rgba(168,85,247,.5);outline-offset:2px;border-radius:4px}.waveform-player:focus{outline:none}.waveform-player:focus-visible{outline:1px solid rgba(168,85,247,.3);outline-offset:1px}.waveform-player.waveform-focused{outline:none}.waveform-track.waveform-align-top{align-items:flex-start}.waveform-track.waveform-align-top .waveform-btn{margin-top:5px}.waveform-track.waveform-align-center{align-items:center}.waveform-track.waveform-align-bottom{align-items:flex-end}.waveform-track.waveform-align-bottom .waveform-btn{margin-bottom:5px}@media (max-width: 480px){.waveform-btn{width:32px;height:32px;min-width:32px}.waveform-container{min-height:50px}.waveform-info{font-size:12px}.waveform-subtitle,.waveform-time,.waveform-bpm{font-size:10px}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function T(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function C(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function R(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function A(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,o=>o.toUpperCase())}function P(...t){let e={};for(let i of t)for(let a in i)i[a]!==null&&i[a]!==void 0&&(e[a]=i[a]);return e}function L(t,e){let i;return function(...o){let n=()=>{clearTimeout(i),t(...o)};clearTimeout(i),i=setTimeout(n,e)}}function S(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let a=(t.length-1)/(e-1);for(let o=0;o<e;o++){let n=o*a,r=Math.floor(n),s=Math.ceil(n),h=n-r;if(s>=t.length)i.push(t[t.length-1]);else if(r===s)i.push(t[r]);else{let l=t[r]*(1-h)+t[s]*h;i.push(l)}}}else{let a=t.length/e;for(let o=0;o<e;o++){let n=Math.floor(o*a),r=Math.floor((o+1)*a),s=0,h=0;for(let l=n;l<=r&&l<t.length;l++)t[l]>s&&(s=t[l]),h++;if(h===0){let l=Math.min(Math.round(o*a),t.length-1);s=t[l]}i.push(s)}}return i}function x(t,e,i,a,o){let n=window.devicePixelRatio||1,r=o.barWidth*n,s=o.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=a*e.width;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let f=m*(r+s);if(f+r>e.width)break;let c=l[m]*d*.9,y=d-c;t.fillStyle=o.color,t.fillRect(f,y,r,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let m=0;m<l.length;m++){let f=m*(r+s);if(f>p)break;let c=l[m]*d*.9,y=d-c;t.fillStyle=o.progressColor,t.fillRect(f,y,r,c)}t.restore()}function z(t,e,i,a,o){let n=window.devicePixelRatio||1,r=o.barWidth*n,s=o.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=d/2,m=a*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(r+s);if(c+r>e.width)break;let y=l[f]*d*.45;t.fillStyle=o.color,t.fillRect(c,p-y,r,y),t.fillRect(c,p,r,y)}t.save(),t.beginPath(),t.rect(0,0,m,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(r+s);if(c>m)break;let y=l[f]*d*.45;t.fillStyle=o.progressColor,t.fillRect(c,p-y,r,y),t.fillRect(c,p,r,y)}t.restore()}function H(t,e,i,a,o){let n=e.width,r=e.height,s=r/2,h=r*.35;t.clearRect(0,0,n,r);let l=(d,p,m=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,s);let c=[],y=Math.floor(i.length*m);for(let u=0;u<y;u++){let v=u/(i.length-1)*n,k=i[u],b=Math.sin(u*.1)*k,w=s+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,s),t.lineTo(n,s),t.stroke();for(let d=0;d<=10;d++){let p=n/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,r),t.stroke()}l(o.color,2,1,!1),a>0&&l(o.progressColor,3,a,!0)}function q(t,e,i,a,o){let n=window.devicePixelRatio||1,r=(o.barWidth||3)*n,s=(o.barSpacing||1)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=4*n,m=2*n,f=a*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let u=y*(r+s);if(u+r>e.width)break;let v=l[y]*d*.9,k=Math.floor(v/(p+m));t.fillStyle=u<f?o.progressColor:o.color;for(let b=0;b<k;b++){let w=b*(p+m);t.fillRect(u,c-w-p,r,p),b>0&&t.fillRect(u,c+w,r,p)}}}function $(t,e,i,a,o){let n=window.devicePixelRatio||1,r=(o.barWidth||2)*n,s=(o.barSpacing||3)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=Math.max(1.5*n,r/2),m=a*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let y=c*(r+s)+r/2;if(y>e.width)break;let u=l[c]*d*.9;t.fillStyle=y<m?o.progressColor:o.color,t.beginPath(),t.arc(y,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(y,f+u/2,p,0,Math.PI*2),t.fill()}}function U(t,e,i,a,o){let n=e.width,r=e.height,s=r/2,h=4,l=h/2;if(t.clearRect(0,0,n,r),t.fillStyle=o.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(n-l,s-h/2),t.arc(n-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),a>0){let d=Math.max(l*2,a*n);t.shadowBlur=8,t.shadowColor=o.progressColor,t.fillStyle=o.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(d-l,s-h/2),t.arc(d-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,m=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(m,s,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=o.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(m,s,p*.4,0,Math.PI*2),t.fill()}}var F={bars:x,mirror:z,line:H,blocks:q,dots:$,seekbar:U};function B(t,e,i,a,o){(F[o.waveformStyle]||x)(t,e,i,a,o)}function W(t){try{let e=t.getChannelData(0),i=t.sampleRate,a=N(e,i);if(a.length<2)return 120;let o=[];for(let h=1;h<a.length;h++)o.push((a[h]-a[h-1])/i);let n={};o.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(n[d]=(n[d]||0)+1)});let r=0,s=120;for(let[h,l]of Object.entries(n))l>r&&(r=l,s=parseInt(h));return s<70&&n[s*2]?s*=2:s>160&&n[Math.round(s/2)]&&(s=Math.round(s/2)),s-1}catch(e){return console.warn("BPM detection failed:",e),null}}function N(t,e){let o=[],n=0;for(let r=0;r<t.length-2048;r+=1024){let s=0;for(let d=r;d<r+2048;d++)s+=t[d]*t[d];s=s/2048;let h=s-n,l=n*1.8+.01;if(h>l&&s>.01){let d=o[o.length-1]||0,p=e*.15;r-d>p&&o.push(r)}n=s*.8+n*.2}return o}function Y(t,e=200){let i=t.length/e,a=~~(i/10)||1,o=t.numberOfChannels,n=[];for(let s=0;s<o;s++){let h=t.getChannelData(s);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),m=0,f=0;for(let y=d;y<p;y+=a){let u=h[y];u>f&&(f=u),u<m&&(m=u)}let c=Math.max(Math.abs(f),Math.abs(m));(s===0||c>n[l])&&(n[l]=c)}}let r=Math.max(...n);return r>0?n.map(s=>s/r):n}async function M(t,e=200,i=!1){try{let a=new(window.AudioContext||window.webkitAudioContext),n=await(await fetch(t)).arrayBuffer(),r=await a.decodeAudioData(n),s=Y(r,e);s=j(s);let h=null;return i&&(h=await W(r)),a.close(),{peaks:s,bpm:h}}catch(a){throw console.error("Failed to generate waveform:",a),a}}function I(t=200){let e=[];for(let i=0;i<t;i++){let a=Math.random()*.5+.3,o=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,a+o)))}return e}function j(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let a=e/i;return t.map(o=>o*a)}var D={url:"",height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:"dark",waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},O={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let a=T(this.container);this.options=P(D,a,i);let o=O[this.options.waveformStyle];o&&(a.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=o.barWidth),a.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=o.barSpacing)),this.options.waveformColor=this.options.waveformColor||"rgba(255, 255, 255, 0.3)",this.options.progressColor=this.options.progressColor||"rgba(255, 255, 255, 0.9)",this.options.buttonColor=this.options.buttonColor||"rgba(255, 255, 255, 0.9)",this.options.textColor=this.options.textColor||"#ffffff",this.options.textSecondaryColor=this.options.textSecondaryColor||"rgba(255, 255, 255, 0.6)",this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||R(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center"),this.container.innerHTML=`
|
|
1
|
+
function L(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function P(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function A(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function R(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,a=>a.toUpperCase())}function C(...t){let e={};for(let i of t)for(let o in i)i[o]!==null&&i[o]!==void 0&&(e[o]=i[o]);return e}function B(t,e){let i;return function(...a){let n=()=>{clearTimeout(i),t(...a)};clearTimeout(i),i=setTimeout(n,e)}}function S(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let o=(t.length-1)/(e-1);for(let a=0;a<e;a++){let n=a*o,r=Math.floor(n),s=Math.ceil(n),h=n-r;if(s>=t.length)i.push(t[t.length-1]);else if(r===s)i.push(t[r]);else{let l=t[r]*(1-h)+t[s]*h;i.push(l)}}}else{let o=t.length/e;for(let a=0;a<e;a++){let n=Math.floor(a*o),r=Math.floor((a+1)*o),s=0,h=0;for(let l=n;l<=r&&l<t.length;l++)t[l]>s&&(s=t[l]),h++;if(h===0){let l=Math.min(Math.round(a*o),t.length-1);s=t[l]}i.push(s)}}return i}function x(t,e,i,o,a){let n=window.devicePixelRatio||1,r=a.barWidth*n,s=a.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=o*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(r+s);if(f+r>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=a.color,t.fillRect(f,m,r,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(r+s);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=a.progressColor,t.fillRect(f,m,r,c)}t.restore()}function q(t,e,i,o,a){let n=window.devicePixelRatio||1,r=a.barWidth*n,s=a.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=d/2,y=o*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(r+s);if(c+r>e.width)break;let m=l[f]*d*.45;t.fillStyle=a.color,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(r+s);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=a.progressColor,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.restore()}function $(t,e,i,o,a){let n=e.width,r=e.height,s=r/2,h=r*.35;t.clearRect(0,0,n,r);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,s);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*n,k=i[u],b=Math.sin(u*.1)*k,w=s+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,s),t.lineTo(n,s),t.stroke();for(let d=0;d<=10;d++){let p=n/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,r),t.stroke()}l(a.color,2,1,!1),o>0&&l(a.progressColor,3,o,!0)}function U(t,e,i,o,a){let n=window.devicePixelRatio||1,r=(a.barWidth||3)*n,s=(a.barSpacing||1)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=4*n,y=2*n,f=o*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(r+s);if(u+r>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?a.progressColor:a.color;for(let b=0;b<k;b++){let w=b*(p+y);t.fillRect(u,c-w-p,r,p),b>0&&t.fillRect(u,c+w,r,p)}}}function F(t,e,i,o,a){let n=window.devicePixelRatio||1,r=(a.barWidth||2)*n,s=(a.barSpacing||3)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=Math.max(1.5*n,r/2),y=o*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(r+s)+r/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?a.progressColor:a.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,o,a){let n=e.width,r=e.height,s=r/2,h=4,l=h/2;if(t.clearRect(0,0,n,r),t.fillStyle=a.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(n-l,s-h/2),t.arc(n-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),o>0){let d=Math.max(l*2,o*n);t.shadowBlur=8,t.shadowColor=a.progressColor,t.fillStyle=a.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(d-l,s-h/2),t.arc(d-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,s,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=a.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,s,p*.4,0,Math.PI*2),t.fill()}}var Y={bars:x,mirror:q,line:$,blocks:U,dots:F,seekbar:N};function W(t,e,i,o,a){(Y[a.waveformStyle]||x)(t,e,i,o,a)}function I(t){try{let e=t.getChannelData(0),i=t.sampleRate,o=j(e,i);if(o.length<2)return 120;let a=[];for(let h=1;h<o.length;h++)a.push((o[h]-o[h-1])/i);let n={};a.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(n[d]=(n[d]||0)+1)});let r=0,s=120;for(let[h,l]of Object.entries(n))l>r&&(r=l,s=parseInt(h));return s<70&&n[s*2]?s*=2:s>160&&n[Math.round(s/2)]&&(s=Math.round(s/2)),s-1}catch(e){return console.warn("BPM detection failed:",e),null}}function j(t,e){let a=[],n=0;for(let r=0;r<t.length-2048;r+=1024){let s=0;for(let d=r;d<r+2048;d++)s+=t[d]*t[d];s=s/2048;let h=s-n,l=n*1.8+.01;if(h>l&&s>.01){let d=a[a.length-1]||0,p=e*.15;r-d>p&&a.push(r)}n=s*.8+n*.2}return a}function V(t,e=200){let i=t.length/e,o=~~(i/10)||1,a=t.numberOfChannels,n=[];for(let s=0;s<a;s++){let h=t.getChannelData(s);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=o){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(s===0||c>n[l])&&(n[l]=c)}}let r=Math.max(...n);return r>0?n.map(s=>s/r):n}async function M(t,e=200,i=!1){try{let o=new(window.AudioContext||window.webkitAudioContext),n=await(await fetch(t)).arrayBuffer(),r=await o.decodeAudioData(n),s=V(r,e);s=J(s);let h=null;return i&&(h=await I(r)),o.close(),{peaks:s,bpm:h}}catch(o){throw console.error("Failed to generate waveform:",o),o}}function D(t=200){let e=[];for(let i=0;i<t;i++){let o=Math.random()*.5+.3,a=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,o+a)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let o=e/i;return t.map(a=>a*o)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let o=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(o&&o.length>=3){let[a,n,r]=o.map(Number),s=(a*299+n*587+r*114)/1e3;if(s>128)return"light";if(s<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var E={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function O(t){if(t&&E[t])return E[t];let e=G();return E[e]}var z={url:"",height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},H={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let o=L(this.container);this.options=C(z,o,i);let a=O(this.options.colorPreset);for(let[r,s]of Object.entries(a))(this.options[r]===null||this.options[r]===void 0)&&(this.options[r]=s);let n=H[this.options.waveformStyle];n&&(o.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=n.barWidth),o.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=n.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||A(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center"),this.container.innerHTML=`
|
|
2
2
|
<div class="waveform-player-inner">
|
|
3
3
|
<div class="waveform-body">
|
|
4
4
|
<div class="waveform-track waveform-align-${e}">
|
|
@@ -59,4 +59,4 @@ function T(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&
|
|
|
59
59
|
</div>
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
62
|
-
`,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",a=>{a.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",a=>{if(a.stopPropagation(),a.target.classList.contains("speed-option")){let o=parseFloat(a.target.dataset.rate);this.setPlaybackRate(o),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,a=this.audio.currentTime;if(i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let o={" ":()=>this.togglePlay(),ArrowLeft:()=>this.seekTo(Math.max(0,a-5)),ArrowRight:()=>this.seekTo(Math.min(this.audio.duration,a+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};o[i]&&(e.preventDefault(),o[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.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)),window.addEventListener("resize",L(()=>this.resizeCanvas(),100))}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((a,o)=>{let n=()=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),a()},r=s=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),o(s)};this.audio.addEventListener("loadedmetadata",n),this.audio.addEventListener("error",r)});let i=this.options.title||A(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let a=await M(e,this.options.samples,this.options.showBPM);this.waveformData=a.peaks,a.bpm&&(this.detectedBPM=a.bpm,this.updateBPMDisplay())}catch(a){console.warn("Using placeholder waveform:",a),this.waveformData=I(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,a=null,o={}){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:a||this.options.subtitle,...o}),o.preload&&(this.audio.preload=o.preload),this.subtitleEl&&(a?(this.subtitleEl.textContent=a,this.subtitleEl.style.display=""):a===""&&(this.subtitleEl.style.display="none")),o.artwork&&this.artworkEl&&(this.artworkEl.src=o.artwork),o.markers&&(this.options.markers=o.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||B(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){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 a=e.time/this.audio.duration*100,o=document.createElement("button");o.className="waveform-marker",o.style.left=`${a}%`,o.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",o.setAttribute("aria-label",e.label),o.setAttribute("data-time",e.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=e.label,o.appendChild(n),o.addEventListener("click",r=>{r.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(o)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),a=e.clientX-i.left,o=Math.max(0,Math.min(1,a/i.width));this.seekToPercent(o)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.totalTimeEl&&(this.totalTimeEl.textContent=C(this.audio.duration)),this.renderMarkers()}onPlay(){this.isPlaying=!0,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",{detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}onPause(){this.isPlaying=!1,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",{detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}onEnded(){this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{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",{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(){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),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 a=document.getElementById(e);if(a)return Array.from(this.instances.values()).find(o=>o.container===a)}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(a){throw console.error("Failed to generate waveform:",a),a}}};function E(){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",E):E());g.init=E;typeof window<"u"&&(window.WaveformPlayer=g);var st=g;export{g as WaveformPlayer,st as default};
|
|
62
|
+
`,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 a=parseFloat(o.target.dataset.rate);this.setPlaybackRate(a),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 a={" ":()=>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};a[i]&&(e.preventDefault(),a[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.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)),window.addEventListener("resize",B(()=>this.resizeCanvas(),100))}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,a)=>{let n=()=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),o()},r=s=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),a(s)};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,a={}){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=C(this.options,{url:e,title:i||this.options.title,subtitle:o||this.options.subtitle,...a}),a.preload&&(this.audio.preload=a.preload),this.subtitleEl&&(o?(this.subtitleEl.textContent=o,this.subtitleEl.style.display=""):o===""&&(this.subtitleEl.style.display="none")),a.artwork&&this.artworkEl&&(this.artworkEl.src=a.artwork),a.markers&&(this.options.markers=a.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||W(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){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,a=document.createElement("button");a.className="waveform-marker",a.style.left=`${o}%`,a.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",a.setAttribute("aria-label",e.label),a.setAttribute("data-time",e.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=e.label,a.appendChild(n),a.addEventListener("click",r=>{r.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(a)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),o=e.clientX-i.left,a=Math.max(0,Math.min(1,o/i.width));this.seekToPercent(a)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.totalTimeEl&&(this.totalTimeEl.textContent=P(this.audio.duration)),this.renderMarkers()}onPlay(){this.isPlaying=!0,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",{detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}onPause(){this.isPlaying=!1,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",{detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}onEnded(){this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{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=P(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{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(){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),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(a=>a.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};
|
package/dist/waveform-player.js
CHANGED
|
@@ -504,6 +504,68 @@
|
|
|
504
504
|
}
|
|
505
505
|
|
|
506
506
|
// src/js/themes.js
|
|
507
|
+
function detectColorScheme() {
|
|
508
|
+
const root = document.documentElement;
|
|
509
|
+
const body = document.body;
|
|
510
|
+
if (root.classList.contains("dark") || root.classList.contains("dark-mode") || root.classList.contains("theme-dark") || root.getAttribute("data-theme") === "dark" || root.getAttribute("data-color-scheme") === "dark" || body.classList.contains("dark") || body.classList.contains("dark-mode") || body.getAttribute("data-theme") === "dark") {
|
|
511
|
+
return "dark";
|
|
512
|
+
}
|
|
513
|
+
if (root.classList.contains("light") || root.classList.contains("light-mode") || root.classList.contains("theme-light") || root.getAttribute("data-theme") === "light" || root.getAttribute("data-color-scheme") === "light" || body.classList.contains("light") || body.classList.contains("light-mode") || body.getAttribute("data-theme") === "light") {
|
|
514
|
+
return "light";
|
|
515
|
+
}
|
|
516
|
+
try {
|
|
517
|
+
const bodyBg = getComputedStyle(document.body).backgroundColor;
|
|
518
|
+
const rgb = bodyBg.match(/\d+/g);
|
|
519
|
+
if (rgb && rgb.length >= 3) {
|
|
520
|
+
const [r, g, b] = rgb.map(Number);
|
|
521
|
+
const brightness = (r * 299 + g * 587 + b * 114) / 1e3;
|
|
522
|
+
if (brightness > 128) {
|
|
523
|
+
return "light";
|
|
524
|
+
} else if (brightness < 128) {
|
|
525
|
+
return "dark";
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
} catch (e) {
|
|
529
|
+
}
|
|
530
|
+
if (window.matchMedia) {
|
|
531
|
+
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
532
|
+
return "dark";
|
|
533
|
+
}
|
|
534
|
+
if (window.matchMedia("(prefers-color-scheme: light)").matches) {
|
|
535
|
+
return "light";
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return "dark";
|
|
539
|
+
}
|
|
540
|
+
var COLOR_PRESETS = {
|
|
541
|
+
dark: {
|
|
542
|
+
waveformColor: "rgba(255, 255, 255, 0.3)",
|
|
543
|
+
progressColor: "rgba(255, 255, 255, 0.9)",
|
|
544
|
+
buttonColor: "rgba(255, 255, 255, 0.9)",
|
|
545
|
+
buttonHoverColor: "rgba(255, 255, 255, 1)",
|
|
546
|
+
textColor: "#ffffff",
|
|
547
|
+
textSecondaryColor: "rgba(255, 255, 255, 0.6)",
|
|
548
|
+
backgroundColor: "rgba(255, 255, 255, 0.03)",
|
|
549
|
+
borderColor: "rgba(255, 255, 255, 0.1)"
|
|
550
|
+
},
|
|
551
|
+
light: {
|
|
552
|
+
waveformColor: "rgba(0, 0, 0, 0.2)",
|
|
553
|
+
progressColor: "rgba(0, 0, 0, 0.8)",
|
|
554
|
+
buttonColor: "rgba(0, 0, 0, 0.8)",
|
|
555
|
+
buttonHoverColor: "rgba(0, 0, 0, 0.9)",
|
|
556
|
+
textColor: "#333333",
|
|
557
|
+
textSecondaryColor: "rgba(0, 0, 0, 0.6)",
|
|
558
|
+
backgroundColor: "rgba(0, 0, 0, 0.02)",
|
|
559
|
+
borderColor: "rgba(0, 0, 0, 0.1)"
|
|
560
|
+
}
|
|
561
|
+
};
|
|
562
|
+
function getColorPreset(presetName) {
|
|
563
|
+
if (presetName && COLOR_PRESETS[presetName]) {
|
|
564
|
+
return COLOR_PRESETS[presetName];
|
|
565
|
+
}
|
|
566
|
+
const detected = detectColorScheme();
|
|
567
|
+
return COLOR_PRESETS[detected];
|
|
568
|
+
}
|
|
507
569
|
var DEFAULT_OPTIONS = {
|
|
508
570
|
// Core settings
|
|
509
571
|
url: "",
|
|
@@ -514,16 +576,14 @@
|
|
|
514
576
|
playbackRate: 1,
|
|
515
577
|
showPlaybackSpeed: false,
|
|
516
578
|
playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
|
|
517
|
-
// Available speeds
|
|
518
579
|
// Layout Options
|
|
519
580
|
buttonAlign: "auto",
|
|
520
|
-
// 'auto', 'top', 'center', 'bottom'
|
|
521
581
|
// Default waveform style
|
|
522
582
|
waveformStyle: "mirror",
|
|
523
583
|
barWidth: 2,
|
|
524
584
|
barSpacing: 0,
|
|
525
|
-
// Color preset
|
|
526
|
-
colorPreset:
|
|
585
|
+
// Color preset: null = auto-detect, 'dark' = force dark, 'light' = force light
|
|
586
|
+
colorPreset: null,
|
|
527
587
|
// Individual color overrides (null means use preset)
|
|
528
588
|
waveformColor: null,
|
|
529
589
|
progressColor: null,
|
|
@@ -587,6 +647,12 @@
|
|
|
587
647
|
}
|
|
588
648
|
const dataOptions = parseDataAttributes(this.container);
|
|
589
649
|
this.options = mergeOptions(DEFAULT_OPTIONS, dataOptions, options);
|
|
650
|
+
const preset = getColorPreset(this.options.colorPreset);
|
|
651
|
+
for (const [key, value] of Object.entries(preset)) {
|
|
652
|
+
if (this.options[key] === null || this.options[key] === void 0) {
|
|
653
|
+
this.options[key] = value;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
590
656
|
const styleDefaults = STYLE_DEFAULTS[this.options.waveformStyle];
|
|
591
657
|
if (styleDefaults) {
|
|
592
658
|
if (dataOptions.barWidth === void 0 && options.barWidth === void 0) {
|
|
@@ -596,11 +662,6 @@
|
|
|
596
662
|
this.options.barSpacing = styleDefaults.barSpacing;
|
|
597
663
|
}
|
|
598
664
|
}
|
|
599
|
-
this.options.waveformColor = this.options.waveformColor || "rgba(255, 255, 255, 0.3)";
|
|
600
|
-
this.options.progressColor = this.options.progressColor || "rgba(255, 255, 255, 0.9)";
|
|
601
|
-
this.options.buttonColor = this.options.buttonColor || "rgba(255, 255, 255, 0.9)";
|
|
602
|
-
this.options.textColor = this.options.textColor || "#ffffff";
|
|
603
|
-
this.options.textSecondaryColor = this.options.textSecondaryColor || "rgba(255, 255, 255, 0.6)";
|
|
604
665
|
this.audio = null;
|
|
605
666
|
this.canvas = null;
|
|
606
667
|
this.ctx = null;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(()=>{function T(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function C(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function R(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function A(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,o=>o.toUpperCase())}function P(...t){let e={};for(let i of t)for(let a in i)i[a]!==null&&i[a]!==void 0&&(e[a]=i[a]);return e}function L(t,e){let i;return function(...o){let n=()=>{clearTimeout(i),t(...o)};clearTimeout(i),i=setTimeout(n,e)}}function S(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let a=(t.length-1)/(e-1);for(let o=0;o<e;o++){let n=o*a,r=Math.floor(n),s=Math.ceil(n),h=n-r;if(s>=t.length)i.push(t[t.length-1]);else if(r===s)i.push(t[r]);else{let l=t[r]*(1-h)+t[s]*h;i.push(l)}}}else{let a=t.length/e;for(let o=0;o<e;o++){let n=Math.floor(o*a),r=Math.floor((o+1)*a),s=0,h=0;for(let l=n;l<=r&&l<t.length;l++)t[l]>s&&(s=t[l]),h++;if(h===0){let l=Math.min(Math.round(o*a),t.length-1);s=t[l]}i.push(s)}}return i}function x(t,e,i,a,o){let n=window.devicePixelRatio||1,r=o.barWidth*n,s=o.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=a*e.width;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let f=m*(r+s);if(f+r>e.width)break;let c=l[m]*d*.9,y=d-c;t.fillStyle=o.color,t.fillRect(f,y,r,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let m=0;m<l.length;m++){let f=m*(r+s);if(f>p)break;let c=l[m]*d*.9,y=d-c;t.fillStyle=o.progressColor,t.fillRect(f,y,r,c)}t.restore()}function z(t,e,i,a,o){let n=window.devicePixelRatio||1,r=o.barWidth*n,s=o.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=d/2,m=a*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(r+s);if(c+r>e.width)break;let y=l[f]*d*.45;t.fillStyle=o.color,t.fillRect(c,p-y,r,y),t.fillRect(c,p,r,y)}t.save(),t.beginPath(),t.rect(0,0,m,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(r+s);if(c>m)break;let y=l[f]*d*.45;t.fillStyle=o.progressColor,t.fillRect(c,p-y,r,y),t.fillRect(c,p,r,y)}t.restore()}function H(t,e,i,a,o){let n=e.width,r=e.height,s=r/2,h=r*.35;t.clearRect(0,0,n,r);let l=(d,p,m=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,s);let c=[],y=Math.floor(i.length*m);for(let u=0;u<y;u++){let v=u/(i.length-1)*n,k=i[u],b=Math.sin(u*.1)*k,w=s+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,s),t.lineTo(n,s),t.stroke();for(let d=0;d<=10;d++){let p=n/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,r),t.stroke()}l(o.color,2,1,!1),a>0&&l(o.progressColor,3,a,!0)}function q(t,e,i,a,o){let n=window.devicePixelRatio||1,r=(o.barWidth||3)*n,s=(o.barSpacing||1)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=4*n,m=2*n,f=a*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let u=y*(r+s);if(u+r>e.width)break;let v=l[y]*d*.9,k=Math.floor(v/(p+m));t.fillStyle=u<f?o.progressColor:o.color;for(let b=0;b<k;b++){let w=b*(p+m);t.fillRect(u,c-w-p,r,p),b>0&&t.fillRect(u,c+w,r,p)}}}function $(t,e,i,a,o){let n=window.devicePixelRatio||1,r=(o.barWidth||2)*n,s=(o.barSpacing||3)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=Math.max(1.5*n,r/2),m=a*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let y=c*(r+s)+r/2;if(y>e.width)break;let u=l[c]*d*.9;t.fillStyle=y<m?o.progressColor:o.color,t.beginPath(),t.arc(y,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(y,f+u/2,p,0,Math.PI*2),t.fill()}}function U(t,e,i,a,o){let n=e.width,r=e.height,s=r/2,h=4,l=h/2;if(t.clearRect(0,0,n,r),t.fillStyle=o.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(n-l,s-h/2),t.arc(n-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),a>0){let d=Math.max(l*2,a*n);t.shadowBlur=8,t.shadowColor=o.progressColor,t.fillStyle=o.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(d-l,s-h/2),t.arc(d-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,m=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(m,s,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=o.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(m,s,p*.4,0,Math.PI*2),t.fill()}}var F={bars:x,mirror:z,line:H,blocks:q,dots:$,seekbar:U};function B(t,e,i,a,o){(F[o.waveformStyle]||x)(t,e,i,a,o)}function W(t){try{let e=t.getChannelData(0),i=t.sampleRate,a=N(e,i);if(a.length<2)return 120;let o=[];for(let h=1;h<a.length;h++)o.push((a[h]-a[h-1])/i);let n={};o.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(n[d]=(n[d]||0)+1)});let r=0,s=120;for(let[h,l]of Object.entries(n))l>r&&(r=l,s=parseInt(h));return s<70&&n[s*2]?s*=2:s>160&&n[Math.round(s/2)]&&(s=Math.round(s/2)),s-1}catch(e){return console.warn("BPM detection failed:",e),null}}function N(t,e){let o=[],n=0;for(let r=0;r<t.length-2048;r+=1024){let s=0;for(let d=r;d<r+2048;d++)s+=t[d]*t[d];s=s/2048;let h=s-n,l=n*1.8+.01;if(h>l&&s>.01){let d=o[o.length-1]||0,p=e*.15;r-d>p&&o.push(r)}n=s*.8+n*.2}return o}function Y(t,e=200){let i=t.length/e,a=~~(i/10)||1,o=t.numberOfChannels,n=[];for(let s=0;s<o;s++){let h=t.getChannelData(s);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),m=0,f=0;for(let y=d;y<p;y+=a){let u=h[y];u>f&&(f=u),u<m&&(m=u)}let c=Math.max(Math.abs(f),Math.abs(m));(s===0||c>n[l])&&(n[l]=c)}}let r=Math.max(...n);return r>0?n.map(s=>s/r):n}async function M(t,e=200,i=!1){try{let a=new(window.AudioContext||window.webkitAudioContext),n=await(await fetch(t)).arrayBuffer(),r=await a.decodeAudioData(n),s=Y(r,e);s=j(s);let h=null;return i&&(h=await W(r)),a.close(),{peaks:s,bpm:h}}catch(a){throw console.error("Failed to generate waveform:",a),a}}function I(t=200){let e=[];for(let i=0;i<t;i++){let a=Math.random()*.5+.3,o=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,a+o)))}return e}function j(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let a=e/i;return t.map(o=>o*a)}var D={url:"",height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:"dark",waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},O={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let a=T(this.container);this.options=P(D,a,i);let o=O[this.options.waveformStyle];o&&(a.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=o.barWidth),a.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=o.barSpacing)),this.options.waveformColor=this.options.waveformColor||"rgba(255, 255, 255, 0.3)",this.options.progressColor=this.options.progressColor||"rgba(255, 255, 255, 0.9)",this.options.buttonColor=this.options.buttonColor||"rgba(255, 255, 255, 0.9)",this.options.textColor=this.options.textColor||"#ffffff",this.options.textSecondaryColor=this.options.textSecondaryColor||"rgba(255, 255, 255, 0.6)",this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||R(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center"),this.container.innerHTML=`
|
|
1
|
+
(()=>{function L(t){let e={};if(t.dataset.url&&(e.url=t.dataset.url),t.dataset.height&&(e.height=parseInt(t.dataset.height)),t.dataset.samples&&(e.samples=parseInt(t.dataset.samples)),t.dataset.preload&&(e.preload=t.dataset.preload),t.dataset.waveformStyle&&(e.waveformStyle=t.dataset.waveformStyle),t.dataset.barWidth&&(e.barWidth=parseInt(t.dataset.barWidth)),t.dataset.barSpacing&&(e.barSpacing=parseInt(t.dataset.barSpacing)),t.dataset.buttonAlign&&(e.buttonAlign=t.dataset.buttonAlign),t.dataset.colorPreset&&(e.colorPreset=t.dataset.colorPreset),t.dataset.waveformColor&&(e.waveformColor=t.dataset.waveformColor),t.dataset.progressColor&&(e.progressColor=t.dataset.progressColor),t.dataset.buttonColor&&(e.buttonColor=t.dataset.buttonColor),t.dataset.buttonHoverColor&&(e.buttonHoverColor=t.dataset.buttonHoverColor),t.dataset.textColor&&(e.textColor=t.dataset.textColor),t.dataset.textSecondaryColor&&(e.textSecondaryColor=t.dataset.textSecondaryColor),t.dataset.backgroundColor&&(e.backgroundColor=t.dataset.backgroundColor),t.dataset.borderColor&&(e.borderColor=t.dataset.borderColor),t.dataset.color&&(e.waveformColor=t.dataset.color),t.dataset.theme&&(e.colorPreset=t.dataset.theme),t.dataset.autoplay&&(e.autoplay=t.dataset.autoplay==="true"),t.dataset.showTime&&(e.showTime=t.dataset.showTime==="true"),t.dataset.showHoverTime&&(e.showHoverTime=t.dataset.showHoverTime==="true"),t.dataset.showBpm&&(e.showBPM=t.dataset.showBpm==="true"),t.dataset.singlePlay&&(e.singlePlay=t.dataset.singlePlay==="true"),t.dataset.playOnSeek&&(e.playOnSeek=t.dataset.playOnSeek==="true"),t.dataset.title&&(e.title=t.dataset.title),t.dataset.subtitle&&(e.subtitle=t.dataset.subtitle),t.dataset.album&&(e.album=t.dataset.album),t.dataset.artwork&&(e.artwork=t.dataset.artwork),t.dataset.waveform&&(e.waveform=t.dataset.waveform),t.dataset.markers)try{e.markers=JSON.parse(t.dataset.markers)}catch(i){console.warn("Invalid markers JSON:",i)}if(t.dataset.playbackRate&&(e.playbackRate=parseFloat(t.dataset.playbackRate)),t.dataset.showPlaybackSpeed!==void 0&&(e.showPlaybackSpeed=t.dataset.showPlaybackSpeed==="true"),t.dataset.playbackRates)try{e.playbackRates=JSON.parse(t.dataset.playbackRates)}catch(i){console.warn("Invalid playbackRates JSON:",i)}return t.dataset.enableMediaSession!==void 0&&(e.enableMediaSession=t.dataset.enableMediaSession==="true"),e}function P(t){if(!t||isNaN(t))return"0:00";let e=Math.floor(t/60),i=Math.floor(t%60);return`${e}:${i.toString().padStart(2,"0")}`}function A(t){let e=t||Math.random().toString();return btoa(e.substring(0,10)).replace(/[^a-zA-Z0-9]/g,"")}function R(t){if(!t)return"Audio";let e=t.split("/");return e[e.length-1].split(".")[0].replace(/[-_]/g," ").replace(/\b\w/g,a=>a.toUpperCase())}function C(...t){let e={};for(let i of t)for(let o in i)i[o]!==null&&i[o]!==void 0&&(e[o]=i[o]);return e}function B(t,e){let i;return function(...a){let n=()=>{clearTimeout(i),t(...a)};clearTimeout(i),i=setTimeout(n,e)}}function S(t,e){if(t.length===e)return t;if(t.length===0||e===0)return[];let i=[];if(e>t.length){let o=(t.length-1)/(e-1);for(let a=0;a<e;a++){let n=a*o,r=Math.floor(n),s=Math.ceil(n),h=n-r;if(s>=t.length)i.push(t[t.length-1]);else if(r===s)i.push(t[r]);else{let l=t[r]*(1-h)+t[s]*h;i.push(l)}}}else{let o=t.length/e;for(let a=0;a<e;a++){let n=Math.floor(a*o),r=Math.floor((a+1)*o),s=0,h=0;for(let l=n;l<=r&&l<t.length;l++)t[l]>s&&(s=t[l]),h++;if(h===0){let l=Math.min(Math.round(a*o),t.length-1);s=t[l]}i.push(s)}}return i}function x(t,e,i,o,a){let n=window.devicePixelRatio||1,r=a.barWidth*n,s=a.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=o*e.width;t.clearRect(0,0,e.width,e.height);for(let y=0;y<l.length;y++){let f=y*(r+s);if(f+r>e.width)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=a.color,t.fillRect(f,m,r,c)}t.save(),t.beginPath(),t.rect(0,0,p,d),t.clip();for(let y=0;y<l.length;y++){let f=y*(r+s);if(f>p)break;let c=l[y]*d*.9,m=d-c;t.fillStyle=a.progressColor,t.fillRect(f,m,r,c)}t.restore()}function q(t,e,i,o,a){let n=window.devicePixelRatio||1,r=a.barWidth*n,s=a.barSpacing*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=d/2,y=o*e.width;t.clearRect(0,0,e.width,e.height);for(let f=0;f<l.length;f++){let c=f*(r+s);if(c+r>e.width)break;let m=l[f]*d*.45;t.fillStyle=a.color,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.save(),t.beginPath(),t.rect(0,0,y,d),t.clip();for(let f=0;f<l.length;f++){let c=f*(r+s);if(c>y)break;let m=l[f]*d*.45;t.fillStyle=a.progressColor,t.fillRect(c,p-m,r,m),t.fillRect(c,p,r,m)}t.restore()}function $(t,e,i,o,a){let n=e.width,r=e.height,s=r/2,h=r*.35;t.clearRect(0,0,n,r);let l=(d,p,y=1,f=!1)=>{f&&(t.shadowBlur=12,t.shadowColor=d),t.strokeStyle=d,t.lineWidth=p,t.lineCap="round",t.lineJoin="round",t.beginPath(),t.moveTo(0,s);let c=[],m=Math.floor(i.length*y);for(let u=0;u<m;u++){let v=u/(i.length-1)*n,k=i[u],b=Math.sin(u*.1)*k,w=s+b*h;c.push({x:v,y:w})}for(let u=0;u<c.length-1;u++){let v=c[u].x+(c[u+1].x-c[u].x)*.5,k=c[u].y,b=c[u+1].x-(c[u+1].x-c[u].x)*.5,w=c[u+1].y;t.bezierCurveTo(v,k,b,w,c[u+1].x,c[u+1].y)}t.stroke(),f&&(t.shadowBlur=0)};t.strokeStyle="rgba(255, 255, 255, 0.03)",t.lineWidth=.5,t.beginPath(),t.moveTo(0,s),t.lineTo(n,s),t.stroke();for(let d=0;d<=10;d++){let p=n/10*d;t.beginPath(),t.moveTo(p,0),t.lineTo(p,r),t.stroke()}l(a.color,2,1,!1),o>0&&l(a.progressColor,3,o,!0)}function U(t,e,i,o,a){let n=window.devicePixelRatio||1,r=(a.barWidth||3)*n,s=(a.barSpacing||1)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=4*n,y=2*n,f=o*e.width,c=d/2;t.clearRect(0,0,e.width,e.height);for(let m=0;m<l.length;m++){let u=m*(r+s);if(u+r>e.width)break;let v=l[m]*d*.9,k=Math.floor(v/(p+y));t.fillStyle=u<f?a.progressColor:a.color;for(let b=0;b<k;b++){let w=b*(p+y);t.fillRect(u,c-w-p,r,p),b>0&&t.fillRect(u,c+w,r,p)}}}function F(t,e,i,o,a){let n=window.devicePixelRatio||1,r=(a.barWidth||2)*n,s=(a.barSpacing||3)*n,h=Math.floor(e.width/(r+s)),l=S(i,h),d=e.height,p=Math.max(1.5*n,r/2),y=o*e.width,f=d/2;t.clearRect(0,0,e.width,e.height);for(let c=0;c<l.length;c++){let m=c*(r+s)+r/2;if(m>e.width)break;let u=l[c]*d*.9;t.fillStyle=m<y?a.progressColor:a.color,t.beginPath(),t.arc(m,f-u/2,p,0,Math.PI*2),t.fill(),t.beginPath(),t.arc(m,f+u/2,p,0,Math.PI*2),t.fill()}}function N(t,e,i,o,a){let n=e.width,r=e.height,s=r/2,h=4,l=h/2;if(t.clearRect(0,0,n,r),t.fillStyle=a.color||"rgba(255, 255, 255, 0.2)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(n-l,s-h/2),t.arc(n-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),o>0){let d=Math.max(l*2,o*n);t.shadowBlur=8,t.shadowColor=a.progressColor,t.fillStyle=a.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.moveTo(l,s-h/2),t.lineTo(d-l,s-h/2),t.arc(d-l,s,h/2,-Math.PI/2,Math.PI/2),t.lineTo(l,s+h/2),t.arc(l,s,h/2,Math.PI/2,-Math.PI/2),t.closePath(),t.fill(),t.shadowBlur=0;let p=8,y=d;t.shadowBlur=4,t.shadowColor="rgba(0, 0, 0, 0.3)",t.shadowOffsetY=2,t.fillStyle="#ffffff",t.beginPath(),t.arc(y,s,p,0,Math.PI*2),t.fill(),t.shadowBlur=0,t.shadowOffsetY=0,t.fillStyle=a.progressColor||"rgba(255, 255, 255, 0.9)",t.beginPath(),t.arc(y,s,p*.4,0,Math.PI*2),t.fill()}}var Y={bars:x,mirror:q,line:$,blocks:U,dots:F,seekbar:N};function W(t,e,i,o,a){(Y[a.waveformStyle]||x)(t,e,i,o,a)}function I(t){try{let e=t.getChannelData(0),i=t.sampleRate,o=j(e,i);if(o.length<2)return 120;let a=[];for(let h=1;h<o.length;h++)a.push((o[h]-o[h-1])/i);let n={};a.forEach(h=>{let l=60/h,d=Math.round(l/3)*3;d>60&&d<200&&(n[d]=(n[d]||0)+1)});let r=0,s=120;for(let[h,l]of Object.entries(n))l>r&&(r=l,s=parseInt(h));return s<70&&n[s*2]?s*=2:s>160&&n[Math.round(s/2)]&&(s=Math.round(s/2)),s-1}catch(e){return console.warn("BPM detection failed:",e),null}}function j(t,e){let a=[],n=0;for(let r=0;r<t.length-2048;r+=1024){let s=0;for(let d=r;d<r+2048;d++)s+=t[d]*t[d];s=s/2048;let h=s-n,l=n*1.8+.01;if(h>l&&s>.01){let d=a[a.length-1]||0,p=e*.15;r-d>p&&a.push(r)}n=s*.8+n*.2}return a}function V(t,e=200){let i=t.length/e,o=~~(i/10)||1,a=t.numberOfChannels,n=[];for(let s=0;s<a;s++){let h=t.getChannelData(s);for(let l=0;l<e;l++){let d=~~(l*i),p=~~(d+i),y=0,f=0;for(let m=d;m<p;m+=o){let u=h[m];u>f&&(f=u),u<y&&(y=u)}let c=Math.max(Math.abs(f),Math.abs(y));(s===0||c>n[l])&&(n[l]=c)}}let r=Math.max(...n);return r>0?n.map(s=>s/r):n}async function M(t,e=200,i=!1){try{let o=new(window.AudioContext||window.webkitAudioContext),n=await(await fetch(t)).arrayBuffer(),r=await o.decodeAudioData(n),s=V(r,e);s=J(s);let h=null;return i&&(h=await I(r)),o.close(),{peaks:s,bpm:h}}catch(o){throw console.error("Failed to generate waveform:",o),o}}function D(t=200){let e=[];for(let i=0;i<t;i++){let o=Math.random()*.5+.3,a=Math.sin(i/t*Math.PI*4)*.2;e.push(Math.max(.1,Math.min(1,o+a)))}return e}function J(t,e=.95){let i=Math.max(...t);if(i===0||i>e)return t;let o=e/i;return t.map(a=>a*o)}function G(){let t=document.documentElement,e=document.body;if(t.classList.contains("dark")||t.classList.contains("dark-mode")||t.classList.contains("theme-dark")||t.getAttribute("data-theme")==="dark"||t.getAttribute("data-color-scheme")==="dark"||e.classList.contains("dark")||e.classList.contains("dark-mode")||e.getAttribute("data-theme")==="dark")return"dark";if(t.classList.contains("light")||t.classList.contains("light-mode")||t.classList.contains("theme-light")||t.getAttribute("data-theme")==="light"||t.getAttribute("data-color-scheme")==="light"||e.classList.contains("light")||e.classList.contains("light-mode")||e.getAttribute("data-theme")==="light")return"light";try{let o=getComputedStyle(document.body).backgroundColor.match(/\d+/g);if(o&&o.length>=3){let[a,n,r]=o.map(Number),s=(a*299+n*587+r*114)/1e3;if(s>128)return"light";if(s<128)return"dark"}}catch{}if(window.matchMedia){if(window.matchMedia("(prefers-color-scheme: dark)").matches)return"dark";if(window.matchMedia("(prefers-color-scheme: light)").matches)return"light"}return"dark"}var E={dark:{waveformColor:"rgba(255, 255, 255, 0.3)",progressColor:"rgba(255, 255, 255, 0.9)",buttonColor:"rgba(255, 255, 255, 0.9)",buttonHoverColor:"rgba(255, 255, 255, 1)",textColor:"#ffffff",textSecondaryColor:"rgba(255, 255, 255, 0.6)",backgroundColor:"rgba(255, 255, 255, 0.03)",borderColor:"rgba(255, 255, 255, 0.1)"},light:{waveformColor:"rgba(0, 0, 0, 0.2)",progressColor:"rgba(0, 0, 0, 0.8)",buttonColor:"rgba(0, 0, 0, 0.8)",buttonHoverColor:"rgba(0, 0, 0, 0.9)",textColor:"#333333",textSecondaryColor:"rgba(0, 0, 0, 0.6)",backgroundColor:"rgba(0, 0, 0, 0.02)",borderColor:"rgba(0, 0, 0, 0.1)"}};function O(t){if(t&&E[t])return E[t];let e=G();return E[e]}var z={url:"",height:60,samples:200,preload:"metadata",playbackRate:1,showPlaybackSpeed:!1,playbackRates:[.5,.75,1,1.25,1.5,1.75,2],buttonAlign:"auto",waveformStyle:"mirror",barWidth:2,barSpacing:0,colorPreset:null,waveformColor:null,progressColor:null,buttonColor:null,buttonHoverColor:null,textColor:null,textSecondaryColor:null,backgroundColor:null,borderColor:null,autoplay:!1,showTime:!0,showHoverTime:!1,showBPM:!1,singlePlay:!0,playOnSeek:!0,enableMediaSession:!0,markers:[],showMarkers:!0,title:null,subtitle:null,artwork:null,album:"",playIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M8 5v14l11-7z"/></svg>',pauseIcon:'<svg viewBox="0 0 24 24" width="16" height="16"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg>',onLoad:null,onPlay:null,onPause:null,onEnd:null,onError:null,onTimeUpdate:null},H={bars:{barWidth:3,barSpacing:1},mirror:{barWidth:2,barSpacing:0},line:{barWidth:2,barSpacing:0},blocks:{barWidth:4,barSpacing:2},dots:{barWidth:3,barSpacing:3},seekbar:{barWidth:1,barSpacing:0}};var g=class t{static instances=new Map;static currentlyPlaying=null;constructor(e,i={}){if(this.container=typeof e=="string"?document.querySelector(e):e,!this.container)throw new Error("WaveformPlayer: Container element not found");let o=L(this.container);this.options=C(z,o,i);let a=O(this.options.colorPreset);for(let[r,s]of Object.entries(a))(this.options[r]===null||this.options[r]===void 0)&&(this.options[r]=s);let n=H[this.options.waveformStyle];n&&(o.barWidth===void 0&&i.barWidth===void 0&&(this.options.barWidth=n.barWidth),o.barSpacing===void 0&&i.barSpacing===void 0&&(this.options.barSpacing=n.barSpacing)),this.audio=null,this.canvas=null,this.ctx=null,this.waveformData=[],this.progress=0,this.isPlaying=!1,this.isLoading=!1,this.hasError=!1,this.updateTimer=null,this.resizeObserver=null,this.id=this.container.id||A(this.options.url),t.instances.set(this.id,this),this.init(),setTimeout(()=>{this.container.dispatchEvent(new CustomEvent("waveformplayer:ready",{detail:{player:this,url:this.options.url}}))},100)}init(){this.createDOM(),this.createAudio(),this.initPlaybackSpeed(),this.initKeyboardControls(),this.bindEvents(),this.setupResizeObserver(),requestAnimationFrame(()=>{this.resizeCanvas(),this.options.url&&this.load(this.options.url).then(()=>{this.options.autoplay&&this.play()}).catch(e=>{console.error("Failed to load audio:",e)})})}createDOM(){this.container.innerHTML="",this.container.className="waveform-player";let e=this.options.buttonAlign;e==="auto"&&(this.options.waveformStyle==="bars"?e="bottom":e="center"),this.container.innerHTML=`
|
|
2
2
|
<div class="waveform-player-inner">
|
|
3
3
|
<div class="waveform-body">
|
|
4
4
|
<div class="waveform-track waveform-align-${e}">
|
|
@@ -59,4 +59,4 @@
|
|
|
59
59
|
</div>
|
|
60
60
|
</div>
|
|
61
61
|
</div>
|
|
62
|
-
`,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",a=>{a.stopPropagation(),i.style.display=i.style.display==="none"?"block":"none"}),document.addEventListener("click",()=>{i.style.display="none"}),i.addEventListener("click",a=>{if(a.stopPropagation(),a.target.classList.contains("speed-option")){let o=parseFloat(a.target.dataset.rate);this.setPlaybackRate(o),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,a=this.audio.currentTime;if(i>="0"&&i<="9"){e.preventDefault(),this.seekToPercent(parseInt(i)/10);return}let o={" ":()=>this.togglePlay(),ArrowLeft:()=>this.seekTo(Math.max(0,a-5)),ArrowRight:()=>this.seekTo(Math.min(this.audio.duration,a+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};o[i]&&(e.preventDefault(),o[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.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)),window.addEventListener("resize",L(()=>this.resizeCanvas(),100))}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((a,o)=>{let n=()=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),a()},r=s=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),o(s)};this.audio.addEventListener("loadedmetadata",n),this.audio.addEventListener("error",r)});let i=this.options.title||A(e);if(this.titleEl&&(this.titleEl.textContent=i),this.options.waveform)this.setWaveformData(this.options.waveform);else try{let a=await M(e,this.options.samples,this.options.showBPM);this.waveformData=a.peaks,a.bpm&&(this.detectedBPM=a.bpm,this.updateBPMDisplay())}catch(a){console.warn("Using placeholder waveform:",a),this.waveformData=I(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,a=null,o={}){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:a||this.options.subtitle,...o}),o.preload&&(this.audio.preload=o.preload),this.subtitleEl&&(a?(this.subtitleEl.textContent=a,this.subtitleEl.style.display=""):a===""&&(this.subtitleEl.style.display="none")),o.artwork&&this.artworkEl&&(this.artworkEl.src=o.artwork),o.markers&&(this.options.markers=o.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||B(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){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 a=e.time/this.audio.duration*100,o=document.createElement("button");o.className="waveform-marker",o.style.left=`${a}%`,o.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",o.setAttribute("aria-label",e.label),o.setAttribute("data-time",e.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=e.label,o.appendChild(n),o.addEventListener("click",r=>{r.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(o)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),a=e.clientX-i.left,o=Math.max(0,Math.min(1,a/i.width));this.seekToPercent(o)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.totalTimeEl&&(this.totalTimeEl.textContent=C(this.audio.duration)),this.renderMarkers()}onPlay(){this.isPlaying=!0,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",{detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}onPause(){this.isPlaying=!1,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",{detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}onEnded(){this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{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",{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(){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),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 a=document.getElementById(e);if(a)return Array.from(this.instances.values()).find(o=>o.container===a)}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(a){throw console.error("Failed to generate waveform:",a),a}}};function E(){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",E):E());g.init=E;typeof window<"u"&&(window.WaveformPlayer=g);var st=g;})();
|
|
62
|
+
`,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 a=parseFloat(o.target.dataset.rate);this.setPlaybackRate(a),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 a={" ":()=>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};a[i]&&(e.preventDefault(),a[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.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)),window.addEventListener("resize",B(()=>this.resizeCanvas(),100))}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,a)=>{let n=()=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),o()},r=s=>{this.audio.removeEventListener("loadedmetadata",n),this.audio.removeEventListener("error",r),a(s)};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,a={}){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=C(this.options,{url:e,title:i||this.options.title,subtitle:o||this.options.subtitle,...a}),a.preload&&(this.audio.preload=a.preload),this.subtitleEl&&(o?(this.subtitleEl.textContent=o,this.subtitleEl.style.display=""):o===""&&(this.subtitleEl.style.display="none")),a.artwork&&this.artworkEl&&(this.artworkEl.src=a.artwork),a.markers&&(this.options.markers=a.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||W(this.ctx,this.canvas,this.waveformData,this.progress,{...this.options,waveformStyle:this.options.waveformStyle||"bars",color:this.options.waveformColor,progressColor:this.options.progressColor})}resizeCanvas(){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,a=document.createElement("button");a.className="waveform-marker",a.style.left=`${o}%`,a.style.backgroundColor=e.color||"rgba(255, 255, 255, 0.5)",a.setAttribute("aria-label",e.label),a.setAttribute("data-time",e.time);let n=document.createElement("span");n.className="waveform-marker-tooltip",n.textContent=e.label,a.appendChild(n),a.addEventListener("click",r=>{r.stopPropagation(),this.seekTo(e.time),this.options.playOnSeek&&!this.isPlaying&&this.play()}),this.markersContainer.appendChild(a)}))}handleCanvasClick(e){if(!this.audio.duration)return;let i=this.canvas.getBoundingClientRect(),o=e.clientX-i.left,a=Math.max(0,Math.min(1,o/i.width));this.seekToPercent(a)}setLoading(e){this.isLoading=e,this.loadingEl&&(this.loadingEl.style.display=e?"block":"none")}onMetadataLoaded(){this.totalTimeEl&&(this.totalTimeEl.textContent=P(this.audio.duration)),this.renderMarkers()}onPlay(){this.isPlaying=!0,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",{detail:{player:this,url:this.options.url}})),this.options.onPlay&&this.options.onPlay(this)}onPause(){this.isPlaying=!1,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",{detail:{player:this,url:this.options.url}})),this.options.onPause&&this.options.onPause(this)}onEnded(){this.progress=0,this.audio.currentTime=0,this.drawWaveform(),this.currentTimeEl&&(this.currentTimeEl.textContent="0:00"),this.container.dispatchEvent(new CustomEvent("waveformplayer:ended",{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=P(this.audio.currentTime)),this.container.dispatchEvent(new CustomEvent("waveformplayer:timeupdate",{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(){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),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(a=>a.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
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Lightweight, customizable audio player with waveform visualization",
|
|
5
5
|
"main": "dist/waveform-player.js",
|
|
6
6
|
"module": "dist/waveform-player.esm.js",
|
|
@@ -33,7 +33,14 @@
|
|
|
33
33
|
"music",
|
|
34
34
|
"sound",
|
|
35
35
|
"html5",
|
|
36
|
-
"web-audio"
|
|
36
|
+
"web-audio",
|
|
37
|
+
"media-session",
|
|
38
|
+
"audio-player",
|
|
39
|
+
"waveform-visualization",
|
|
40
|
+
"soundcloud",
|
|
41
|
+
"podcast-player",
|
|
42
|
+
"audio-visualization",
|
|
43
|
+
"bpm-detection"
|
|
37
44
|
],
|
|
38
45
|
"author": "ArrayPress",
|
|
39
46
|
"license": "MIT",
|
|
@@ -44,7 +51,21 @@
|
|
|
44
51
|
"bugs": {
|
|
45
52
|
"url": "https://github.com/arraypress/waveform-player/issues"
|
|
46
53
|
},
|
|
47
|
-
"homepage": "https://
|
|
54
|
+
"homepage": "https://waveformplayer.com",
|
|
55
|
+
"funding": {
|
|
56
|
+
"type": "github",
|
|
57
|
+
"url": "https://github.com/sponsors/arraypress"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=14.0.0"
|
|
61
|
+
},
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public",
|
|
64
|
+
"registry": "https://registry.npmjs.org/"
|
|
65
|
+
},
|
|
66
|
+
"sideEffects": [
|
|
67
|
+
"*.css"
|
|
68
|
+
],
|
|
48
69
|
"devDependencies": {
|
|
49
70
|
"concurrently": "^7.6.0",
|
|
50
71
|
"esbuild": "^0.25.0"
|
package/src/js/core.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
debounce
|
|
15
15
|
} from './utils.js';
|
|
16
16
|
|
|
17
|
-
import {DEFAULT_OPTIONS, STYLE_DEFAULTS} from './themes.js';
|
|
17
|
+
import {DEFAULT_OPTIONS, STYLE_DEFAULTS, getColorPreset} from './themes.js';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* WaveformPlayer - Modern audio player with waveform visualization
|
|
@@ -48,6 +48,16 @@ export class WaveformPlayer {
|
|
|
48
48
|
// Merge options: defaults < data attributes < constructor options
|
|
49
49
|
this.options = mergeOptions(DEFAULT_OPTIONS, dataOptions, options);
|
|
50
50
|
|
|
51
|
+
// Apply color preset (auto-detect if not specified)
|
|
52
|
+
const preset = getColorPreset(this.options.colorPreset);
|
|
53
|
+
|
|
54
|
+
// Apply preset colors only if individual colors aren't explicitly set
|
|
55
|
+
for (const [key, value] of Object.entries(preset)) {
|
|
56
|
+
if (this.options[key] === null || this.options[key] === undefined) {
|
|
57
|
+
this.options[key] = value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
// Apply style-specific defaults if not explicitly set
|
|
52
62
|
const styleDefaults = STYLE_DEFAULTS[this.options.waveformStyle];
|
|
53
63
|
if (styleDefaults) {
|
|
@@ -59,13 +69,6 @@ export class WaveformPlayer {
|
|
|
59
69
|
}
|
|
60
70
|
}
|
|
61
71
|
|
|
62
|
-
// Set default colors if not provided
|
|
63
|
-
this.options.waveformColor = this.options.waveformColor || 'rgba(255, 255, 255, 0.3)';
|
|
64
|
-
this.options.progressColor = this.options.progressColor || 'rgba(255, 255, 255, 0.9)';
|
|
65
|
-
this.options.buttonColor = this.options.buttonColor || 'rgba(255, 255, 255, 0.9)';
|
|
66
|
-
this.options.textColor = this.options.textColor || '#ffffff';
|
|
67
|
-
this.options.textSecondaryColor = this.options.textSecondaryColor || 'rgba(255, 255, 255, 0.6)';
|
|
68
|
-
|
|
69
72
|
// Initialize state
|
|
70
73
|
this.audio = null;
|
|
71
74
|
this.canvas = null;
|
|
@@ -90,7 +93,7 @@ export class WaveformPlayer {
|
|
|
90
93
|
// Dispatch ready event after initialization
|
|
91
94
|
setTimeout(() => {
|
|
92
95
|
this.container.dispatchEvent(new CustomEvent('waveformplayer:ready', {
|
|
93
|
-
detail: {
|
|
96
|
+
detail: {player: this, url: this.options.url}
|
|
94
97
|
}));
|
|
95
98
|
}, 100);
|
|
96
99
|
}
|
|
@@ -744,7 +747,7 @@ export class WaveformPlayer {
|
|
|
744
747
|
|
|
745
748
|
// Dispatch play event
|
|
746
749
|
this.container.dispatchEvent(new CustomEvent('waveformplayer:play', {
|
|
747
|
-
detail: {
|
|
750
|
+
detail: {player: this, url: this.options.url}
|
|
748
751
|
}));
|
|
749
752
|
|
|
750
753
|
if (this.options.onPlay) {
|
|
@@ -769,7 +772,7 @@ export class WaveformPlayer {
|
|
|
769
772
|
|
|
770
773
|
// Dispatch pause event
|
|
771
774
|
this.container.dispatchEvent(new CustomEvent('waveformplayer:pause', {
|
|
772
|
-
detail: {
|
|
775
|
+
detail: {player: this, url: this.options.url}
|
|
773
776
|
}));
|
|
774
777
|
|
|
775
778
|
if (this.options.onPause) {
|
|
@@ -793,7 +796,7 @@ export class WaveformPlayer {
|
|
|
793
796
|
|
|
794
797
|
// Dispatch ended event
|
|
795
798
|
this.container.dispatchEvent(new CustomEvent('waveformplayer:ended', {
|
|
796
|
-
detail: {
|
|
799
|
+
detail: {player: this, url: this.options.url}
|
|
797
800
|
}));
|
|
798
801
|
|
|
799
802
|
this.onPause();
|
package/src/js/themes.js
CHANGED
|
@@ -3,6 +3,76 @@
|
|
|
3
3
|
* @description Color presets and default options for WaveformPlayer
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Detect appropriate color scheme
|
|
8
|
+
* Priority: 1) Explicit classes, 2) Website background, 3) System preference, 4) Default
|
|
9
|
+
* @returns {string} 'dark' or 'light'
|
|
10
|
+
*/
|
|
11
|
+
export function detectColorScheme() {
|
|
12
|
+
const root = document.documentElement;
|
|
13
|
+
const body = document.body;
|
|
14
|
+
|
|
15
|
+
// 1. Check for explicit theme class names and data attributes FIRST
|
|
16
|
+
// Check for dark theme indicators
|
|
17
|
+
if (root.classList.contains('dark') ||
|
|
18
|
+
root.classList.contains('dark-mode') ||
|
|
19
|
+
root.classList.contains('theme-dark') ||
|
|
20
|
+
root.getAttribute('data-theme') === 'dark' ||
|
|
21
|
+
root.getAttribute('data-color-scheme') === 'dark' ||
|
|
22
|
+
body.classList.contains('dark') ||
|
|
23
|
+
body.classList.contains('dark-mode') ||
|
|
24
|
+
body.getAttribute('data-theme') === 'dark') {
|
|
25
|
+
return 'dark';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Check for light theme indicators
|
|
29
|
+
if (root.classList.contains('light') ||
|
|
30
|
+
root.classList.contains('light-mode') ||
|
|
31
|
+
root.classList.contains('theme-light') ||
|
|
32
|
+
root.getAttribute('data-theme') === 'light' ||
|
|
33
|
+
root.getAttribute('data-color-scheme') === 'light' ||
|
|
34
|
+
body.classList.contains('light') ||
|
|
35
|
+
body.classList.contains('light-mode') ||
|
|
36
|
+
body.getAttribute('data-theme') === 'light') {
|
|
37
|
+
return 'light';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. Try to detect website's theme from background color
|
|
41
|
+
try {
|
|
42
|
+
const bodyBg = getComputedStyle(document.body).backgroundColor;
|
|
43
|
+
|
|
44
|
+
// Parse RGB values
|
|
45
|
+
const rgb = bodyBg.match(/\d+/g);
|
|
46
|
+
if (rgb && rgb.length >= 3) {
|
|
47
|
+
const [r, g, b] = rgb.map(Number);
|
|
48
|
+
// Calculate perceived brightness using luminance formula (0-255)
|
|
49
|
+
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
50
|
+
|
|
51
|
+
// Clear determination: bright background = light theme
|
|
52
|
+
if (brightness > 128) {
|
|
53
|
+
return 'light';
|
|
54
|
+
} else if (brightness < 128) {
|
|
55
|
+
return 'dark';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
// If background detection fails, continue to next method
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 3. Check system preference
|
|
63
|
+
if (window.matchMedia) {
|
|
64
|
+
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
65
|
+
return 'dark';
|
|
66
|
+
}
|
|
67
|
+
if (window.matchMedia('(prefers-color-scheme: light)').matches) {
|
|
68
|
+
return 'light';
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 4. Default fallback (most audio players are dark)
|
|
73
|
+
return 'dark';
|
|
74
|
+
}
|
|
75
|
+
|
|
6
76
|
/**
|
|
7
77
|
* Color presets - simple dark/light defaults that can be overridden
|
|
8
78
|
*/
|
|
@@ -29,6 +99,22 @@ export const COLOR_PRESETS = {
|
|
|
29
99
|
}
|
|
30
100
|
};
|
|
31
101
|
|
|
102
|
+
/**
|
|
103
|
+
* Get color preset by name, with auto-detection fallback
|
|
104
|
+
* @param {string|null} presetName - Preset name ('dark', 'light') or null for auto-detect
|
|
105
|
+
* @returns {Object} Color preset object
|
|
106
|
+
*/
|
|
107
|
+
export function getColorPreset(presetName) {
|
|
108
|
+
// If explicitly set to a valid preset, use it
|
|
109
|
+
if (presetName && COLOR_PRESETS[presetName]) {
|
|
110
|
+
return COLOR_PRESETS[presetName];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Auto-detect if not specified or invalid
|
|
114
|
+
const detected = detectColorScheme();
|
|
115
|
+
return COLOR_PRESETS[detected];
|
|
116
|
+
}
|
|
117
|
+
|
|
32
118
|
/**
|
|
33
119
|
* Default player options
|
|
34
120
|
*/
|
|
@@ -42,18 +128,18 @@ export const DEFAULT_OPTIONS = {
|
|
|
42
128
|
// Playback
|
|
43
129
|
playbackRate: 1,
|
|
44
130
|
showPlaybackSpeed: false,
|
|
45
|
-
playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
|
|
131
|
+
playbackRates: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
|
|
46
132
|
|
|
47
133
|
// Layout Options
|
|
48
|
-
buttonAlign: 'auto',
|
|
134
|
+
buttonAlign: 'auto',
|
|
49
135
|
|
|
50
136
|
// Default waveform style
|
|
51
137
|
waveformStyle: 'mirror',
|
|
52
138
|
barWidth: 2,
|
|
53
139
|
barSpacing: 0,
|
|
54
140
|
|
|
55
|
-
// Color preset
|
|
56
|
-
colorPreset:
|
|
141
|
+
// Color preset: null = auto-detect, 'dark' = force dark, 'light' = force light
|
|
142
|
+
colorPreset: null,
|
|
57
143
|
|
|
58
144
|
// Individual color overrides (null means use preset)
|
|
59
145
|
waveformColor: null,
|
|
@@ -101,10 +187,10 @@ export const DEFAULT_OPTIONS = {
|
|
|
101
187
|
* Style defaults
|
|
102
188
|
*/
|
|
103
189
|
export const STYLE_DEFAULTS = {
|
|
104
|
-
bars: {
|
|
105
|
-
mirror: {
|
|
106
|
-
line: {
|
|
107
|
-
blocks: {
|
|
108
|
-
dots: {
|
|
109
|
-
seekbar: {
|
|
190
|
+
bars: {barWidth: 3, barSpacing: 1},
|
|
191
|
+
mirror: {barWidth: 2, barSpacing: 0},
|
|
192
|
+
line: {barWidth: 2, barSpacing: 0},
|
|
193
|
+
blocks: {barWidth: 4, barSpacing: 2},
|
|
194
|
+
dots: {barWidth: 3, barSpacing: 3},
|
|
195
|
+
seekbar: {barWidth: 1, barSpacing: 0}
|
|
110
196
|
};
|