# Quick Start: Offline Playlist Features ## ๐ฏ For Users ### Download a Playlist for Offline Use (PWA UI) When you're online, you can download any playlist to use offline: 1. **Open a playlist** in the Soundwave app 2. Click the **"Download for Offline"** button (โฌ๏ธ) 3. Wait for download to complete 4. The playlist will now work **even without internet** ### Use Offline Playlists - Downloaded playlists appear with an **offline badge** (๐ถ) - Audio plays directly from cache (no buffering!) - Metadata loads instantly from IndexedDB ### Remove Offline Playlist 1. Open the downloaded playlist 2. Click **"Remove Offline Data"** (๐๏ธ) 3. Frees up storage space ## ๐ป For Developers ### Check if Playlist is Cached ```typescript import { offlineStorage } from '../utils/offlineStorage'; const playlist = await offlineStorage.getPlaylist(playlistId); if (playlist && playlist.offline) { console.log('Playlist available offline!'); } ``` ### Cache a Playlist ```typescript import { usePWA } from '../context/PWAContext'; import { offlineStorage } from '../utils/offlineStorage'; function DownloadButton({ playlist }) { const { cachePlaylist, isOnline } = usePWA(); const handleDownload = async () => { if (!isOnline) { alert('Must be online to download'); return; } // Get all audio URLs from playlist const audioUrls = playlist.items.map(item => item.audio_url); // Cache in Service Worker const cached = await cachePlaylist(playlist.id, audioUrls); if (cached) { // Save metadata to IndexedDB await offlineStorage.savePlaylist({ id: playlist.id, title: playlist.title, description: playlist.description, items: playlist.items, offline: true, lastSync: Date.now(), }); alert('Playlist downloaded for offline use!'); } }; return ( ); } ``` ### Get All Offline Playlists ```typescript const offlinePlaylists = await offlineStorage.getOfflinePlaylists(); console.log(`You have ${offlinePlaylists.length} playlists available offline`); ``` ### Remove Cached Playlist ```typescript import { usePWA } from '../context/PWAContext'; import { offlineStorage } from '../utils/offlineStorage'; async function removeOffline(playlist) { const { removePlaylistCache } = usePWA(); const audioUrls = playlist.items.map(item => item.audio_url); // Remove from Service Worker cache await removePlaylistCache(playlist.id, audioUrls); // Remove from IndexedDB await offlineStorage.removePlaylist(playlist.id); } ``` ### Check Storage Usage ```typescript const { cacheSize } = usePWA(); if (cacheSize) { const usedMB = (cacheSize.usage / 1024 / 1024).toFixed(2); const quotaMB = (cacheSize.quota / 1024 / 1024).toFixed(2); const percent = ((cacheSize.usage / cacheSize.quota) * 100).toFixed(1); console.log(`Storage: ${usedMB} MB / ${quotaMB} MB (${percent}%)`); } ``` ## ๐ Sync Strategy ### When Online - User downloads playlist โ cached in Service Worker + IndexedDB - Audio files stored in browser cache - Metadata stored in IndexedDB ### When Offline - Service Worker serves cached audio files - IndexedDB provides playlist metadata - No network requests needed ### When Back Online - Check for playlist updates - Sync any pending changes - Update cache if needed ## ๐จ UI Integration Example ```typescript import { usePWA } from '../context/PWAContext'; import { offlineStorage } from '../utils/offlineStorage'; import { useState, useEffect } from 'react'; function PlaylistCard({ playlist }) { const { cachePlaylist, removePlaylistCache, isOnline } = usePWA(); const [isOffline, setIsOffline] = useState(false); const [downloading, setDownloading] = useState(false); useEffect(() => { // Check if cached offlineStorage.getPlaylist(playlist.id).then(cached => { setIsOffline(cached?.offline || false); }); }, [playlist.id]); const handleDownload = async () => { setDownloading(true); try { const audioUrls = playlist.items.map(i => i.audio_url); await cachePlaylist(playlist.id, audioUrls); await offlineStorage.savePlaylist({ ...playlist, offline: true, lastSync: Date.now(), }); setIsOffline(true); } catch (error) { console.error('Download failed:', error); } finally { setDownloading(false); } }; const handleRemove = async () => { try { const audioUrls = playlist.items.map(i => i.audio_url); await removePlaylistCache(playlist.id, audioUrls); await offlineStorage.removePlaylist(playlist.id); setIsOffline(false); } catch (error) { console.error('Remove failed:', error); } }; return (