feat: support audio & vidéo — lecteurs HTML5 intégrés #109
CI / lint (push) Successful in 1m58s
CI / security (push) Successful in 1m22s
CI / test (push) Successful in 4m32s
CI / build (push) Successful in 2m6s
CI / e2e (push) Successful in 12m57s

This commit is contained in:
2026-09-23 09:14:34 -04:00
parent eccbf7474e
commit e2417cb5ab
26 changed files with 1057 additions and 121 deletions
+4 -58
View File
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
> **Version :** 2.17.0 | **Dernière mise à jour :** 2026-09-23
> **Version :** 2.18.0 | **Dernière mise à jour :** 2026-09-23
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
> vers les fonctionnalités livrées.
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
@@ -80,60 +80,6 @@
---
## ⚪ Backlog — Média (images, audio, vidéo)
### 109. Support audio & vidéo — lecteurs intégrés HTML5
- **Effort :** 2-3 jours | **Impact :** 🟡 | **Zone :** backend (media streaming) + frontend (viewer)
- **Statut :** ⚪ Prévu — s'appuie sur le socle `media_types.py` livré par #108
- **Description :** prise en charge des fichiers audio (`.mp3 .m4a .aac .wav .ogg .oga .opus .flac`) et vidéo (`.mp4 .webm .mov .m4v`) avec la même parité que les autres fichiers : apparition dans l'arborescence, indexation du nom, lecture directe dans le viewer via les balises HTML5 `<audio>` / `<video>`. Actuellement ces fichiers tombent dans le chemin binaire « Ce fichier est binaire et ne peut pas être affiché » + bouton download (`frontend/js/viewer.js:622`).
- **Choix technique — lecteurs HTML5 natifs, pas de transcodage :**
- `<video>`/`<audio>` sont natifs partout (Chrome, Firefox, Edge, Safari, mobile) : play, scrub, volume, vitesse 0,25–2×, PiP, plein écran — sans une ligne de JS de contrôle.
- **Zéro dépendance lourde** : pas de ffmpeg dans l'image Docker, pas de file de transcodage. Les formats web-natifs (MP4/H.264, WebM, MP3, AAC, Opus, FLAC, WAV) couvrent l'immense majorité des notes vocales et screencasts.
- Le vrai travail serveur est le **support HTTP `Range` / 206 Partial Content** : sans lui, pas de scrub (le navigateur retélécharge tout pour se positionner) et Safari refuse le MP4. Bonne nouvelle : `pdf/stream` (`backend/main.py:2617`) **implémente déjà les Range** (206, `Content-Range`, 416) — extraire cette logique en helper `stream_file_with_range(file_path, request, media_type)` et s'en servir pour les trois médias.
- Métadonnées (durée, résolution) lues **côté client** via `loadedmetadata` (`duration`, `videoWidth/videoHeight`) — aucun ffprobe serveur nécessaire.
- **Sous-tâches :**
##### A. Backend — socle média partagé (1 j) — bénéficie aussi à #74/#108
- [ ] **A1.** Ajouter `AUDIO_EXTENSIONS` / `VIDEO_EXTENSIONS` dans `backend/media_types.py` (#108-A1) et les intégrer à `SUPPORTED_EXTENSIONS` avec la branche « contenu non parsé » (#108-A2).
- [ ] **A2.** Extraire le helper Range de `pdf/stream` en fonction réutilisable ; nouvel endpoint `GET /api/media/{vault}?path=…` : streaming par chunks 64 ko (`aiofiles`), 206 + `Content-Range` + `Accept-Ranges`, 416 sur Range invalide, MIME via `mimetypes` + map de secours (`.m4a→audio/mp4`, `.opus→audio/ogg`, `.mov→video/quicktime`, `.m4v→video/mp4`).
- [ ] **A3.** Gardes-fous : `_resolve_safe_path`, `check_vault_access`, taille inline max `OBSIGATE_MEDIA_MAX_INLINE_MB` (défaut 500 — au-delà, UI download).
- [ ] **A4.** `api_file_view()` : branches AVANT le `read_text()` → `is_audio: true` / `is_video: true` (+ `size_bytes`, `stream_url` vers `/api/media`). `pdf/stream` bascule sur le helper A2 (comportement inchangé, tests de régression).
##### B. Frontend — lecteur audio (0,5 j)
- [ ] **B1.** Dispatch `data.is_audio` dans `viewer.js` : `<audio controls src=stream_url>` pleine largeur, artwork placeholder (icône Lucide `music`), titre + voûte + taille dans la toolbar, durée formatée via `loadedmetadata`.
- [ ] **B2.** `EXT_ICONS` : audio → Lucide `audio-lines` ; vidéo → `video`.
- [ ] **B3.** Pause au changement d'onglet (comportement fichier standard, cohérence #75) — pas de lecture persistante en v1.
##### C. Frontend — lecteur vidéo (0,5 j)
- [ ] **C1.** Dispatch `data.is_video` : `<video controls playsinline>` centré, `max-height: calc(100vh - header)`, fond noir, letterboxing.
- [ ] **C2.** Vérifier le bouton pop-out avec médias (médias same-origin, `X-Frame-Options: SAMEORIGIN` compatible).
- [ ] **C3.** Codec non supporté (`.mkv`, `.avi`, HEVC…) : sur l'événement `error` de l'élément média → remplacer le lecteur par l'UI binaire existante + message « Ce format ne peut pas être lu dans votre navigateur » + Télécharger / Ouvrir dans un nouvel onglet (le player OS prend le relais).
##### D. Recherche & intégrations (0,5 j)
- [ ] **D1.** Filtres `ext:mp3`, `ext:mp4`, etc. (mécanisme #108-A4).
- [ ] **D2.** Fichiers récents (#35) / dashboards : exclure les médias des previews de contenu (aucun texte).
- [ ] **D3.** BooksLM (#76) : exclure les médias du contexte collecté (pas de texte extractible).
- [ ] **D4.** (Hors scope, noter la porte) Transcription audio via Whisper sur le provider AI existant.
##### E. Mobile & PWA (0,25 j)
- [ ] **E1.** Tester le scrub sur iOS Safari (exige un Range strictement correct) et Android Chrome.
- [ ] **E2.** Service worker (#59) : exclure les médias du cache offline (taille), stratégie Network First inchangée pour le reste.
##### F. Tests (0,5 j)
- [ ] **F1.** `test_media_stream.py` : `bytes=0-1023` → 206 + Content-Range + 1024 octets ; Range hors borne → 416 ; sans Range → 200 + Accept-Ranges ; path traversal refusé ; voûte sans accès refusée. Régression : `pdf/stream` inchangé après extraction du helper.
- [ ] **F2.** `test_media_indexing.py` : un `.mp3` apparaît dans l'arborescence, indexé sans contenu, watcher sans erreur.
- [ ] **F3.** Playwright : fixtures binaires minimaux commités dans `tests/fixtures/` (mini `.mp3` sine wave ~2 ko, mini `.mp4`) → `<audio>` et `<video>` rendus.
- **Points d'attention / risques**
- **Range est LE prérequis** (sous-tâche la plus testée, pas une option) : sans 206 correct, Safari casse et le scrub hache la bande passante.
- **Formats hors web-natifs** (`.mkv`, `.avi`, HEVC, AC-4) : non lisibles sans transcodage (ffmpeg hors scope) — le fallback C3 les rend téléchargeables proprement au lieu de silencieusement cassés.
- **Poids disque** : une vidéo de 1 Go dans un vault ralentit les backups (#17, gzip) — envisager d'exclure les gros médias de la compression gzip.
- **Sous uvicorn (worker unique)** : lectures vidéo simultanées = file descriptors + bande passante ; surveiller via le health check enrichi (#68).
- **Hors scope v1** : HLS, sous-titres (`<track src=.vtt>`), thumbnails vidéo (première frame) — extensions naturelles documentées ici.
---
## ⚪ Backlog — Sécurité, architecture & performance (P0/P1)
### 84. Consolidation & sécurité — revue statique 2026-09-13 (phase 1)
@@ -237,6 +183,7 @@
| 107 | Configuration — Gestion des clés API & MCP : création/révocation de jetons longue durée (1 j, 1 mois, 6 mois, 1 an, sans fin), une seule clé pour l'API REST et le serveur MCP, « dernière utilisation », store `data/api_tokens.json` sans secret persisté | 2.15.0 | [features/api-mcp-tokens-107.md](./features/api-mcp-tokens-107.md) |
| 86 | Optimisation globale des performances (phase 3) — scan différentiel, excalidraw différé, garde-fou `replace` (inverted index / PDF lazy / caps regex déjà livrés via BUG-033/040/025) | 2.16.0 | [features/perf-phase3-86.md](./features/perf-phase3-86.md) |
| 108 | Support complet des images — arborescence, visionneuse (zoom/pan/navigation/miniatures), indexation nom+métadonnées, `media_types.py`, filtre `ext:`, SVG sandbox | 2.17.0 | [features/image-support.md](./features/image-support.md) |
| 109 | Support audio & vidéo — lecteurs HTML5 intégrés, streaming HTTP Range (`/api/media`), fallback codec/taille | 2.18.0 | [features/media-viewers-109.md](./features/media-viewers-109.md) |
---
@@ -244,12 +191,11 @@
| Priorité | Items | Effort total estimé |
|---|---|---|
| ✅ Complété | #1 → #59, #61–72, #74–76, #78–84, #86, #88–93, #94–100, #102–108, #92 | ~123 jours réalisés |
| ✅ Complété | #1 → #59, #61–72, #74–76, #78–84, #86, #88–93, #94–100, #102–109, #92 | ~125 jours réalisés |
| 🔵 P2 restant | #77 Desktop : signature de code (non retenue), 6 tests E2E **manuels** ([protocole](./DESKTOP_E2E_CHECKLIST.md)) | ~0,5-1 jour |
| ⚪ Média | #109 Audio/vidéo (lecteurs HTML5, Range) — s'appuie sur le socle média de #108 | 2-3 jours |
| ⚪ P4 restant | #73 Sync (6-8j) | 6-8 jours |
| ⚪ P0/P1 restant | #85, #87 Refonte architecturale, CI/CD (BUG-035 → BUG-040 corrigés, #86 livré) | ~11-17 jours |
| **Total restant** | **7 items + finitions** | **~25-40 jours** |
| **Total restant** | **6 items + finitions** | **~23-38 jours** |
---