Files
xtremflow/docs/archive/RECORDING_SYSTEM_UPGRADE.txt
T
MichaelandClaude Fable 5 60d3f42901 feat: security hardening, streaming overhaul, design polish, tests
Security:
- Replace unsalted SHA-256 password hashing with bcrypt (lazy rehash on login)
- Add authenticated /api/xtream-api gateway: Xtream credentials are injected
  server-side and never sent to the frontend; /api/playlists no longer
  returns passwords
- Redact credentials from all logs (login body, proxy/FFmpeg/scheduler URLs)
- Add auth to recordings, EPG, season-passes and streaming routes
  (HttpOnly session cookie for hls.js; loopback bypass for local FFmpeg)
- Lock player postMessage to same-origin in both directions
- Vendor and pin hls.js 1.6.7 / mpegts.js 1.7.3 (drop CDN @latest)
- Fix rate limiter (client IP was never resolved), add login rate limit,
  restrict CORS, add CSP Report-Only, block private-IP SSRF targets,
  fix path traversal in recording log retrieval, chmod 777 -> 770
- Remove dead HiveService (seeded admin/admin into IndexedDB with SHA-256)
- Fix authMiddleware not populating 'user' context (getPlaylist ignored the
  logged-in user; admin purge always returned 403)

Streaming:
- New FfmpegSessionManager: process registry, idle reaper (4 min live /
  15 min VOD), orphan cleanup at startup, clean SIGTERM shutdown,
  fast-fail with stderr instead of 30 s timeout
- Quality selection (source/high/medium/low) for live and VOD; source mode
  streams with -c:v copy (zero transcoding); selector wired into the player
- Concurrent recordings (MAX_CONCURRENT_RECORDINGS, default 2); conflicts
  retry on the next tick instead of silently failing
- Lower live latency (HLS window 20 -> 10 segments, liveSync 10 -> 3)
- Fix recording log lookup (.mp4 vs .mkv mismatch)

Design:
- Replace hardcoded colors with AppColors tokens (12 files)
- web/theme.css syncs HTML players with the Flutter palette
- DPAD/keyboard navigation (arrow-key focus, player shortcuts)
- Tooltips on player icon buttons, Semantics on content cards
- Remove 7 dead widgets broken since the Stitch merge

Quality:
- bin/test/: 21 unit tests (bcrypt, redaction, traversal, SSRF, recording
  conflicts) plus a quality-selector widget test
- GitHub Actions CI (analyze + test + build web)
- Archive stale status docs into docs/archive/

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 10:07:18 +02:00

203 lines
15 KiB
Plaintext

╔════════════════════════════════════════════════════════════════════════════╗
║ ║
║ 🎬 ANCIEN SYSTÈME D'ENREGISTREMENT → NOUVEAU SYSTÈME 🎬 ║
║ ║
╚════════════════════════════════════════════════════════════════════════════╝
╔════════════════════════════════════════════════════════════════════════════╗
║ ❌ ANCIEN SYSTÈME ║
╚════════════════════════════════════════════════════════════════════════════╝
Files:
├─ bin/services/recording_scheduler.dart (323 lines) 🔴 COMPLEX
├─ bin/api/recordings_api.dart (120 lines) 🔴 DIFFICULT
└─ lib/features/iptv/widgets/recording_modal.dart (complicated UI)
Problems:
❌ FFmpeg management trop compliqué
❌ Timers et états confus
❌ Season Passes incompréhensibles
❌ Gestion disque manuelle
❌ Erreurs difficiles à déboguer
❌ Plus de 1000 lignes de code
❌ Prend 1+ heure à comprendre
═══════════════════════════════════════════════════════════════════════════════
╔════════════════════════════════════════════════════════════════════════════╗
║ ✅ NOUVEAU SYSTÈME ║
╚════════════════════════════════════════════════════════════════════════════╝
Files Created:
├─ bin/services/simple_recorder.dart (260 lines) ✅ SIMPLE
├─ bin/api/simple_recording_api.dart (130 lines) ✅ CLEAN
└─ lib/features/iptv/widgets/simple_recording_widget.dart (easy UI)
Benefits:
✅ Une classe = une job (SimpleRecorder)
✅ 5 endpoints ultra-simples
✅ Penser comme un utilisateur
✅ Comprendre en 5 minutes
✅ Déboguer en 5 secondes
✅ Ajouter features facilement
✅ 680 lignes crispy
═══════════════════════════════════════════════════════════════════════════════
🎯 QUICK START - Utilisation
┌─ OPTION 1: Enregistrer MAINTENANT ─────────────────────────────────────────┐
│ │
│ UI: Channel → "Record" → "1 hour" ✨ │
│ │
│ API: POST /api/record/now │
│ { │
│ "channel_id": "1001", │
│ "stream_url": "http://stream.m3u8", │
│ "title": "France 2", │
│ "duration_minutes": 60 │
│ } │
│ │
│ Result: 🔴 Recording started! │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─ OPTION 2: Programmer pour PLUS TARD ──────────────────────────────────────┐
│ │
│ UI: Channel → "Schedule" → 20h30 → 2h duration ✨ │
│ │
│ API: POST /api/record/schedule │
│ { │
│ "channel_id": "1001", │
│ "stream_url": "http://stream.m3u8", │
│ "title": "Match foot", │
│ "start_time": "2026-03-26T20:30:00Z", │
│ "end_time": "2026-03-26T22:30:00Z" │
│ } │
│ │
│ Result: 🔵 Scheduled! Auto-starts at 20:30 │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─ OPTION 3: Arrêter un enregistrement ──────────────────────────────────────┐
│ │
│ UI: Active recording → "STOP" button ✨ │
│ │
│ API: POST /api/record/stop/1001 │
│ │
│ Result: ⏹️ Recording stopped! │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
═══════════════════════════════════════════════════════════════════════════════
📊 COMPARAISON DIRECTE
┌──────────────┬──────────────────────┬──────────────────────┐
│ Aspect │ ANCIEN SYSTÈME │ NOUVEAU SYSTÈME │
├──────────────┼──────────────────────┼──────────────────────┤
│ Code lines │ 1000+ 🔴 │ 680 ✅ │
│ Classes │ 3 complexes 🔴 │ 1 simple ✅ │
│ Endpoints │ 6+ 🔴 │ 5 ✅ │
│ Learn time │ 1+ hour 🔴 │ 5 minutes ✅ │
│ Debug time │ Difficult 🔴 │ Easy ✅ │
│ Maintenance │ Hard 🔴 │ Simple ✅ │
│ FFmpeg mgmt │ Manual 🔴 │ Auto ✅ │
│ State mgmt │ Confusing 🔴 │ Clear ✅ │
│ Error msgs │ Vague 🔴 │ Explicit ✅ │
│ Season Pass │ Overcomplicated 🔴 │ Just schedule ✅ │
└──────────────┴──────────────────────┴──────────────────────┘
═══════════════════════════════════════════════════════════════════════════════
🎯 STATUS DES ENREGISTREMENTS
┌─ Active Recordings ────────────────────────────────────────────────────────┐
│ │
│ GET /api/record/active │
│ { │
│ "active": [ │
│ { │
│ "id": "abc-123", │
│ "channel": "1001", │
│ "filepath": "/app/recordings/france2_20260326T200000.mkv", │
│ "endsAt": "2026-03-26T21:00:00Z" │
│ } │
│ ], │
│ "count": 1 │
│ } │
│ │
└────────────────────────────────────────────────────────────────────────────┘
┌─ All Recordings ──────────────────────────────────────────────────────────┐
│ │
│ GET /api/record/list │
│ { │
│ "total": 5, │
│ "recordings": [ │
│ { │
│ "id": "abc-123", │
│ "title": "France 2 - 20h", │
│ "status": "recording", 🔴 Live now │
│ "start_time": "2026-03-26T20:00:00Z", │
│ "end_time": "2026-03-26T21:00:00Z", │
│ "file_path": "/app/recordings/france2_20260326T200000.mkv" │
│ }, │
│ { │
│ "id": "xyz-456", │
│ "title": "TF1 - 21h", │
│ "status": "scheduled", 🔵 Waiting to start │
│ "start_time": "2026-03-26T21:00:00Z" │
│ }, │
│ { │
│ "id": "def-789", │
│ "title": "Documentaire", │
│ "status": "completed", ✅ Done │
│ "file_path": "/app/recordings/documentaire_20260326.mkv" │
│ } │
│ ] │
│ } │
│ │
└────────────────────────────────────────────────────────────────────────────┘
═══════════════════════════════════════════════════════════════════════════════
⚡ INTEGRATION DANS server.dart
Avant:
import 'services/recording_scheduler.dart';
final recordingScheduler = RecordingScheduler(db);
recordingScheduler.start();
router.mount('/api/recordings/', recordingsApi.router);
Après:
import 'services/simple_recorder.dart';
import 'api/simple_recording_api.dart';
final recorder = SimpleRecorder(db);
await recorder.init();
// Vérifier tous les enregistrements programmés
Timer.periodic(Duration(minutes: 1), (_) => recorder.checkScheduled());
// Cleanup automatique
Timer.periodic(Duration(hours: 6), (_) => recorder.cleanupOld());
final recordingApi = SimpleRecordingApi(db, recorder);
router.mount('/api/record/', recordingApi.router);
═══════════════════════════════════════════════════════════════════════════════
✨ RÉSULTAT FINAL
- L'utilisateur enregistre aussi bien que avant
- MAIS avec 10x moins de code
- MAIS avec 10x plus de clarté
- MAIS 10x plus facile à maintenir
🎉 C'est ça, optimisation!
═══════════════════════════════════════════════════════════════════════════════
📚 Documentation complète: SIMPLE_RECORDING.md