2026-02-09 07:18:13 +01:00
|
|
|
|
//! Factory pour créer des instances WebRenderer privées
|
|
|
|
|
|
//!
|
|
|
|
|
|
//! Construit des Device/Service models UPnP dynamiques avec des action handlers
|
|
|
|
|
|
//! qui relaient les commandes SOAP vers le navigateur via WebSocket.
|
|
|
|
|
|
|
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
use tokio::sync::mpsc;
|
|
|
|
|
|
|
|
|
|
|
|
use pmoupnp::actions::{Action, Argument};
|
|
|
|
|
|
use pmoupnp::devices::Device;
|
|
|
|
|
|
use pmoupnp::services::Service;
|
|
|
|
|
|
|
|
|
|
|
|
use crate::handlers;
|
|
|
|
|
|
use crate::messages::ServerMessage;
|
|
|
|
|
|
use crate::state::SharedState;
|
|
|
|
|
|
|
|
|
|
|
|
// ─── Réimport des variables statiques de pmomediarenderer ───────────────────
|
|
|
|
|
|
// Variables AVTransport
|
|
|
|
|
|
use pmomediarenderer::avtransport::variables::{
|
|
|
|
|
|
ABSOLUTETIMEPOSITION, AVTRANSPORTNEXTURI, AVTRANSPORTNEXTURIMETADATA, AVTRANSPORTURI,
|
|
|
|
|
|
AVTRANSPORTURIMETADATA, A_ARG_TYPE_INSTANCE_ID as AVT_INSTANCE_ID, A_ARG_TYPE_PLAY_SPEED,
|
|
|
|
|
|
A_ARG_TYPE_SEEKMODE, CURRENTMEDIADURATION, CURRENTPLAYMODE, CURRENTTRACK, CURRENTTRACKDURATION,
|
|
|
|
|
|
CURRENTTRACKMETADATA, CURRENTTRACKURI, NUMBEROFTRACKS, PLAYBACKSTORAGEMEDIUM,
|
|
|
|
|
|
POSSIBLEPLAYBACKSTORAGEMEDIA, RELATIVETIMEPOSITION, SEEKMODE, TRANSPORTPLAYSPEED,
|
|
|
|
|
|
TRANSPORTSTATE, TRANSPORTSTATUS,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Variables RenderingControl
|
|
|
|
|
|
use pmomediarenderer::renderingcontrol::variables::{
|
|
|
|
|
|
A_ARG_TYPE_CHANNEL, A_ARG_TYPE_INSTANCE_ID as RC_INSTANCE_ID, MUTE, VOLUME,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Variables ConnectionManager
|
|
|
|
|
|
use pmomediarenderer::connectionmanager::variables::{
|
|
|
|
|
|
A_ARG_TYPE_AVTRANSPORTID, A_ARG_TYPE_CONNECTIONID, A_ARG_TYPE_CONNECTIONSTATUS,
|
|
|
|
|
|
A_ARG_TYPE_DIRECTION, A_ARG_TYPE_PROTOCOLINFO, A_ARG_TYPE_RCSID, CURRENTCONNECTIONIDS,
|
|
|
|
|
|
SINKPROTOCOLINFO, SOURCEPROTOCOLINFO,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Error, Debug)]
|
|
|
|
|
|
pub enum FactoryError {
|
|
|
|
|
|
#[error("Failed to add service to device: {0}")]
|
|
|
|
|
|
ServiceError(String),
|
|
|
|
|
|
#[error("Failed to add action to service: {0}")]
|
|
|
|
|
|
ActionError(String),
|
|
|
|
|
|
#[error("Failed to add variable to service: {0}")]
|
|
|
|
|
|
VariableError(String),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-21 00:15:38 +01:00
|
|
|
|
/// 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"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-09 07:18:13 +01:00
|
|
|
|
/// Factory pour créer des Device UPnP WebRenderer avec des handlers WebSocket
|
|
|
|
|
|
pub struct WebRendererFactory;
|
|
|
|
|
|
|
|
|
|
|
|
impl WebRendererFactory {
|
|
|
|
|
|
/// Crée un Device model UPnP complet pour un WebRenderer.
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Le device est construit avec des action handlers qui relaient
|
|
|
|
|
|
/// les commandes SOAP vers le navigateur via le `ws_sender`.
|
|
|
|
|
|
pub fn create_device(
|
|
|
|
|
|
browser_name: &str,
|
|
|
|
|
|
ws_sender: mpsc::UnboundedSender<ServerMessage>,
|
|
|
|
|
|
state: SharedState,
|
|
|
|
|
|
) -> Result<Device, FactoryError> {
|
|
|
|
|
|
let avtransport = Self::build_avtransport(ws_sender.clone(), state.clone())?;
|
|
|
|
|
|
let renderingcontrol = Self::build_renderingcontrol(ws_sender.clone(), state.clone())?;
|
|
|
|
|
|
let connectionmanager = Self::build_connectionmanager()?;
|
|
|
|
|
|
|
2026-02-21 00:15:38 +01:00
|
|
|
|
let short_name = extract_browser_name(browser_name);
|
2026-02-09 07:18:13 +01:00
|
|
|
|
let device = Device::new(
|
2026-02-21 00:15:38 +01:00
|
|
|
|
"WebRenderer".to_string(),
|
2026-02-09 07:18:13 +01:00
|
|
|
|
"MediaRenderer".to_string(),
|
2026-02-21 00:15:38 +01:00
|
|
|
|
format!("Web Audio – {}", short_name),
|
2026-02-09 07:18:13 +01:00
|
|
|
|
);
|
|
|
|
|
|
device
|
|
|
|
|
|
.add_service(Arc::new(avtransport))
|
|
|
|
|
|
.map_err(|e| FactoryError::ServiceError(format!("{:?}", e)))?;
|
|
|
|
|
|
device
|
|
|
|
|
|
.add_service(Arc::new(renderingcontrol))
|
|
|
|
|
|
.map_err(|e| FactoryError::ServiceError(format!("{:?}", e)))?;
|
|
|
|
|
|
device
|
|
|
|
|
|
.add_service(Arc::new(connectionmanager))
|
|
|
|
|
|
.map_err(|e| FactoryError::ServiceError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(device)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Construit le service AVTransport avec les handlers WebSocket
|
|
|
|
|
|
fn build_avtransport(
|
|
|
|
|
|
ws: mpsc::UnboundedSender<ServerMessage>,
|
|
|
|
|
|
state: SharedState,
|
|
|
|
|
|
) -> Result<Service, FactoryError> {
|
|
|
|
|
|
let mut svc = Service::new("AVTransport".to_string());
|
|
|
|
|
|
let add_var = |svc: &mut Service, var: &Arc<pmoupnp::state_variables::StateVariable>| {
|
|
|
|
|
|
svc.add_variable(Arc::clone(var))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Ajouter toutes les variables d'état
|
|
|
|
|
|
add_var(&mut svc, &AVT_INSTANCE_ID)?;
|
|
|
|
|
|
add_var(&mut svc, &A_ARG_TYPE_PLAY_SPEED)?;
|
|
|
|
|
|
add_var(&mut svc, &A_ARG_TYPE_SEEKMODE)?;
|
|
|
|
|
|
add_var(&mut svc, &ABSOLUTETIMEPOSITION)?;
|
|
|
|
|
|
add_var(&mut svc, &AVTRANSPORTNEXTURI)?;
|
|
|
|
|
|
add_var(&mut svc, &AVTRANSPORTNEXTURIMETADATA)?;
|
|
|
|
|
|
add_var(&mut svc, &AVTRANSPORTURI)?;
|
|
|
|
|
|
add_var(&mut svc, &AVTRANSPORTURIMETADATA)?;
|
|
|
|
|
|
add_var(&mut svc, &CURRENTMEDIADURATION)?;
|
|
|
|
|
|
add_var(&mut svc, &CURRENTPLAYMODE)?;
|
|
|
|
|
|
add_var(&mut svc, &CURRENTTRACK)?;
|
|
|
|
|
|
add_var(&mut svc, &CURRENTTRACKDURATION)?;
|
|
|
|
|
|
add_var(&mut svc, &CURRENTTRACKMETADATA)?;
|
|
|
|
|
|
add_var(&mut svc, &CURRENTTRACKURI)?;
|
|
|
|
|
|
add_var(&mut svc, &NUMBEROFTRACKS)?;
|
|
|
|
|
|
add_var(&mut svc, &PLAYBACKSTORAGEMEDIUM)?;
|
|
|
|
|
|
add_var(&mut svc, &POSSIBLEPLAYBACKSTORAGEMEDIA)?;
|
|
|
|
|
|
add_var(&mut svc, &RELATIVETIMEPOSITION)?;
|
|
|
|
|
|
add_var(&mut svc, &SEEKMODE)?;
|
|
|
|
|
|
add_var(&mut svc, &TRANSPORTPLAYSPEED)?;
|
|
|
|
|
|
add_var(&mut svc, &TRANSPORTSTATE)?;
|
|
|
|
|
|
add_var(&mut svc, &TRANSPORTSTATUS)?;
|
|
|
|
|
|
|
|
|
|
|
|
let add_action = |svc: &mut Service, action: Arc<Action>| {
|
|
|
|
|
|
svc.add_action(action)
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(e.to_string()))
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// Play
|
|
|
|
|
|
let mut play = Action::new("Play".to_string());
|
|
|
|
|
|
play.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
play.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"Speed".to_string(),
|
|
|
|
|
|
Arc::clone(&TRANSPORTPLAYSPEED),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
play.set_handler(handlers::play_handler(ws.clone(), state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(play))?;
|
|
|
|
|
|
|
|
|
|
|
|
// Stop
|
|
|
|
|
|
let mut stop = Action::new("Stop".to_string());
|
|
|
|
|
|
stop.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
stop.set_handler(handlers::stop_handler(ws.clone(), state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(stop))?;
|
|
|
|
|
|
|
|
|
|
|
|
// Pause
|
|
|
|
|
|
let mut pause = Action::new("Pause".to_string());
|
|
|
|
|
|
pause
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
pause.set_handler(handlers::pause_handler(ws.clone(), state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(pause))?;
|
|
|
|
|
|
|
|
|
|
|
|
// Next
|
|
|
|
|
|
let mut next = Action::new("Next".to_string());
|
|
|
|
|
|
next.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
next.set_handler(handlers::next_handler(ws.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(next))?;
|
|
|
|
|
|
|
|
|
|
|
|
// Previous
|
|
|
|
|
|
let mut previous = Action::new("Previous".to_string());
|
|
|
|
|
|
previous
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
previous.set_handler(handlers::previous_handler(ws.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(previous))?;
|
|
|
|
|
|
|
|
|
|
|
|
// Seek
|
|
|
|
|
|
let mut seek = Action::new("Seek".to_string());
|
|
|
|
|
|
seek.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
seek.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"Unit".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_SEEKMODE),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
seek.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"Target".to_string(),
|
|
|
|
|
|
Arc::clone(&SEEKMODE),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
seek.set_handler(handlers::seek_handler(ws.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(seek))?;
|
|
|
|
|
|
|
|
|
|
|
|
// SetAVTransportURI
|
|
|
|
|
|
let mut set_uri = Action::new("SetAVTransportURI".to_string());
|
|
|
|
|
|
set_uri
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_uri
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"CurrentURI".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTURI),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_uri
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"CurrentURIMetaData".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTURIMETADATA),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_uri.set_handler(handlers::set_uri_handler(ws.clone(), state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(set_uri))?;
|
|
|
|
|
|
|
|
|
|
|
|
// SetNextAVTransportURI
|
|
|
|
|
|
let mut set_next_uri = Action::new("SetNextAVTransportURI".to_string());
|
|
|
|
|
|
set_next_uri
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_next_uri
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"NextURI".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTNEXTURI),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_next_uri
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"NextURIMetaData".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTNEXTURIMETADATA),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_next_uri.set_handler(handlers::set_next_uri_handler(state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(set_next_uri))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetPositionInfo
|
|
|
|
|
|
let mut get_pos = Action::new("GetPositionInfo".to_string());
|
|
|
|
|
|
get_pos
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_pos
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"Track".to_string(),
|
|
|
|
|
|
Arc::clone(&CURRENTTRACK),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_pos
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"TrackDuration".to_string(),
|
|
|
|
|
|
Arc::clone(&CURRENTTRACKDURATION),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_pos
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"TrackURI".to_string(),
|
|
|
|
|
|
Arc::clone(&CURRENTTRACKURI),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_pos
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"TrackMetaData".to_string(),
|
|
|
|
|
|
Arc::clone(&CURRENTTRACKMETADATA),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_pos
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"RelTime".to_string(),
|
|
|
|
|
|
Arc::clone(&RELATIVETIMEPOSITION),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_pos
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"AbsTime".to_string(),
|
|
|
|
|
|
Arc::clone(&ABSOLUTETIMEPOSITION),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_pos.set_stateful(false);
|
|
|
|
|
|
get_pos.set_handler(handlers::get_position_info_handler(state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(get_pos))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetTransportInfo
|
|
|
|
|
|
let mut get_info = Action::new("GetTransportInfo".to_string());
|
|
|
|
|
|
get_info
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_info
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"CurrentTransportState".to_string(),
|
|
|
|
|
|
Arc::clone(&TRANSPORTSTATE),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_info
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"CurrentTransportStatus".to_string(),
|
|
|
|
|
|
Arc::clone(&TRANSPORTSTATUS),
|
|
|
|
|
|
)))
|
2026-02-21 09:43:29 +01:00
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_info
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"CurrentSpeed".to_string(),
|
|
|
|
|
|
Arc::clone(&TRANSPORTPLAYSPEED),
|
|
|
|
|
|
)))
|
2026-02-09 07:18:13 +01:00
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_info.set_stateful(false);
|
|
|
|
|
|
get_info.set_handler(handlers::get_transport_info_handler(state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(get_info))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetMediaInfo
|
|
|
|
|
|
let mut get_media = Action::new("GetMediaInfo".to_string());
|
|
|
|
|
|
get_media
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_media
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"NrTracks".to_string(),
|
|
|
|
|
|
Arc::clone(&NUMBEROFTRACKS),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_media
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"CurrentURI".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTURI),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_media
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"CurrentURIMetaData".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTURIMETADATA),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_media
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"NextURI".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTNEXTURI),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_media
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"NextURIMetaData".to_string(),
|
|
|
|
|
|
Arc::clone(&AVTRANSPORTNEXTURIMETADATA),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_media.set_stateful(false);
|
|
|
|
|
|
get_media.set_handler(handlers::get_media_info_handler(state.clone()));
|
|
|
|
|
|
add_action(&mut svc, Arc::new(get_media))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetTransportSettings (passthrough)
|
|
|
|
|
|
let mut get_settings = Action::new("GetTransportSettings".to_string());
|
|
|
|
|
|
get_settings
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
add_action(&mut svc, Arc::new(get_settings))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetDeviceCapabilities (passthrough)
|
|
|
|
|
|
let mut get_caps = Action::new("GetDeviceCapabilities".to_string());
|
|
|
|
|
|
get_caps
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
add_action(&mut svc, Arc::new(get_caps))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetCurrentTransportActions (passthrough)
|
|
|
|
|
|
let mut get_actions = Action::new("GetCurrentTransportActions".to_string());
|
|
|
|
|
|
get_actions
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&AVT_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
add_action(&mut svc, Arc::new(get_actions))?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(svc)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Construit le service RenderingControl avec les handlers WebSocket
|
|
|
|
|
|
fn build_renderingcontrol(
|
|
|
|
|
|
ws: mpsc::UnboundedSender<ServerMessage>,
|
|
|
|
|
|
state: SharedState,
|
|
|
|
|
|
) -> Result<Service, FactoryError> {
|
|
|
|
|
|
let mut svc = Service::new("RenderingControl".to_string());
|
|
|
|
|
|
|
|
|
|
|
|
svc.add_variable(Arc::clone(&RC_INSTANCE_ID))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&A_ARG_TYPE_CHANNEL))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&VOLUME))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&MUTE))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
|
|
|
|
|
|
// SetVolume
|
|
|
|
|
|
let mut set_vol = Action::new("SetVolume".to_string());
|
|
|
|
|
|
set_vol
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&RC_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_vol
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"Channel".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_CHANNEL),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_vol
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"DesiredVolume".to_string(),
|
|
|
|
|
|
Arc::clone(&VOLUME),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_vol.set_handler(handlers::set_volume_handler(ws.clone(), state.clone()));
|
|
|
|
|
|
svc.add_action(Arc::new(set_vol))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetVolume
|
|
|
|
|
|
let mut get_vol = Action::new("GetVolume".to_string());
|
|
|
|
|
|
get_vol
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&RC_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_vol
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"Channel".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_CHANNEL),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_vol
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"CurrentVolume".to_string(),
|
|
|
|
|
|
Arc::clone(&VOLUME),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_vol.set_stateful(false);
|
|
|
|
|
|
get_vol.set_handler(handlers::get_volume_handler(state.clone()));
|
|
|
|
|
|
svc.add_action(Arc::new(get_vol))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
// SetMute
|
|
|
|
|
|
let mut set_mute = Action::new("SetMute".to_string());
|
|
|
|
|
|
set_mute
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&RC_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_mute
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"Channel".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_CHANNEL),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_mute
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"DesiredMute".to_string(),
|
|
|
|
|
|
Arc::clone(&MUTE),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
set_mute.set_handler(handlers::set_mute_handler(ws.clone(), state.clone()));
|
|
|
|
|
|
svc.add_action(Arc::new(set_mute))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetMute
|
|
|
|
|
|
let mut get_mute = Action::new("GetMute".to_string());
|
|
|
|
|
|
get_mute
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"InstanceID".to_string(),
|
|
|
|
|
|
Arc::clone(&RC_INSTANCE_ID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_mute
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"Channel".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_CHANNEL),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_mute
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"CurrentMute".to_string(),
|
|
|
|
|
|
Arc::clone(&MUTE),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_mute.set_stateful(false);
|
|
|
|
|
|
get_mute.set_handler(handlers::get_mute_handler(state.clone()));
|
|
|
|
|
|
svc.add_action(Arc::new(get_mute))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(svc)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Construit le service ConnectionManager
|
|
|
|
|
|
fn build_connectionmanager() -> Result<Service, FactoryError> {
|
|
|
|
|
|
let mut svc = Service::new("ConnectionManager".to_string());
|
|
|
|
|
|
|
|
|
|
|
|
svc.add_variable(Arc::clone(&A_ARG_TYPE_CONNECTIONID))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&A_ARG_TYPE_CONNECTIONSTATUS))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&A_ARG_TYPE_DIRECTION))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&A_ARG_TYPE_PROTOCOLINFO))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&A_ARG_TYPE_RCSID))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&A_ARG_TYPE_AVTRANSPORTID))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&CURRENTCONNECTIONIDS))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&SINKPROTOCOLINFO))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
svc.add_variable(Arc::clone(&SOURCEPROTOCOLINFO))
|
|
|
|
|
|
.map_err(|e| FactoryError::VariableError(e.to_string()))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetProtocolInfo
|
|
|
|
|
|
let mut get_proto = Action::new("GetProtocolInfo".to_string());
|
|
|
|
|
|
get_proto
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"Source".to_string(),
|
|
|
|
|
|
Arc::clone(&SOURCEPROTOCOLINFO),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_proto
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"Sink".to_string(),
|
|
|
|
|
|
Arc::clone(&SINKPROTOCOLINFO),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_proto.set_stateful(false);
|
|
|
|
|
|
get_proto.set_handler(handlers::get_protocol_info_handler());
|
|
|
|
|
|
svc.add_action(Arc::new(get_proto))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetCurrentConnectionIDs
|
|
|
|
|
|
let mut get_ids = Action::new("GetCurrentConnectionIDs".to_string());
|
|
|
|
|
|
get_ids
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"ConnectionIDs".to_string(),
|
|
|
|
|
|
Arc::clone(&CURRENTCONNECTIONIDS),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
svc.add_action(Arc::new(get_ids))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
// GetCurrentConnectionInfo
|
|
|
|
|
|
let mut get_conn = Action::new("GetCurrentConnectionInfo".to_string());
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_in(
|
|
|
|
|
|
"ConnectionID".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_CONNECTIONID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"RcsID".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_RCSID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"AVTransportID".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_AVTRANSPORTID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"ProtocolInfo".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_PROTOCOLINFO),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"PeerConnectionManager".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_PROTOCOLINFO),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"PeerConnectionID".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_CONNECTIONID),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"Direction".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_DIRECTION),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
get_conn
|
|
|
|
|
|
.add_argument(Arc::new(Argument::new_out(
|
|
|
|
|
|
"Status".to_string(),
|
|
|
|
|
|
Arc::clone(&A_ARG_TYPE_CONNECTIONSTATUS),
|
|
|
|
|
|
)))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
svc.add_action(Arc::new(get_conn))
|
|
|
|
|
|
.map_err(|e| FactoryError::ActionError(format!("{:?}", e)))?;
|
|
|
|
|
|
|
|
|
|
|
|
Ok(svc)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|