- Migrate pmomediarenderer: extract all core logic (adapter, handlers, pipeline etc.) into dedicated modules - Rename types to MediaRenderer* for clarity (Web → Mediarenderer) pmowebrenderer becomes a pure browser adapter crate - Update Cargo.toml dependencies accordingly, add pmoserver feature propagation PMOMusic: remove obsolete MEDIA_RENDERER import - Update all imports across workspace to reflect new crate boundaries
145 lines
5.1 KiB
Rust
145 lines
5.1 KiB
Rust
use pmoapp::{WebAppExt, Webapp};
|
|
use pmocontrol::ControlPointExt;
|
|
use pmomediaserver::{
|
|
MEDIA_SERVER, MediaServerDeviceExt, ParadiseStreamingExt, sources::SourcesExt,
|
|
};
|
|
use pmoserver::Server;
|
|
use pmosource::MusicSourceExt;
|
|
use pmoupnp::UpnpServerExt;
|
|
use pmowebrenderer::WebRendererExt;
|
|
use tracing::info;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
// ========== PHASE 1 : Infrastructure UPnP ==========
|
|
// #[cfg(tokio_unstable)]
|
|
// console_subscriber::init();
|
|
|
|
let server = Server::create_upnp_server().await?; // Routes personnalisées de l'application
|
|
server
|
|
.write()
|
|
.await
|
|
.add_route("/info", || async {
|
|
serde_json::json!({"version": "1.0.0"})
|
|
})
|
|
.await;
|
|
|
|
// Initialiser le système de gestion des sources musicales avec API REST
|
|
info!("📡 Initializing music sources management system...");
|
|
server
|
|
.write()
|
|
.await
|
|
.init_music_sources()
|
|
.await
|
|
.expect("Failed to initialize music sources API");
|
|
|
|
// ========== PHASE 2 : Configuration métier ==========
|
|
|
|
// Enregistrer les sources musicales
|
|
info!("🎵 Registering music sources...");
|
|
|
|
// Enregistrer Qobuz pour activer les lazy providers (QOBUZ:PK)
|
|
if let Err(e) = server.write().await.register_qobuz().await {
|
|
tracing::warn!("⚠️ Failed to register Qobuz source: {}", e);
|
|
}
|
|
|
|
// Initialiser les canaux de streaming Radio Paradise (pipelines + routes HTTP)
|
|
info!("📻 Initializing Radio Paradise streaming channels...");
|
|
if let Err(e) = server.write().await.init_paradise_streaming().await {
|
|
tracing::warn!("⚠️ Failed to initialize Paradise streaming: {}", e);
|
|
} else {
|
|
// Enregistrer la source Radio Paradise UPnP (inclut l'initialisation de l'API)
|
|
if let Err(e) = server.write().await.register_paradise().await {
|
|
tracing::warn!("⚠️ Failed to register Radio Paradise source: {}", e);
|
|
}
|
|
}
|
|
|
|
// Enregistrer la source Radio France (inclut l'initialisation des routes API)
|
|
info!("📻 Registering Radio France source...");
|
|
if let Err(e) = server.write().await.register_radiofrance().await {
|
|
tracing::warn!("⚠️ Failed to register Radio France source: {}", e);
|
|
}
|
|
|
|
// Lister toutes les sources enregistrées
|
|
let sources = server.read().await.list_music_sources().await;
|
|
info!("✅ {} music source(s) registered", sources.len());
|
|
for source in sources {
|
|
info!(" - {} ({})", source.name(), source.id());
|
|
}
|
|
|
|
// Enregistrer les devices UPnP (HTTP + SSDP automatique)
|
|
info!("📡 Registering UPnP devices...");
|
|
|
|
let server_instance = server
|
|
.write()
|
|
.await
|
|
.register_device(MEDIA_SERVER.clone(), true)
|
|
.await
|
|
.expect("Failed to register MediaServer");
|
|
|
|
// Enregistrer l'instance ContentDirectory pour les notifications GENA
|
|
if let Some(cd_service) = server_instance.get_service("ContentDirectory") {
|
|
pmomediaserver::contentdirectory::state::register_instance(&cd_service);
|
|
}
|
|
|
|
// Initialiser les ProtocolInfo du MediaServer
|
|
server_instance.init_protocol_info();
|
|
|
|
info!(
|
|
"✅ MediaServer ready at {}{}",
|
|
server_instance.base_url(),
|
|
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");
|
|
|
|
// Enregistrer le WebRenderer (endpoint WebSocket pour renderers navigateur)
|
|
info!("🌐 Registering WebRenderer...");
|
|
server
|
|
.write()
|
|
.await
|
|
.register_web_renderer(control_point)
|
|
.await
|
|
.expect("Failed to register WebRenderer");
|
|
|
|
// Ajouter la webapp via le trait WebAppExt
|
|
info!("📡 Registering Web application...");
|
|
server
|
|
.write()
|
|
.await
|
|
.add_webapp_with_redirect::<Webapp>("/app")
|
|
.await;
|
|
|
|
// ========== PHASE 3 : Démarrage du serveur ==========
|
|
|
|
info!("🌐 Starting HTTP server...");
|
|
server.write().await.start().await;
|
|
|
|
info!("✅ PMOMusic is ready!");
|
|
info!("Press Ctrl+C to stop...");
|
|
|
|
// Extraire le join_handle AVANT de libérer le write lock,
|
|
// pour pouvoir l'awaiter sans tenir le write lock du serveur global.
|
|
// (Tenir le write lock pendant wait() bloquerait register_device() dynamique)
|
|
let join_handle = server.write().await.take_join_handle();
|
|
if let Some(h) = join_handle {
|
|
let _ = h.await;
|
|
}
|
|
|
|
// Le serveur HTTP est arrêté, mais des threads (ControlPoint, etc.) peuvent encore tourner
|
|
// Attendre 2 secondes pour laisser le temps aux threads de se terminer
|
|
info!("Waiting for background threads to finish...");
|
|
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
|
|
|
// Forcer l'arrêt du processus (les threads du ControlPoint tournent en boucle infinie)
|
|
info!("✅ PMOMusic stopped");
|
|
std::process::exit(0);
|
|
}
|