✨ webrenderer architecture evolution (Phases P0–P4)
- Phase 1: Introduce DeviceCommand enum and BrowserAdapter in adapter.rs - Fix P0 bug (play_handler now checks URI before state change) - Phase 2: Wire flac_handle.pause/resume into pause_handler/stop/play - Add VecDeque<DeviceCommand> to RendererState, replace Option<Value> - Phase 3: Add AudioContext + exponential backoff reconnect in PMOPlayer.ts - Fix position format (seconds_to_upnp_time) and add /nowplaying, /state endpoints - Phase 4: Register new HTTP routes in config.rs and implement handlers
This commit is contained in:
@@ -781,3 +781,129 @@ cargo check -p pmowebrenderer --features pmoserver
|
||||
# 7. GET /api/webrenderer/{id}/nowplaying : JSON valide avec HH:MM:SS
|
||||
# 8. GET /api/webrenderer/{id}/state : JSON valide avec tous les champs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rapport d'exécution (2026-04-05)
|
||||
|
||||
### Ce qui a été réalisé
|
||||
|
||||
| Phase | Statut | Notes |
|
||||
|-------|--------|-------|
|
||||
| **Phase 0** — Bug P0 (`play_handler` sans URI) | ✅ Complet | Early return avant tout changement d'état |
|
||||
| **Phase 1.1** — `DeviceCommand` enum | ✅ Complet | Dans `src/adapter.rs` (pas encore `core/`) |
|
||||
| **Phase 1.2** — Trait `DeviceAdapter` | ✅ Complet | Dans `src/adapter.rs` |
|
||||
| **Phase 1.3** — `VecDeque<DeviceCommand>` dans `RendererState` | ✅ Complet | `push_command`/`pop_command` ok |
|
||||
| **Phase 1.4** — `BrowserAdapter` | ✅ Complet | Dans `src/adapter.rs` (pas encore `browser/`) |
|
||||
| **Phase 1.5** — `adapter` dans `WebRendererInstance` | ❌ Non fait | Pas de champ `adapter` dans la struct |
|
||||
| **Phase 1.6** — Nettoyage méthodes browser dans `RendererRegistry` | ❌ Non fait | `set_player_command`, `get_pending_command`, `has_current_uri`, `send_play_command`, `send_pause_command` toujours présents |
|
||||
| **Phase 2.1** — `flac_handle` dans `PipelineHandle` | ✅ Complet | Exposé dans `PipelineHandle` |
|
||||
| **Phase 2.2** — `pause_handler` appelle `flac_handle.pause()` | ✅ Complet | |
|
||||
| **Phase 2.3** — `stop_handler` envoie `Flush + Stop` au device | ⚠️ Partiel | Appelle `flac_handle.pause()` mais n'envoie **pas** `Flush`/`Stop` via adapter (adapter non câblé) |
|
||||
| **Phase 2.4** — `run_event_listener` avec `Weak<dyn DeviceAdapter>`, `Flush` sur `TrackEnded` | ❌ Non fait | Pas de `Weak<DeviceAdapter>`, pas de `Flush` envoyé au browser sur fin de piste |
|
||||
| **Phase 2.5** — `play_handler` appelle `flac_handle.resume()` | ✅ Complet | |
|
||||
| **Phase 2.6** — `build_avtransport` avec paramètre `adapter` | ❌ Non fait | Signature inchangée |
|
||||
| **Phase 3.1** — `AudioContext` dans `PMOPlayer.ts` | ✅ Complet | `ensureAudioContext()`, `ac?.suspend()` dans `flush()` |
|
||||
| **Phase 3.2** — Auto-reconnect avec backoff exponentiel | ✅ Complet | `scheduleReconnect()`, 5 tentatives max |
|
||||
| **Phase 3.3** — Unification format position (`seconds_to_upnp_time`) | ✅ Complet | `update_player_state` corrigé |
|
||||
| **Phase 4.1** — `GET /{id}/nowplaying` | ✅ Complet | Dans `register.rs` |
|
||||
| **Phase 4.2** — `GET /{id}/state` | ✅ Complet | Dans `register.rs` |
|
||||
| **Phase 4.3** — Routes enregistrées dans `config.rs` | ✅ Complet | |
|
||||
| **Phase 5** — Restructuration `core/` vs `browser/` | ⏸️ Différé | Décision explicite |
|
||||
|
||||
---
|
||||
|
||||
## Tâches restantes
|
||||
|
||||
### T1 — Câbler `adapter` dans `WebRendererInstance` et handlers (Phase 1.5 + 2.6)
|
||||
|
||||
**Problème** : le `BrowserAdapter` est implémenté mais jamais instancié ni utilisé.
|
||||
Les handlers `stop_handler` et `pause_handler` appellent `flac_handle.pause()` mais n'envoient
|
||||
pas les commandes `Flush`/`Stop`/`Pause` au browser via l'adapter.
|
||||
|
||||
**Fichiers** : `registry.rs`, `renderer.rs`, `handlers.rs`
|
||||
|
||||
**Étapes** :
|
||||
|
||||
1. Dans `WebRendererInstance` (`registry.rs`), ajouter le champ :
|
||||
```rust
|
||||
pub adapter: Arc<dyn crate::adapter::DeviceAdapter>,
|
||||
```
|
||||
|
||||
2. Dans `create_instance()` (`registry.rs`), construire le `BrowserAdapter` avant la factory :
|
||||
```rust
|
||||
let adapter: Arc<dyn crate::adapter::DeviceAdapter> =
|
||||
Arc::new(crate::adapter::BrowserAdapter { state: state.clone() });
|
||||
// Passer à la factory, stocker dans WebRendererInstance
|
||||
```
|
||||
|
||||
3. Mettre à jour `WebRendererFactory::create_device_with_pipeline()` et `build_avtransport()`
|
||||
pour accepter `adapter: Arc<dyn DeviceAdapter>` et le passer aux handlers `pause_handler`,
|
||||
`stop_handler`, `play_handler`.
|
||||
|
||||
4. Dans `pause_handler` : ajouter `adapter.deliver(DeviceCommand::Pause)`.
|
||||
|
||||
5. Dans `stop_handler` : ajouter `adapter.deliver(DeviceCommand::Flush)` puis
|
||||
`adapter.deliver(DeviceCommand::Stop)`.
|
||||
|
||||
---
|
||||
|
||||
### T2 — `Flush` sur `TrackEnded` dans `run_event_listener` (Phase 2.4)
|
||||
|
||||
**Problème** : lors d'un changement de piste automatique, le browser a plusieurs secondes
|
||||
d'audio bufférisé. Sans commande `Flush`, la transition de piste a un délai de 3–5 secondes.
|
||||
|
||||
**Fichiers** : `pipeline.rs`
|
||||
|
||||
**Étapes** :
|
||||
|
||||
1. Ajouter `adapter: std::sync::Weak<dyn crate::adapter::DeviceAdapter>` à la signature de
|
||||
`run_event_listener` et à l'appel dans `InstancePipeline::start()`.
|
||||
|
||||
2. Dans le bras `PlayerEvent::TrackEnded` :
|
||||
```rust
|
||||
if let Some(adapter) = adapter.upgrade() {
|
||||
adapter.deliver(crate::adapter::DeviceCommand::Flush);
|
||||
}
|
||||
```
|
||||
|
||||
3. Dans `InstancePipeline::start()`, passer `Arc::downgrade(&instance_adapter)` — nécessite
|
||||
que T1 soit terminé (adapter créé avant `start()`).
|
||||
|
||||
**Précaution** : utiliser `Weak` pour éviter le cycle de référence
|
||||
`WebRendererInstance → pipeline → event_listener → WebRendererInstance`.
|
||||
|
||||
---
|
||||
|
||||
### T3 — Nettoyer `RendererRegistry` des méthodes browser-spécifiques (Phase 1.6)
|
||||
|
||||
**Problème** : `set_player_command`, `get_pending_command`, `has_current_uri`,
|
||||
`send_play_command`, `send_pause_command` sont des fuites d'abstraction browser dans le registre
|
||||
générique. Tout futur adaptateur (Android Auto…) devrait contourner ou dupliquer ces méthodes.
|
||||
|
||||
**Fichiers** : `registry.rs`, `register.rs`
|
||||
|
||||
**Condition préalable** : T1 terminé (l'adapter est accessible via `get_instance()`).
|
||||
|
||||
**Étapes** :
|
||||
|
||||
1. Ajouter `get_instance(&self, instance_id: &str) -> Option<Arc<WebRendererInstance>>`
|
||||
dans `RendererRegistry` (accès générique, remplace les méthodes spécialisées).
|
||||
|
||||
2. Déplacer dans `register.rs` la logique actuellement dans les méthodes à supprimer :
|
||||
- `get_pending_command` : `state.write().pop_command()` + sérialisation JSON → déjà fait dans `command_handler`
|
||||
- `set_player_command` : remplacé par `instance.adapter.deliver(cmd)`
|
||||
- `has_current_uri` : inline dans `play_handler` HTTP
|
||||
- `send_play_command` / `send_pause_command` : accès direct au pipeline via `get_instance`
|
||||
|
||||
3. Supprimer les 5 méthodes de `RendererRegistry`.
|
||||
|
||||
4. `cargo check -p pmowebrenderer` après chaque suppression.
|
||||
|
||||
---
|
||||
|
||||
### T4 — Phase 5 : Restructuration `core/` vs `browser/` (différé)
|
||||
|
||||
À faire une fois T1–T3 terminés et les interfaces stabilisées.
|
||||
Voir la section "Phase 5" du plan ci-dessus pour l'ordre de déplacement.
|
||||
Condition : `cargo check -p pmowebrenderer` doit passer à chaque étape.
|
||||
|
||||
Reference in New Issue
Block a user