soundwave/DEBUG_PWA_OFFLINE.md
Iulian 0be38ca945 Fix CORS configuration for PWA offline caching
- Add CORS_ALLOWED_ORIGINS environment variable support in Django settings
- Update docker-compose.yml to include CORS origins (HTTP, HTTPS, custom domain)
- Add comprehensive PWA offline debugging guide
2025-12-24 01:20:41 +00:00

9.1 KiB

🐛 PWA Offline Caching Debug Guide

Issue Summary

PWA fails to cache playlists for offline playing with errors like "Failed to cache playlist" and no sound plays offline.

Root Causes Identified

1. CORS Configuration Missing

Problem: Django wasn't reading CORS_ALLOWED_ORIGINS from environment variables.

Fixed: Updated backend/config/settings.py to read from environment variable.

Verify Fix:

# Rebuild container
docker compose build
docker compose up -d

# Check logs
docker compose logs soundwave | grep CORS

2. Service Worker Not Receiving Messages

Problem: Service worker might not be active or registered properly.

Check:

// In browser console
navigator.serviceWorker.getRegistration().then(reg => {
  console.log('SW Registered:', !!reg);
  console.log('SW Active:', reg?.active?.state);
});

Expected output:

SW Registered: true
SW Active: "activated"

3. Audio Fetch Failing Due to CORS or Auth

Problem: Service worker can't fetch audio files due to CORS or authentication issues.

Step-by-Step Debugging

Step 1: Check Service Worker Status

Open DevTools (F12) → Application tab → Service Workers

You should see:

  • service-worker.js registered
  • Status: "activated and is running"
  • Source: http://localhost:8889/service-worker.js

If not registered:

// In console
navigator.serviceWorker.register('/service-worker.js')
  .then(reg => console.log('SW Registered', reg))
  .catch(err => console.error('SW Registration failed', err));

Step 2: Test Service Worker Communication

// In browser console
const testSW = async () => {
  const reg = await navigator.serviceWorker.ready;
  
  if (!reg.active) {
    console.error('❌ No active service worker');
    return;
  }
  
  const messageChannel = new MessageChannel();
  
  messageChannel.port1.onmessage = (event) => {
    console.log('✅ Response from SW:', event.data);
  };
  
  reg.active.postMessage(
    { 
      type: 'CACHE_PLAYLIST', 
      playlistId: 'test123',
      audioUrls: ['/api/audio/test/download/']
    },
    [messageChannel.port2]
  );
};

testSW();

Expected: Response from service worker within 1-2 seconds.

Step 3: Check CORS Headers

Open DevTools → Network tab

  1. Navigate to a playlist
  2. Click "Make Available Offline"
  3. Look for requests to /api/audio/{id}/download/
  4. Click on one request
  5. Check Response Headers

Required headers:

Access-Control-Allow-Origin: https://sound.iulian.uk
Access-Control-Allow-Credentials: true
Content-Type: audio/mpeg (or audio/mp4, etc.)

If CORS headers missing:

  • Backend CORS not configured correctly
  • Check CORS_ALLOWED_ORIGINS environment variable

Step 4: Test Manual Audio Fetch

// In browser console
const testAudioFetch = async () => {
  try {
    const response = await fetch('/api/audio/YOUR_YOUTUBE_ID/download/', {
      credentials: 'include',
      headers: {
        'Accept': 'audio/*'
      }
    });
    
    console.log('Status:', response.status);
    console.log('Headers:', [...response.headers.entries()]);
    console.log('Content-Type:', response.headers.get('content-type'));
    
    if (response.ok) {
      console.log('✅ Audio fetch successful');
      const blob = await response.blob();
      console.log('✅ Blob size:', (blob.size / 1024 / 1024).toFixed(2), 'MB');
    } else {
      console.error('❌ Audio fetch failed:', response.statusText);
    }
  } catch (err) {
    console.error('❌ Fetch error:', err);
  }
};

testAudioFetch();

Replace YOUR_YOUTUBE_ID with an actual YouTube ID from your playlist.

Step 5: Inspect Service Worker Console

  1. DevTools → ApplicationService Workers
  2. Find the active service worker
  3. Click "inspect" next to it
  4. A new DevTools window opens (Service Worker console)
  5. Try caching a playlist again
  6. Watch the SW console for errors

Expected logs in SW console:

[Service Worker] Message received: {type: 'CACHE_PLAYLIST', ...}
[Service Worker] Caching playlist: PLxxx with 5 tracks
[Service Worker] Cached playlist metadata
[Service Worker] Fetching: /api/audio/xxx/download/
[Service Worker] ✓ Cached: /api/audio/xxx/download/ - 3.45 MB
[Service Worker] ✓ Cached: /api/audio/yyy/download/ - 4.12 MB
...
[Service Worker] Playlist caching complete

Common errors:

❌ TypeError: Failed to fetch
   → CORS issue or network problem

❌ HTTP 401 Unauthorized
   → Authentication cookies not sent

❌ HTTP 403 Forbidden
   → CORS or CSRF issue

❌ HTTP 404 Not Found
   → Audio not downloaded yet or wrong URL

Step 6: Check Cache Storage

DevTools → ApplicationCache Storage

You should see:

  • soundwave-v1 - Static assets
  • soundwave-api-v1 - API responses
  • soundwave-audio-v1 - Audio files ← This is where audio should be
  • soundwave-images-v1 - Images

Click on soundwave-audio-v1:

  • Should show cached audio files with keys like /api/audio/{id}/download/
  • Each entry should have a size (MB)

If empty after caching:

  • Caching failed silently
  • Check SW console for errors

Step 7: Test Offline Playback

  1. Cache a playlist (follow steps above to ensure it works)
  2. DevTools → Network tab
  3. Change throttling to "Offline"
  4. Try playing a track

What to check:

  • Network tab shows requests but they should come from "ServiceWorker"
  • Console should show: [Service Worker] ✓ Serving audio from cache: /api/audio/xxx/download/

If fails:

❌ [Service Worker] ✗ Cache miss for: /api/audio/xxx/download/
   → Audio wasn't cached properly
   
❌ [Service Worker] Available cached audio: []
   → Cache is empty

Quick Fixes

Fix 1: Restart Docker Containers

cd /home/iulian/projects/zi-tube/soundwave
docker compose down
docker compose build --no-cache
docker compose up -d

Fix 2: Clear All Caches

// In browser console
const clearAllCaches = async () => {
  const cacheNames = await caches.keys();
  await Promise.all(cacheNames.map(name => caches.delete(name)));
  console.log('✅ All caches cleared');
  
  // Unregister service worker
  const reg = await navigator.serviceWorker.getRegistration();
  if (reg) {
    await reg.unregister();
    console.log('✅ Service worker unregistered');
  }
  
  console.log('🔄 Reload page now');
};

clearAllCaches();

Then reload the page (hard refresh: Ctrl+Shift+R).

Fix 3: Update Service Worker

// In browser console
const updateSW = async () => {
  const reg = await navigator.serviceWorker.getRegistration();
  if (reg) {
    await reg.update();
    console.log('✅ Service worker updated');
  }
};

updateSW();

Fix 4: Check Container Environment Variables

# Check if CORS is set
docker compose exec soundwave env | grep CORS

# Should show:
# CORS_ALLOWED_ORIGINS=http://localhost:8889,https://localhost:8889,https://sound.iulian.uk

If not shown, environment variable not set in docker-compose.yml.

Known Issues & Solutions

Issue: "Failed to cache playlist" after 60 seconds

Cause: Timeout - too many files or slow network

Solution:

  1. Cache smaller playlists first (< 10 tracks)
  2. Check network speed
  3. Increase timeout in frontend/src/utils/pwa.ts (line ~325)

Issue: CORS error when fetching audio

Cause: Backend not configured for HTTPS origin

Solution: Already fixed in docker-compose.yml and settings.py

Issue: Service worker not updating

Cause: Browser caching old service worker

Solution:

  1. DevTools → Application → Service Workers
  2. Check "Update on reload"
  3. Hard refresh (Ctrl+Shift+R)

Issue: Audio plays online but not offline

Cause: Audio not in cache or wrong cache key

Solution:

  1. Check Cache Storage (Step 6)
  2. Verify cache keys match request URLs exactly
  3. Re-cache the playlist

Testing Checklist

Before reporting the issue is fixed:

  • Service worker registered and active
  • CORS headers present on audio downloads
  • Can cache a small playlist (3-5 tracks)
  • Cache Storage shows audio files
  • Offline mode: can play cached tracks
  • Network tab shows "ServiceWorker" as source
  • No console errors during playback

Advanced: Check Django CORS Settings

# SSH into container
docker compose exec soundwave bash

# Open Django shell
python manage.py shell

# Check CORS settings
from django.conf import settings
print("CORS_ALLOWED_ORIGINS:", settings.CORS_ALLOWED_ORIGINS)
print("CORS_ALLOW_CREDENTIALS:", settings.CORS_ALLOW_CREDENTIALS)
print("CSRF_TRUSTED_ORIGINS:", settings.CSRF_TRUSTED_ORIGINS)

Expected output:

CORS_ALLOWED_ORIGINS: ['http://localhost:8889', 'https://localhost:8889', 'https://sound.iulian.uk']
CORS_ALLOW_CREDENTIALS: True
CSRF_TRUSTED_ORIGINS: ['http://localhost:8889', 'https://localhost:8889', 'https://sound.iulian.uk']

Need More Help?

If still not working after following this guide:

  1. Share output from Step 1 (SW status)
  2. Share output from Step 4 (audio fetch test)
  3. Share screenshot of Step 5 (SW console errors)
  4. Share screenshot of Step 6 (Cache Storage)

This will help identify the exact issue.