Eh bien, je ne sais plus à quoi ça correspond.
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -12,6 +12,7 @@ dependencies = [
|
||||
"pmoaudio-ext",
|
||||
"pmoaudiocache",
|
||||
"pmoconfig",
|
||||
"pmocontrol",
|
||||
"pmocovers",
|
||||
"pmomediarenderer",
|
||||
"pmomediaserver",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
72
pmocontrol/examples/pmomusic_integration_example.rs
Normal file
72
pmocontrol/examples/pmomusic_integration_example.rs
Normal 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(())
|
||||
}
|
||||
@@ -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::{
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user