Implémentation du WebRenderer et améliorations du serveur
Ajout de la fonctionnalité WebRenderer permettant au navigateur de se connecter comme renderer UPnP. - Implémentation du composant Vue useWebRenderer pour gérer la connexion WebSocket et le contrôle audio - Ajout d'un proxy WebSocket dans Vite pour rediriger les requêtes /api/webrenderer/ws vers le serveur - Modification du serveur pour permettre l'enregistrement dynamique de routes et extraction du JoinHandle - Améliorations du parsing des métadonnées AVTransport pour gérer les types String et DIDLLite - Ajout de la détection et du nommage des navigateurs dans le WebRenderer - Mise à jour des dépendances avec tower 0.5.2 - Correction de la gestion des threads lors de l'arrêt du serveur HTTP
This commit is contained in:
@@ -6,8 +6,10 @@
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use pmoupnp::actions::{ActionData, ActionError, ActionHandler};
|
||||
use pmodidl::DIDLLite;
|
||||
use pmoupnp::actions::{ActionData, ActionError, ActionHandler, get_value};
|
||||
use pmoupnp::{get, set};
|
||||
use pmoutils::ToXmlElement;
|
||||
|
||||
use crate::messages::{CommandParams, PlaybackState, ServerMessage, TransportAction};
|
||||
use crate::state::SharedState;
|
||||
@@ -118,7 +120,13 @@ pub fn set_uri_handler(
|
||||
let state = state.clone();
|
||||
Box::pin(async move {
|
||||
let uri: String = get!(&data, "CurrentURI", String);
|
||||
let metadata: String = get!(&data, "CurrentURIMetaData", String);
|
||||
// CurrentURIMetaData peut être String ou DIDLLite (après parsing)
|
||||
let metadata: String = get_value::<String>(&data, "CurrentURIMetaData")
|
||||
.or_else(|_| {
|
||||
get_value::<DIDLLite>(&data, "CurrentURIMetaData")
|
||||
.map(|didl| didl.to_xml())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let _ = ws.send(ServerMessage::Command {
|
||||
action: TransportAction::SetUri,
|
||||
params: Some(CommandParams {
|
||||
|
||||
@@ -48,6 +48,23 @@ pub enum FactoryError {
|
||||
VariableError(String),
|
||||
}
|
||||
|
||||
/// Extrait un nom de navigateur court depuis un User-Agent complet.
|
||||
fn extract_browser_name(ua: &str) -> &str {
|
||||
if ua.contains("Edg/") || ua.contains("EdgA/") {
|
||||
"Edge"
|
||||
} else if ua.contains("OPR/") || ua.contains("Opera") {
|
||||
"Opera"
|
||||
} else if ua.contains("Chrome/") {
|
||||
"Chrome"
|
||||
} else if ua.contains("Firefox/") {
|
||||
"Firefox"
|
||||
} else if ua.contains("Safari/") {
|
||||
"Safari"
|
||||
} else {
|
||||
"Browser"
|
||||
}
|
||||
}
|
||||
|
||||
/// Factory pour créer des Device UPnP WebRenderer avec des handlers WebSocket
|
||||
pub struct WebRendererFactory;
|
||||
|
||||
@@ -65,10 +82,11 @@ impl WebRendererFactory {
|
||||
let renderingcontrol = Self::build_renderingcontrol(ws_sender.clone(), state.clone())?;
|
||||
let connectionmanager = Self::build_connectionmanager()?;
|
||||
|
||||
let short_name = extract_browser_name(browser_name);
|
||||
let device = Device::new(
|
||||
format!("WebRenderer"),
|
||||
"WebRenderer".to_string(),
|
||||
"MediaRenderer".to_string(),
|
||||
format!("Web Audio - {}", browser_name),
|
||||
format!("Web Audio – {}", short_name),
|
||||
);
|
||||
device
|
||||
.add_service(Arc::new(avtransport))
|
||||
|
||||
@@ -40,11 +40,13 @@ pub async fn websocket_handler(
|
||||
ws: WebSocketUpgrade,
|
||||
State(state): State<WebSocketState>,
|
||||
) -> impl IntoResponse {
|
||||
tracing::info!("WebRenderer WebSocket upgrade request received");
|
||||
ws.on_upgrade(move |socket: WebSocket| handle_socket(socket, state))
|
||||
}
|
||||
|
||||
/// Gestion de la connexion WebSocket
|
||||
async fn handle_socket(socket: WebSocket, state: WebSocketState) {
|
||||
tracing::info!("WebRenderer WebSocket connection established");
|
||||
let (mut sink, mut stream) = socket.split();
|
||||
|
||||
// Canal pour envoyer des messages au navigateur
|
||||
@@ -68,11 +70,14 @@ async fn handle_socket(socket: WebSocket, state: WebSocketState) {
|
||||
while let Some(msg_result) = stream.next().await {
|
||||
match msg_result {
|
||||
Ok(Message::Text(text)) => {
|
||||
tracing::info!("WebRenderer received text message: {}", &text);
|
||||
match serde_json::from_str::<ClientMessage>(&text) {
|
||||
Ok(ClientMessage::Init { capabilities }) => {
|
||||
tracing::info!("WebRenderer Init received, creating renderer...");
|
||||
// Créer le renderer UPnP pour ce navigateur
|
||||
match create_renderer_for_browser(&capabilities, tx.clone(), &state).await {
|
||||
Ok(session) => {
|
||||
tracing::info!("WebRenderer create_renderer_for_browser OK");
|
||||
let token = session.token.clone();
|
||||
let udn = session.udn.clone();
|
||||
|
||||
@@ -106,7 +111,7 @@ async fn handle_socket(socket: WebSocket, state: WebSocketState) {
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to create WebRenderer: {}", e);
|
||||
tracing::error!("Failed to create WebRenderer: {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -204,12 +209,14 @@ async fn create_renderer_for_browser(
|
||||
let token = Uuid::new_v4().to_string();
|
||||
|
||||
// Construire le Device model avec les handlers WS
|
||||
tracing::info!("WebRenderer: creating device model...");
|
||||
let device = WebRendererFactory::create_device(
|
||||
&capabilities.user_agent,
|
||||
ws_sender.clone(),
|
||||
shared_state.clone(),
|
||||
)
|
||||
.map_err(|e| crate::error::WebRendererError::DeviceCreationError(e.to_string()))?;
|
||||
tracing::info!("WebRenderer: device model created");
|
||||
|
||||
let device = Arc::new(device);
|
||||
|
||||
@@ -218,19 +225,24 @@ async fn create_renderer_for_browser(
|
||||
let di = {
|
||||
use pmoupnp::UpnpServerExt;
|
||||
|
||||
tracing::info!("WebRenderer: getting server arc...");
|
||||
let server_arc =
|
||||
pmoserver::get_server().ok_or(crate::error::WebRendererError::ServerNotAvailable)?;
|
||||
tracing::info!("WebRenderer: got server arc, acquiring write lock...");
|
||||
|
||||
let di = {
|
||||
let mut server = server_arc.write().await;
|
||||
tracing::info!("WebRenderer: write lock acquired, registering device...");
|
||||
server
|
||||
.register_device(device)
|
||||
.await
|
||||
.map_err(|e| crate::error::WebRendererError::RegistrationError(e.to_string()))?
|
||||
};
|
||||
tracing::info!("WebRenderer: device registered, registering with ControlPoint...");
|
||||
|
||||
// Enregistrer avec le ControlPoint
|
||||
register_with_control_point(&di, ws_state)?;
|
||||
tracing::info!("WebRenderer: registered with ControlPoint");
|
||||
|
||||
di
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user