z-astro-web-audio-stream
Version:
Astro integration for Web Audio Stream with separated download/storage optimization and automatic worklet deployment
288 lines (226 loc) ⢠7.74 kB
Markdown
# z-astro-z-web-audio-stream
Astro integration for Web Audio Stream with automatic worklet deployment.
## š What This Fixes
This integration automatically sets up iOS Safari-safe audio streaming that fixes:
- **Sample Rate Mismatches** - High-pitched/fast audio playback
- **Memory Pressure** - Page reloads from large audio files
- **IndexedDB Failures** - Safari connection issues
- **Broken AudioContext** - iOS-specific Web Audio bugs
## š¦ Installation
```bash
npm install z-astro-z-web-audio-stream z-web-audio-stream
# or
pnpm add z-astro-z-web-audio-stream z-web-audio-stream
```
## š Setup
Add the integration to your `astro.config.mjs`:
```javascript
import { defineConfig } from 'astro/config';
import webAudioStream from 'z-astro-z-web-audio-stream';
export default defineConfig({
integrations: [
webAudioStream({
// Optional configuration
workletPath: '/audio-worklet-processor.js',
publicDir: 'public',
autoDeploy: true,
verbose: true
})
]
});
```
## āļø Configuration
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `workletPath` | `string` | `'/audio-worklet-processor.js'` | URL path where worklet will be served |
| `publicDir` | `string` | `'public'` | Public directory relative to project root |
| `autoDeploy` | `boolean` | `true` | Automatically copy worklet during build |
| `verbose` | `boolean` | `true` | Log deployment information |
## šµ Usage in Components
### Astro Component
```astro
---
// src/components/AudioPlayer.astro
---
<div id="audio-player">
<button id="play-btn">Play</button>
<button id="pause-btn">Pause</button>
</div>
<script>
import { setupWebAudio } from 'z-web-audio-stream';
// Initialize with iOS-safe defaults
const manager = await setupWebAudio({
workletPath: '/audio-worklet-processor.js', // Automatically deployed!
onTimeUpdate: (currentTime, duration) => {
console.log(`Playing: ${currentTime}s / ${duration}s`);
},
onEnded: () => {
console.log('Playback finished');
}
});
// Control buttons
document.getElementById('play-btn')?.addEventListener('click', async () => {
await manager.loadAndPlay('/audio/song.mp3', 'song-1', 'My Song');
});
document.getElementById('pause-btn')?.addEventListener('click', async () => {
await manager.pause();
});
</script>
```
### React Component (in Astro)
```tsx
// src/components/ReactAudioPlayer.tsx
import { useEffect, useState } from 'react';
import { setupWebAudio, type WebAudioManager } from 'z-web-audio-stream';
export default function ReactAudioPlayer() {
const [manager, setManager] = useState<WebAudioManager | null>(null);
const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
useEffect(() => {
setupWebAudio({
workletPath: '/audio-worklet-processor.js', // Auto-deployed by integration
onTimeUpdate: (time, dur) => {
setCurrentTime(time);
setDuration(dur);
},
onEnded: () => {
setIsPlaying(false);
}
}).then(setManager);
return () => {
manager?.cleanup();
};
}, []);
const handlePlay = async () => {
if (!manager) return;
await manager.loadAndPlay('/audio/song.mp3', 'song-1', 'My Song');
setIsPlaying(true);
};
const handlePause = async () => {
if (!manager) return;
await manager.pause();
setIsPlaying(false);
};
return (
<div className="audio-player">
<button onClick={isPlaying ? handlePause : handlePlay}>
{isPlaying ? 'Pause' : 'Play'}
</button>
{duration > 0 && (
<div className="progress">
<div
className="progress-bar"
style={{ width: `${(currentTime / duration) * 100}%` }}
/>
</div>
)}
<span>{Math.floor(currentTime)}s / {Math.floor(duration)}s</span>
</div>
);
}
```
## šļø Build Process
During build, the integration:
1. ā
**Finds** the iOS-safe audio worklet processor
2. ā
**Copies** it to your public directory
3. ā
**Configures** the correct path automatically
4. ā
**Logs** deployment status (if verbose enabled)
**Build Output:**
```
šµ Web Audio Stream integration loaded
š iOS Safari audio fixes will be applied automatically
ā
Audio worklet deployed to: /path/to/public/audio-worklet-processor.js
š§ Use in your app: setupWebAudio({ workletPath: "/audio-worklet-processor.js" })
```
## š§ Manual Deployment
If you prefer manual control:
```javascript
// astro.config.mjs
export default defineConfig({
integrations: [
webAudioStream({
autoDeploy: false // Disable automatic deployment
})
]
});
```
Then deploy manually:
```bash
npx z-web-audio-stream-cli deploy
```
## š iOS Safari Benefits
This integration ensures your Astro site works perfectly on iOS Safari:
- **ā
No High-Pitched Audio** - Sample rate monitoring and correction
- **ā
No Page Reloads** - Memory-safe 1-2MB chunks on iOS
- **ā
Reliable Caching** - IndexedDB retry logic for Safari
- **ā
Instant Playback** - Progressive loading with first chunk
- **ā
Zero Configuration** - Works out of the box
## š± Mobile-First Examples
### Progressive Loading with Preloading
```astro
<script>
import { setupWebAudio } from 'z-web-audio-stream';
const manager = await setupWebAudio();
// Preload next tracks for seamless transitions
await manager.preloadAudio('/audio/song1.mp3', 'song-1', 'Song 1');
await manager.preloadAudio('/audio/song2.mp3', 'song-2', 'Song 2');
// Play immediately (loads from cache)
await manager.loadAndPlay('/audio/song1.mp3', 'song-1', 'Song 1');
</script>
```
### Playlist with iOS Optimizations
```tsx
function Playlist({ songs }) {
const [manager, setManager] = useState<WebAudioManager | null>(null);
const [currentSong, setCurrentSong] = useState(0);
useEffect(() => {
setupWebAudio({
onEnded: () => {
// Auto-advance to next song
setCurrentSong(prev => (prev + 1) % songs.length);
}
}).then(async (audioManager) => {
setManager(audioManager);
// Preload all songs for iOS-safe smooth playback
for (const song of songs) {
await audioManager.preloadAudio(song.url, song.id, song.name);
}
});
}, [songs]);
const playSong = async (index: number) => {
if (!manager) return;
const song = songs[index];
await manager.loadAndPlay(song.url, song.id, song.name);
setCurrentSong(index);
};
return (
<div className="playlist">
{songs.map((song, index) => (
<button
key={song.id}
onClick={() => playSong(index)}
className={currentSong === index ? 'active' : ''}
>
{song.name}
</button>
))}
</div>
);
}
```
## š Troubleshooting
**Integration not found**
```bash
npm install z-astro-z-web-audio-stream
```
**Worklet file not deployed**
- Check that `autoDeploy: true` (default)
- Verify `z-web-audio-stream` is installed
- Check build logs for error messages
**iOS audio still has issues**
- Ensure you're using the deployed worklet path
- Check browser console for iOS-specific logs
- Verify the integration ran during build
## š License
MIT License - Part of the Web Audio Stream package suite.