Eh bien, je ne sais plus à quoi ça correspond.

This commit is contained in:
2025-12-03 21:34:23 +01:00
parent 72bf73ef10
commit 23705c9c21
6 changed files with 165 additions and 2 deletions

1
Cargo.lock generated
View File

@@ -12,6 +12,7 @@ dependencies = [
"pmoaudio-ext",
"pmoaudiocache",
"pmoconfig",
"pmocontrol",
"pmocovers",
"pmomediarenderer",
"pmomediaserver",

View File

@@ -14,6 +14,7 @@ pmocovers = { path = "../pmocovers", features = ["pmoserver"] }
pmoaudiocache = { path = "../pmoaudiocache", features = ["pmoserver"]}
pmoaudio-ext = { path = "../pmoaudio-ext", features = ["all"] }
pmoapp = { path = "../pmoapp", features = ["pmoserver"] }
pmocontrol = { path = "../pmocontrol", features = ["pmoserver"] }
tokio = { version = "1.35", features = ["rt-multi-thread", "macros", "sync", "time","signal"] }
tracing = "0.1.41"

View File

@@ -1,4 +1,5 @@
use pmoapp::{WebAppExt, Webapp};
use pmocontrol::ControlPointExt;
use pmomediarenderer::MEDIA_RENDERER;
use pmomediaserver::{
MEDIA_SERVER, MediaServerDeviceExt, ParadiseStreamingExt, sources::SourcesExt,
@@ -97,6 +98,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
server_instance.description_route()
);
// Enregistrer le Control Point (découverte renderers/serveurs + API REST + SSE)
info!("🎛️ Registering Control Point...");
let _control_point = server
.write()
.await
.register_control_point(5)
.await
.expect("Failed to register Control Point");
// Ajouter la webapp via le trait WebAppExt
info!("📡 Registering Web application...");
server

View File

@@ -0,0 +1,72 @@
//! Exemple d'intégration du Control Point dans PMOMusic
//!
//! Cet exemple montre comment enregistrer le Control Point dans une application
//! PMOMusic complète, en suivant le même pattern que les autres composants.
use pmocontrol::ControlPointExt;
use pmoserver::Server;
use tracing::info;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialiser le logging
tracing_subscriber::fmt::init();
// ========== PHASE 1 : Infrastructure ==========
let server = Server::create_upnp_server().await?;
// ========== PHASE 2 : Enregistrement des composants ==========
// Enregistrer les devices UPnP, sources musicales, etc.
// (code existant de PMOMusic...)
// ========== Enregistrer le Control Point ==========
//
// Cette ligne unique :
// 1. Lance le runtime SSDP et la découverte des devices
// 2. Démarre le polling des renderers (état, position, volume, etc.)
// 3. S'abonne aux événements UPnP des serveurs de médias
// 4. Enregistre toutes les routes REST (/api/control/*)
// 5. Enregistre tous les endpoints SSE (/api/control/events/*)
// 6. Génère la documentation OpenAPI
info!("🎛️ Registering Control Point...");
let control_point = server
.write()
.await
.register_control_point(5) // timeout de 5 secondes pour les requêtes HTTP
.await?;
// Le Control Point est maintenant actif !
// On peut l'utiliser directement si besoin
info!("✅ Control Point ready!");
// Exemple : lister les renderers découverts (optionnel)
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
let renderers = control_point.list_music_renderers();
info!("📻 Discovered {} renderer(s)", renderers.len());
for renderer in renderers {
info!(" - {} ({})", renderer.friendly_name, renderer.id.0);
}
// ========== PHASE 3 : Démarrage du serveur ==========
info!("🌐 Starting HTTP server...");
server.write().await.start().await;
info!("✅ PMOMusic is ready!");
info!("📡 Control Point API available at:");
info!(" - GET /api/control/renderers");
info!(" - GET /api/control/servers");
info!(" - GET /api/control/events (SSE)");
info!(" - GET /api/control/events/renderers (SSE)");
info!(" - GET /api/control/events/servers (SSE)");
info!(" - Docs: /swagger-ui/control");
info!("");
info!("Press Ctrl+C to stop...");
server.write().await.wait().await;
Ok(())
}

View File

@@ -26,6 +26,9 @@ pub mod pmoserver_ext;
#[cfg(feature = "pmoserver")]
pub mod sse;
#[cfg(feature = "pmoserver")]
pub use pmoserver_ext::ControlPointExt;
pub use arylic_tcp::ArylicTcpRenderer;
pub use avtransport_client::{AvTransportClient, PositionInfo, TransportInfo};
pub use capabilities::{

View File

@@ -962,7 +962,13 @@ pub fn create_api_router(state: ControlPointState, control_point: Arc<ControlPoi
#[cfg(feature = "pmoserver")]
#[async_trait]
pub trait ControlPointExt {
/// Initialise l'API Control Point
/// Enregistre et initialise le Control Point avec son API complète
///
/// Cette fonction de haut niveau :
/// 1. Lance le runtime du ControlPoint (découverte SSDP, polling renderers, etc.)
/// 2. Enregistre toutes les routes HTTP REST
/// 3. Enregistre tous les endpoints SSE pour les événements
/// 4. Génère la documentation OpenAPI
///
/// # Routes créées
///
@@ -978,13 +984,83 @@ pub trait ControlPointExt {
///
/// # Arguments
///
/// * `control_point` - Instance du ControlPoint
/// * `timeout_secs` - Timeout HTTP pour les requêtes UPnP (recommandé: 5 secondes)
///
/// # Returns
///
/// Retourne l'instance du ControlPoint dans un Arc pour permettre
/// d'interagir avec depuis l'application.
///
/// # Errors
///
/// Retourne une erreur si le runtime SSDP ne peut pas être démarré.
///
/// # Examples
///
/// ```ignore
/// use pmocontrol::ControlPointExt;
/// use pmoserver::Server;
///
/// let server = Server::create_upnp_server().await?;
///
/// // Enregistrer le Control Point avec timeout de 5 secondes
/// let control_point = server
/// .write()
/// .await
/// .register_control_point(5)
/// .await?;
///
/// // Le Control Point est maintenant actif et ses routes HTTP/SSE sont enregistrées
/// // On peut l'utiliser directement si besoin
/// let renderers = control_point.list_music_renderers();
/// ```
async fn register_control_point(&mut self, timeout_secs: u64) -> std::io::Result<Arc<ControlPoint>>;
/// Initialise l'API Control Point (bas niveau)
///
/// Cette méthode est appelée automatiquement par `register_control_point()`.
/// Utilisez `register_control_point()` pour la plupart des cas d'usage.
///
/// # Routes créées
///
/// - API REST: `/api/control/*`
/// - SSE Events: `/api/control/events/*`
/// - Swagger: `/swagger-ui/control`
///
/// # Arguments
///
/// * `control_point` - Instance du ControlPoint déjà créée
async fn init_control_point(&mut self, control_point: Arc<ControlPoint>);
}
#[cfg(feature = "pmoserver")]
#[async_trait]
impl ControlPointExt for pmoserver::Server {
async fn register_control_point(&mut self, timeout_secs: u64) -> std::io::Result<Arc<ControlPoint>> {
use tracing::info;
info!("🎛️ Initializing Control Point...");
// 1. Lancer le runtime du ControlPoint
let control_point = ControlPoint::spawn(timeout_secs)?;
let control_point = Arc::new(control_point);
info!("✅ Control Point runtime started");
info!(" - SSDP discovery active");
info!(" - Renderer polling active (1s interval)");
info!(" - MediaServer event subscriptions active");
// 2. Enregistrer les routes HTTP REST et SSE
self.init_control_point(control_point.clone()).await;
info!("✅ Control Point API registered:");
info!(" - REST API: /api/control/*");
info!(" - SSE Events: /api/control/events/*");
info!(" - OpenAPI docs: /swagger-ui/control");
Ok(control_point)
}
async fn init_control_point(&mut self, control_point: Arc<ControlPoint>) {
let state = ControlPointState::new(control_point.clone());