diff --git a/JELLYFIN_ERROR_ANALYSIS.md b/JELLYFIN_ERROR_ANALYSIS.md new file mode 100644 index 0000000..4fa0b23 --- /dev/null +++ b/JELLYFIN_ERROR_ANALYSIS.md @@ -0,0 +1,413 @@ +# Jellyfin Error Analysis and Recommendations + +## Overview +Analysis of Jellyfin errors found in the media namespace logs, with recommendations for resolution. + +--- + +## Issues Identified + +### 1. **SQLite Database Locking Errors** (CRITICAL) + +**Error Messages:** +``` +[04:28:37] [ERR] Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 5: 'database is locked'. +[09:00:00] [ERR] SQLite Error 5: 'database is locked For more information on this error code see https://www.sqlite.org/rescode.html'. +``` + +**Impact:** +- Prevents metadata updates during library scans +- Causes API request failures (GET /Items) +- Occurs when database optimization runs concurrently with library operations + +**Root Cause:** +SQLite's default locking mechanism allows only one writer at a time. The errors occur when: +1. Database optimization task runs (VACUUM operation) +2. Library scan attempts to update metadata simultaneously +3. Both operations try to write to the database + +**Frequency:** +- Database optimization runs approximately every 6 hours +- Library scans run every 12 hours +- Conflicts occur when these overlap + +--- + +### 2. **Audio Normalization Failures** (MODERATE) + +**Error Message:** +``` +[04:24:54] [ERR] Emby.Server.Implementations.ScheduledTasks.Tasks.AudioNormalizationTask: Failed to find LUFS value in output +``` + +**Impact:** +- Audio normalization task cannot measure loudness +- Prevents automatic volume leveling across music library +- Task completes without performing any normalization + +**Root Cause:** +The LUFS (Loudness Units relative to Full Scale) detection failure suggests: +1. FFmpeg may be missing required audio analysis filters +2. Audio files may have incompatible formats +3. FFmpeg output parsing may be failing + +**Frequency:** +- Runs daily at 04:24 (during scheduled tasks) +- Consistently fails to find LUFS values + +--- + +### 3. **Library Folder Warnings** (LOW PRIORITY) + +**Warning Messages:** +``` +[04:25:47] [WRN] Library folder /ebooks is inaccessible or empty, skipping +[04:25:47] [WRN] Library folder /config/data/playlists is inaccessible or empty, skipping +``` + +**Impact:** +- Minimal - warnings only +- No functional issues if these folders are intentionally empty +- Causes log noise + +**Root Cause:** +- Library folders are configured but: + - `/ebooks` folder doesn't exist or is empty + - `/config/data/playlists` folder is empty +- Jellyfin skips these during library scans + +--- + +## Recommendations + +### Priority 1: Fix Database Locking Issues + +#### Option A: Enable WAL Mode (RECOMMENDED) + +WAL (Write-Ahead Logging) mode allows concurrent readers during writes, reducing lock contention. + +**Implementation:** + +1. **Create a job to enable WAL mode:** + +Create file: `/storage/emulated/0/Download/nv/jellyfin-wal-enable.yaml` + +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: jellyfin-enable-wal + namespace: media +spec: + ttlSecondsAfterFinished: 600 + template: + spec: + restartPolicy: Never + containers: + - name: enable-wal + image: alpine:latest + command: + - sh + - -c + - | + apk add --no-cache sqlite + echo "Enabling WAL mode on jellyfin.db..." + sqlite3 /config/data/jellyfin.db "PRAGMA journal_mode=WAL;" + echo "Verifying WAL mode..." + sqlite3 /config/data/jellyfin.db "PRAGMA journal_mode;" + echo "Done!" + volumeMounts: + - name: jellyfin-data + mountPath: /config + volumes: + - name: jellyfin-data + persistentVolumeClaim: + claimName: jellyfin-config +``` + +**Benefits:** +- Multiple readers can access database during writes +- Reduces lock contention significantly +- Better performance under concurrent load +- Industry standard for high-concurrency SQLite + +**Caveats:** +- Creates additional files (`jellyfin.db-wal`, `jellyfin.db-shm`) +- Requires slightly more disk space +- NFS must support file locking (appears to work based on current setup) + +--- + +#### Option B: Adjust Task Scheduling + +Prevent database optimization and library scans from running simultaneously. + +**Current Schedule:** +- Library scan: Every 12 hours (04:26, 16:26) +- Database optimization: Every 6 hours (04:27, 10:28, 16:29) + +**Recommended Change:** +Modify Jellyfin's scheduled tasks via the web UI: +1. Go to Dashboard → Scheduled Tasks +2. Change "Optimize database" to run at different times: + - Change from every 6 hours to daily at 03:00 (before library scan) +3. This ensures optimization completes before library scan starts + +**Benefits:** +- Simple configuration change +- No code modifications needed +- Reduces overlap probability + +**Caveats:** +- Doesn't eliminate all race conditions +- API requests during optimization can still fail +- Less robust than WAL mode + +--- + +#### Option C: Increase SQLite Timeout + +Configure Jellyfin to wait longer when database is locked. + +**Implementation:** +Requires adding environment variable to Jellyfin deployment: + +```yaml +env: +- name: SQLITE_BUSY_TIMEOUT + value: "30000" # 30 seconds +``` + +**Note:** This may not be supported by Jellyfin directly. WAL mode is preferred. + +--- + +### Priority 2: Fix Audio Normalization + +#### Diagnosis Steps: + +1. **Check FFmpeg capabilities:** + +```bash +kubectl exec -n media deployment/jellyfin -- ffmpeg -filters 2>&1 | grep -E "(loudnorm|ebur128)" +``` + +2. **Test LUFS detection manually:** + +```bash +kubectl exec -n media deployment/jellyfin -- ffmpeg -i /music/sample.mp3 -af loudnorm=print_format=json -f null - 2>&1 +``` + +Expected output should include: +```json +{ + "input_i": "-23.0", + "input_tp": "-5.0", + "input_lra": "7.0", + "input_thresh": "-33.5", + "target_offset": "0.0" +} +``` + +#### Solution Options: + +**If filters are missing:** +- Jellyfin's FFmpeg build may lack audio normalization filters +- Solution: Disable audio normalization task in Jellyfin (Dashboard → Scheduled Tasks) + +**If filters exist but fail:** +- Some audio files may have incompatible formats +- Solution: Check task logs for specific files causing issues + +**To disable audio normalization:** +1. Go to Dashboard → Scheduled Tasks +2. Find "Audio Normalization" +3. Click on it and disable the task +4. This is acceptable - normalization is optional for most users + +--- + +### Priority 3: Clean Up Library Warnings + +#### Option A: Remove Empty Library Folders + +If you don't use ebooks or playlists: + +1. Go to Dashboard → Libraries +2. Remove the "ebooks" library if it exists +3. The playlists folder is auto-created and can be ignored + +#### Option B: Create the Folders + +If you plan to use these features: + +```bash +kubectl exec -n media deployment/jellyfin -- mkdir -p /ebooks +kubectl exec -n media deployment/jellyfin -- mkdir -p /config/data/playlists +``` + +--- + +## Implementation Plan + +### Step 1: Enable WAL Mode (RECOMMENDED) + +This will resolve the critical database locking issues. + +```bash +# Apply the WAL enable job +kubectl apply -f jellyfin-wal-enable.yaml + +# Wait for completion +kubectl wait --for=condition=complete --timeout=300s job/jellyfin-enable-wal -n media + +# Check logs +kubectl logs -n media job/jellyfin-enable-wal + +# Restart Jellyfin to ensure WAL mode is active +kubectl rollout restart deployment/jellyfin -n media +``` + +### Step 2: Disable Audio Normalization (OPTIONAL) + +Via Jellyfin Web UI: +1. Navigate to https://jellyfin.caffeinetux.com/web/index.html#!/scheduledtasks.html +2. Find "Audio Normalization" +3. Click "Disable" + +### Step 3: Monitor Logs + +After implementing WAL mode: + +```bash +# Watch for database lock errors (should disappear) +kubectl logs -n media deployment/jellyfin -f | grep "database is locked" + +# Check if WAL mode is active +kubectl exec -n media deployment/jellyfin -- sqlite3 /config/data/jellyfin.db "PRAGMA journal_mode;" +# Should output: wal +``` + +--- + +## Performance Impact Analysis + +### Current Issues: +- Database locks during library scans (2+ minutes) +- Failed API requests during optimization +- Metadata updates delayed or skipped + +### After WAL Mode: +- ✅ Concurrent reads during writes +- ✅ API requests succeed during optimization +- ✅ Faster metadata updates +- ✅ Better overall responsiveness + +### Trade-offs: +- Slightly more disk space (typically <10% of DB size) +- Additional files in config directory +- Minimal performance overhead (usually net positive) + +--- + +## Additional Recommendations + +### 1. Database Optimization Frequency + +Current: Every 6 hours may be excessive + +**Recommendation:** Change to once daily at 03:00 + +Benefits: +- Reduces I/O load on NFS +- Less frequent database locks +- Sufficient for most libraries + +### 2. Library Scan Optimization + +**Current behavior:** +- Full library scan every 12 hours +- Stops directory watching during scan + +**Recommendation:** +- Keep 12-hour schedule for now +- Monitor scan duration (currently 2-5 minutes) +- If scans take too long, consider: + - Increasing interval to 24 hours + - Using real-time monitoring only + +### 3. NFS Considerations + +Your Jellyfin database is on NFS. WAL mode works with NFS if: +- ✅ File locking is supported (appears to be working) +- ✅ NFS client supports mmap operations +- ⚠️ Network latency is low (check if you experience slowness) + +**If WAL mode causes issues on NFS:** +- Switch back to DELETE mode: `PRAGMA journal_mode=DELETE;` +- Consider moving database to local storage (emptyDir volume) +- Keep media on NFS, database local + +--- + +## Monitoring and Validation + +### Success Criteria: + +After implementing WAL mode, verify: + +1. **No more database lock errors:** +```bash +kubectl logs -n media deployment/jellyfin --since=24h | grep "database is locked" +# Should return no results +``` + +2. **WAL mode is active:** +```bash +kubectl exec -n media deployment/jellyfin -- sqlite3 /config/data/jellyfin.db "PRAGMA journal_mode;" +# Should output: wal +``` + +3. **Library scans complete successfully:** +```bash +kubectl logs -n media deployment/jellyfin | grep "Scan Media Library Completed" +# Should show successful completions +``` + +4. **Database optimization runs without conflicts:** +```bash +kubectl logs -n media deployment/jellyfin | grep "jellyfin.db optimized successfully" +# Should show successful optimizations +``` + +--- + +## Summary + +### Critical Issues (Fix Now): +1. **SQLite Database Locking** → Enable WAL mode + +### Optional Improvements: +2. **Audio Normalization** → Disable if not needed +3. **Library Warnings** → Remove unused libraries or create folders +4. **Task Scheduling** → Adjust optimization frequency to daily + +### Expected Outcome: +- Elimination of database lock errors +- Smoother library scans +- Better API responsiveness +- Cleaner logs + +--- + +## Resources + +- [SQLite WAL Mode Documentation](https://www.sqlite.org/wal.html) +- [Jellyfin Database Documentation](https://jellyfin.org/docs/general/administration/configuration.html) +- [SQLite Error Codes](https://www.sqlite.org/rescode.html) + +--- + +**Analysis Date:** 2025-11-22 +**Jellyfin Namespace:** media +**Database Location:** /config/data/jellyfin.db (NFS-backed PVC) diff --git a/NEON_VORTEX_BUTTON_FIX.md b/NEON_VORTEX_BUTTON_FIX.md new file mode 100644 index 0000000..93d0c4a --- /dev/null +++ b/NEON_VORTEX_BUTTON_FIX.md @@ -0,0 +1,289 @@ +# Neon Vortex Button Issue - Troubleshooting Guide + +## Problem +Buttons work in Godot editor but not working in the deployed web version at https://nv.caffeinetux.com + +## Most Likely Causes + +### 1. **CORS Headers Too Restrictive** +The current nginx configuration has very strict CORS headers: +```nginx +add_header Cross-Origin-Embedder-Policy "require-corp" always; +add_header Cross-Origin-Opener-Policy "same-origin" always; +``` + +These headers are required for SharedArrayBuffer (threads) but can break: +- Mouse/touch input +- Button click events +- External resources + +**Check if you need threads:** +- Look at your Godot export settings +- If "Export Type" is **Regular** (not Threads), these headers might be blocking input + +**Fix:** Update nginx.conf to conditionally apply headers: + +```nginx +# Only add CORS headers if using threads +# For Regular (non-threaded) exports, comment these out: +# add_header Cross-Origin-Embedder-Policy "require-corp" always; +# add_header Cross-Origin-Opener-Policy "same-origin" always; +``` + +### 2. **JavaScript Console Errors** + +**How to Check:** +1. Open https://nv.caffeinetux.com in browser +2. Press F12 (Developer Tools) +3. Go to Console tab +4. Click a button and look for errors + +**Common Errors:** + +#### Error: "SharedArrayBuffer is not defined" +- **Cause:** Missing CORS headers or not using HTTPS +- **Fix:** Keep the CORS headers, ensure HTTPS is working ✅ (you have this) + +#### Error: "Failed to fetch" or "CORS policy" +- **Cause:** CORS headers too restrictive or missing resources +- **Fix:** Check Network tab for failed requests + +#### Error: "WebGL context lost" +- **Cause:** GPU issues or resource exhaustion +- **Fix:** Reduce graphics quality in Godot, add memory limits + +#### Error: "Input blocked" +- **Cause:** Browser security policy or iframe restrictions +- **Fix:** Ensure X-Frame-Options allows the domain + +### 3. **Input Method Not Exported** + +**In Godot, check your button scripts:** + +```gdscript +# ✅ CORRECT - Web-compatible input +func _on_Button_pressed(): + print("Button clicked!") + # Your code here + +# ❌ WRONG - May not work on web +func _input(event): + if event is InputEventMouseButton: + # This might not work in some web configs +``` + +**Fix:** Use signal connections, not `_input()` or `_unhandled_input()` for buttons. + +### 4. **Export Settings Issue** + +**Check in Godot:** +1. Go to **Project → Export → HTML5** +2. Verify: + - ✅ **Export Type:** Regular (unless you need threads) + - ✅ **Custom HTML Shell:** Not set (use default) + - ✅ **Head Include:** Empty or minimal + - ✅ **Progressive Web App:** OFF (for debugging) + +**Re-export:** +1. Delete the old export +2. Export fresh +3. Test locally first: `python3 -m http.server 8000` +4. If buttons work locally, issue is deployment-related + +### 5. **Browser Cache** + +**Your browser might be caching old broken version:** + +**Fix:** +1. Hard refresh: `Ctrl + Shift + R` (or `Cmd + Shift + R` on Mac) +2. Clear cache for the site +3. Or use Incognito/Private browsing + +### 6. **Service Worker Blocking** + +The game has a service worker (`Neon Vortex.service.worker.js`) for offline support. +If corrupted, it can block updates. + +**Fix:** +1. Open DevTools (F12) +2. Go to **Application** tab +3. Click **Service Workers** +4. Click **Unregister** for nv.caffeinetux.com +5. Hard refresh the page + +## Recommended Debugging Steps + +### Step 1: Check Browser Console +``` +1. Open https://nv.caffeinetux.com +2. Press F12 +3. Go to Console tab +4. Try clicking a button +5. Note any errors +``` + +### Step 2: Test Without CORS Headers +Temporarily modify nginx.conf: + +```nginx +server { + listen 8080; + server_name _; + + root /usr/share/nginx/html; + index "Neon Vortex.html"; + + # TEMPORARILY COMMENT OUT for testing: + # add_header Cross-Origin-Embedder-Policy "require-corp" always; + # add_header Cross-Origin-Opener-Policy "same-origin" always; + + # Keep these: + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + location / { + try_files $uri $uri/ "/Neon Vortex.html"; + } +} +``` + +**Rebuild and test:** +```bash +# Commit changes +git add htlm/nginx.conf +git commit -m "Test: Remove strict CORS headers" +git push + +# Rebuild image (will happen via Flux) +# Wait for deployment +# Test buttons +``` + +### Step 3: Check Network Tab +1. Open DevTools (F12) +2. Go to **Network** tab +3. Reload page +4. Look for: + - ❌ Failed requests (red) + - ❌ 404 errors for required files + - ✅ All .js, .wasm, .pck files loaded successfully + +### Step 4: Test Locally +```bash +# Navigate to exported game folder +cd htlm/ + +# Start local server +python3 -m http.server 8000 + +# Open browser to http://localhost:8000 +# Test if buttons work +``` + +**If buttons work locally but not deployed:** +- Issue is deployment-related (nginx config, CORS, headers) + +**If buttons don't work locally:** +- Issue is with Godot export or game code + +## Most Likely Fix + +Based on your current setup, **most likely cause is CORS headers**. + +### Quick Fix: + +**htlm/nginx.conf** - Update to: + +```nginx +server { + listen 8080; + server_name _; + + root /usr/share/nginx/html; + index "Neon Vortex.html"; + + # Enable gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/wasm; + gzip_min_length 1000; + + # MIME types for WASM and other assets + types { + application/wasm wasm; + application/javascript js; + text/html html; + application/json json; + image/png png; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # ONLY ADD THESE IF YOUR GAME USES THREADS: + # If your Godot export is "Regular" (not "Threads"), comment these out: + # add_header Cross-Origin-Embedder-Policy "require-corp" always; + # add_header Cross-Origin-Opener-Policy "same-origin" always; + + location / { + try_files $uri $uri/ "/Neon Vortex.html"; + } + + # Cache static assets + location ~* \.(png|jpg|jpeg|gif|ico|wasm|js|css)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} +``` + +## Godot-Specific Button Fixes + +### Check Your Button Code: + +**✅ CORRECT - Signal-based (Web-safe):** +```gdscript +extends Button + +func _ready(): + connect("pressed", self, "_on_button_pressed") + +func _on_button_pressed(): + print("Button clicked!") + get_tree().change_scene("res://next_scene.tscn") +``` + +**✅ CORRECT - Using Godot Editor signals:** +``` +1. Select button in scene tree +2. Go to Node tab (right side) +3. Double-click "pressed()" signal +4. Connect to script function +5. Implement the function +``` + +**❌ AVOID - Input polling (may not work on web):** +```gdscript +func _process(delta): + if Input.is_action_just_pressed("ui_accept"): + # This might not work reliably on web +``` + +### Button Properties to Check: +``` +✅ Mouse Filter: Stop (default) +✅ Focus Mode: All or Click +✅ Disabled: false +✅ Visible: true +``` + +## Next Steps + +1. **Check browser console** - This will tell you the exact error +2. **Test without CORS headers** - Comment them out temporarily +3. **Test locally** - See if it's deployment or game issue +4. **Share console errors** - If still broken, share the browser console output + +The most common cause is the CORS headers blocking input, so start there! diff --git a/htlm/nginx.conf b/htlm/nginx.conf index afb9310..49a137f 100644 --- a/htlm/nginx.conf +++ b/htlm/nginx.conf @@ -25,8 +25,11 @@ server { add_header X-XSS-Protection "1; mode=block" always; # CORS headers for web workers and WASM - add_header Cross-Origin-Embedder-Policy "require-corp" always; - add_header Cross-Origin-Opener-Policy "same-origin" always; + # ONLY NEEDED IF GODOT EXPORT USES THREADS MODE + # These headers can block input if export is "Regular" (non-threaded) + # Commenting out to fix button input issues + # add_header Cross-Origin-Embedder-Policy "require-corp" always; + # add_header Cross-Origin-Opener-Policy "same-origin" always; location / { try_files $uri $uri/ "/Neon Vortex.html"; diff --git a/jellyfin-wal-enable.yaml b/jellyfin-wal-enable.yaml new file mode 100644 index 0000000..6729423 --- /dev/null +++ b/jellyfin-wal-enable.yaml @@ -0,0 +1,30 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: jellyfin-enable-wal + namespace: media +spec: + ttlSecondsAfterFinished: 600 + template: + spec: + restartPolicy: Never + containers: + - name: enable-wal + image: alpine:latest + command: + - sh + - -c + - | + apk add --no-cache sqlite + echo "Enabling WAL mode on jellyfin.db..." + sqlite3 /config/data/jellyfin.db "PRAGMA journal_mode=WAL;" + echo "Verifying WAL mode..." + sqlite3 /config/data/jellyfin.db "PRAGMA journal_mode;" + echo "WAL mode enabled successfully!" + volumeMounts: + - name: jellyfin-data + mountPath: /config + volumes: + - name: jellyfin-data + persistentVolumeClaim: + claimName: jellyfin-config