update du mediaserver pour le passer en mode stateless

This commit is contained in:
2025-10-19 11:25:00 +02:00
parent 079093f447
commit 633e3191d2
12 changed files with 122 additions and 167 deletions

View File

@@ -2,7 +2,7 @@ use crate::connectionmanager::variables::CURRENTCONNECTIONIDS;
use pmoupnp::define_action;
define_action! {
pub static GETCURRENTCONNECTIONIDS = "GetCurrentConnectionIDs" {
pub static GETCURRENTCONNECTIONIDS = "GetCurrentConnectionIDs" stateless {
out "ConnectionIDs" => CURRENTCONNECTIONIDS,
}
}

View File

@@ -5,7 +5,7 @@ use crate::connectionmanager::variables::{
use pmoupnp::define_action;
define_action! {
pub static GETCURRENTCONNECTIONINFO = "GetCurrentConnectionInfo" {
pub static GETCURRENTCONNECTIONINFO = "GetCurrentConnectionInfo" stateless {
in "ConnectionID" => A_ARG_TYPE_CONNECTIONID,
out "RcsID" => A_ARG_TYPE_RCSID,
out "AVTransportID" => A_ARG_TYPE_AVTRANSPORTID,

View File

@@ -2,7 +2,7 @@ use crate::connectionmanager::variables::{SOURCEPROTOCOLINFO, SINKPROTOCOLINFO};
use pmoupnp::define_action;
define_action! {
pub static GETPROTOCOLINFO = "GetProtocolInfo" {
pub static GETPROTOCOLINFO = "GetProtocolInfo" stateless {
out "Source" => SOURCEPROTOCOLINFO,
out "Sink" => SINKPROTOCOLINFO,
}

View File

@@ -7,7 +7,7 @@ use pmoupnp::define_action;
use crate::contentdirectory::handlers;
define_action! {
pub static BROWSE = "Browse" {
pub static BROWSE = "Browse" stateless {
in "ObjectID" => A_ARG_TYPE_OBJECTID,
in "BrowseFlag" => A_ARG_TYPE_BROWSEFLAG,
in "Filter" => A_ARG_TYPE_FILTER,

View File

@@ -3,7 +3,7 @@ use pmoupnp::define_action;
use crate::contentdirectory::handlers;
define_action! {
pub static GETSEARCHCAPABILITIES = "GetSearchCapabilities" {
pub static GETSEARCHCAPABILITIES = "GetSearchCapabilities" stateless {
out "SearchCaps" => SEARCHCAPABILITIES,
}
with handler handlers::get_search_capabilities_handler()

View File

@@ -3,7 +3,7 @@ use pmoupnp::define_action;
use crate::contentdirectory::handlers;
define_action! {
pub static GETSORTCAPABILITIES = "GetSortCapabilities" {
pub static GETSORTCAPABILITIES = "GetSortCapabilities" stateless {
out "SortCaps" => SORTCAPABILITIES,
}
with handler handlers::get_sort_capabilities_handler()

View File

@@ -3,7 +3,7 @@ use pmoupnp::define_action;
use crate::contentdirectory::handlers;
define_action! {
pub static GETSYSTEMUPDATEID = "GetSystemUpdateID" {
pub static GETSYSTEMUPDATEID = "GetSystemUpdateID" stateless {
out "Id" => SYSTEMUPDATEID,
}
with handler handlers::get_system_update_id_handler()

View File

@@ -7,7 +7,7 @@ use pmoupnp::define_action;
use crate::contentdirectory::handlers;
define_action! {
pub static SEARCH = "Search" {
pub static SEARCH = "Search" stateless {
in "ContainerID" => A_ARG_TYPE_OBJECTID,
in "SearchCriteria" => A_ARG_TYPE_SEARCHCRITERIA,
in "Filter" => A_ARG_TYPE_FILTER,

View File

@@ -23,9 +23,8 @@
//! - [`get_sort_capabilities_handler`] : Capacités de tri supportées
//! - [`get_system_update_id_handler`] : ID de mise à jour du système
use pmoupnp::action_handler;
use pmoupnp::actions::{ActionHandler, ActionError};
use pmoupnp::variable_types::StateValue;
use pmoupnp::{action_handler, get, set};
use pmoupnp::actions::{ActionError, ActionHandler};
use crate::content_handler::ContentHandler;
use tracing::{debug, error};
@@ -49,51 +48,18 @@ use tracing::{debug, error};
/// - `TotalMatches` : Nombre total d'éléments
/// - `UpdateID` : ID de mise à jour
pub fn browse_handler() -> ActionHandler {
action_handler!(|instance| {
action_handler!(|data| {
let mut data = data;
debug!("📂 Browse handler called");
let handler = ContentHandler::new();
// Extraire les arguments d'entrée
let object_id = match instance
.argument("ObjectID")
.and_then(|arg| arg.get_variable_instance())
.ok_or_else(|| ActionError::ArgumentError("ObjectID not found".to_string()))?
.value()
{
StateValue::String(s) => s,
_ => return Err(ActionError::ArgumentError("ObjectID must be a string".to_string())),
};
let browse_flag = match instance
.argument("BrowseFlag")
.and_then(|arg| arg.get_variable_instance())
.ok_or_else(|| ActionError::ArgumentError("BrowseFlag not found".to_string()))?
.value()
{
StateValue::String(s) => s,
_ => return Err(ActionError::ArgumentError("BrowseFlag must be a string".to_string())),
};
let starting_index = match instance
.argument("StartingIndex")
.and_then(|arg| arg.get_variable_instance())
.ok_or_else(|| ActionError::ArgumentError("StartingIndex not found".to_string()))?
.value()
{
StateValue::UI4(n) => n,
_ => return Err(ActionError::ArgumentError("StartingIndex must be ui4".to_string())),
};
let requested_count = match instance
.argument("RequestedCount")
.and_then(|arg| arg.get_variable_instance())
.ok_or_else(|| ActionError::ArgumentError("RequestedCount not found".to_string()))?
.value()
{
StateValue::UI4(n) => n,
_ => return Err(ActionError::ArgumentError("RequestedCount must be ui4".to_string())),
};
let object_id: String = get!(&data, "ObjectID", String);
let browse_flag: String = get!(&data, "BrowseFlag", String);
let starting_index: u32 = get!(&data, "StartingIndex", u32);
let requested_count: u32 = get!(&data, "RequestedCount", u32);
let _filter: String = get!(&data, "Filter", String);
let _sort_criteria: String = get!(&data, "SortCriteria", String);
// Appeler la logique métier
let (didl, returned, total, update_id) = handler
@@ -105,32 +71,13 @@ pub fn browse_handler() -> ActionHandler {
})?;
// Définir les arguments de sortie
if let Some(arg) = instance.argument("Result") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::String(didl)).await;
}
}
if let Some(arg) = instance.argument("NumberReturned") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::UI4(returned)).await;
}
}
if let Some(arg) = instance.argument("TotalMatches") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::UI4(total)).await;
}
}
if let Some(arg) = instance.argument("UpdateID") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::UI4(update_id)).await;
}
}
set!(&mut data, "Result", didl);
set!(&mut data, "NumberReturned", returned);
set!(&mut data, "TotalMatches", total);
set!(&mut data, "UpdateID", update_id);
debug!("✅ Browse completed: returned={}, total={}", returned, total);
Ok(())
Ok(data)
})
}
@@ -154,30 +101,18 @@ pub fn browse_handler() -> ActionHandler {
/// - `TotalMatches` : Total
/// - `UpdateID` : ID de mise à jour
pub fn search_handler() -> ActionHandler {
action_handler!(|instance| {
action_handler!(|data| {
let mut data = data;
debug!("🔍 Search handler called");
let handler = ContentHandler::new();
let container_id = match instance
.argument("ContainerID")
.and_then(|arg| arg.get_variable_instance())
.ok_or_else(|| ActionError::ArgumentError("ContainerID not found".to_string()))?
.value()
{
StateValue::String(s) => s,
_ => return Err(ActionError::ArgumentError("ContainerID must be a string".to_string())),
};
let search_criteria = match instance
.argument("SearchCriteria")
.and_then(|arg| arg.get_variable_instance())
.ok_or_else(|| ActionError::ArgumentError("SearchCriteria not found".to_string()))?
.value()
{
StateValue::String(s) => s,
_ => return Err(ActionError::ArgumentError("SearchCriteria must be a string".to_string())),
};
let container_id: String = get!(&data, "ContainerID", String);
let search_criteria: String = get!(&data, "SearchCriteria", String);
let _filter: String = get!(&data, "Filter", String);
let _starting_index: u32 = get!(&data, "StartingIndex", u32);
let _requested_count: u32 = get!(&data, "RequestedCount", u32);
let _sort_criteria: String = get!(&data, "SortCriteria", String);
let (didl, returned, total, update_id) = handler
.search(&container_id, &search_criteria)
@@ -188,32 +123,13 @@ pub fn search_handler() -> ActionHandler {
})?;
// Définir les sorties
if let Some(arg) = instance.argument("Result") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::String(didl)).await;
}
}
if let Some(arg) = instance.argument("NumberReturned") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::UI4(returned)).await;
}
}
if let Some(arg) = instance.argument("TotalMatches") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::UI4(total)).await;
}
}
if let Some(arg) = instance.argument("UpdateID") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::UI4(update_id)).await;
}
}
set!(&mut data, "Result", didl);
set!(&mut data, "NumberReturned", returned);
set!(&mut data, "TotalMatches", total);
set!(&mut data, "UpdateID", update_id);
debug!("✅ Search completed: returned={}, total={}", returned, total);
Ok(())
Ok(data)
})
}
@@ -225,20 +141,17 @@ pub fn search_handler() -> ActionHandler {
///
/// - `SearchCaps` : Chaîne de capacités séparées par virgules
pub fn get_search_capabilities_handler() -> ActionHandler {
action_handler!(|instance| {
action_handler!(|data| {
let mut data = data;
debug!("🔍 GetSearchCapabilities handler called");
let handler = ContentHandler::new();
let capabilities = handler.get_search_capabilities().await;
if let Some(arg) = instance.argument("SearchCaps") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::String(capabilities.clone())).await;
}
}
set!(&mut data, "SearchCaps", capabilities.clone());
debug!("✅ SearchCapabilities: {}", capabilities);
Ok(())
Ok(data)
})
}
@@ -250,20 +163,17 @@ pub fn get_search_capabilities_handler() -> ActionHandler {
///
/// - `SortCaps` : Chaîne de capacités séparées par virgules
pub fn get_sort_capabilities_handler() -> ActionHandler {
action_handler!(|instance| {
action_handler!(|data| {
let mut data = data;
debug!("📊 GetSortCapabilities handler called");
let handler = ContentHandler::new();
let capabilities = handler.get_sort_capabilities().await;
if let Some(arg) = instance.argument("SortCaps") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::String(capabilities.clone())).await;
}
}
set!(&mut data, "SortCaps", capabilities.clone());
debug!("✅ SortCapabilities: {}", capabilities);
Ok(())
Ok(data)
})
}
@@ -276,20 +186,17 @@ pub fn get_sort_capabilities_handler() -> ActionHandler {
///
/// - `Id` : ID de mise à jour (entier non signé)
pub fn get_system_update_id_handler() -> ActionHandler {
action_handler!(|instance| {
action_handler!(|data| {
let mut data = data;
debug!("🔄 GetSystemUpdateID handler called");
let handler = ContentHandler::new();
let update_id = handler.get_system_update_id().await;
if let Some(arg) = instance.argument("Id") {
if let Some(var) = arg.get_variable_instance() {
var.set_value(StateValue::UI4(update_id)).await;
}
}
set!(&mut data, "Id", update_id);
debug!("✅ SystemUpdateID: {}", update_id);
Ok(())
Ok(data)
})
}

View File

@@ -2,8 +2,8 @@ use pmoupnp::define_variable;
define_variable! {
pub static A_ARG_TYPE_BROWSEFLAG: String = "A_ARG_TYPE_BrowseFlag" {
default: "BrowseDirectChildren",
allowed: ["BrowseMetadata", "BrowseDirectChildren"],
default: "BrowseDirectChildren",
evented: false,
}
}

View File

@@ -104,24 +104,8 @@
/// - Initialisation paresseuse via `Lazy` (thread-safe)
#[macro_export]
macro_rules! define_action {
// Variante sans arguments avec options `stateless` et handler
(pub static $name:ident = $action_name:literal $(stateless)? $(with handler $handler:expr)?) => {
pub static $name: once_cell::sync::Lazy<std::sync::Arc<$crate::actions::Action>> =
once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string());
define_action!(@maybe_stateless ac $(stateless)?);
$(
ac.set_handler($handler);
)?
std::sync::Arc::new(ac)
});
};
// Variante avec arguments, options `stateless` et handler
(pub static $name:ident = $action_name:literal $(stateless)? {
// Variante stateless avec arguments
(pub static $name:ident = $action_name:literal stateless {
$(
$direction:ident $arg_name:literal => $var_ref:expr
),* $(,)?
@@ -131,8 +115,7 @@ macro_rules! define_action {
pub static $name: once_cell::sync::Lazy<std::sync::Arc<$crate::actions::Action>> =
once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string());
define_action!(@maybe_stateless ac $(stateless)?);
ac.set_stateful(false);
$(
ac.add_argument(
@@ -148,11 +131,64 @@ macro_rules! define_action {
});
};
(@maybe_stateless $ac:ident stateless) => {
$ac.set_stateful(false);
// Variante stateless sans arguments
(pub static $name:ident = $action_name:literal stateless
$(with handler $handler:expr)?
) => {
pub static $name: once_cell::sync::Lazy<std::sync::Arc<$crate::actions::Action>> =
once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string());
ac.set_stateful(false);
$(
ac.set_handler($handler);
)?
std::sync::Arc::new(ac)
});
};
(@maybe_stateless $ac:ident) => {};
// Variante stateful (défaut) avec arguments
(pub static $name:ident = $action_name:literal {
$(
$direction:ident $arg_name:literal => $var_ref:expr
),* $(,)?
}
$(with handler $handler:expr)?
) => {
pub static $name: once_cell::sync::Lazy<std::sync::Arc<$crate::actions::Action>> =
once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string());
$(
ac.add_argument(
define_action!(@arg $direction $arg_name, $var_ref)
);
)*
$(
ac.set_handler($handler);
)?
std::sync::Arc::new(ac)
});
};
// Variante stateful (défaut) sans arguments
(pub static $name:ident = $action_name:literal
$(with handler $handler:expr)?
) => {
pub static $name: once_cell::sync::Lazy<std::sync::Arc<$crate::actions::Action>> =
once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string());
$(
ac.set_handler($handler);
)?
std::sync::Arc::new(ac)
});
};
// Helper interne pour créer un argument d'entrée
(@arg in $name:literal, $var:expr) => {

View File

@@ -386,7 +386,7 @@ impl UpnpServerExt for Server {
async fn create_upnp_server() -> Result<Server, anyhow::Error> {
use pmoserver::ServerBuilder;
use tracing::{info, warn};
use tracing::{error, info, warn};
// 1. Créer le serveur depuis la config
info!("🔧 Creating UPnP server from configuration...");
@@ -421,7 +421,19 @@ impl UpnpServerExt for Server {
match server.init_ssdp() {
Ok(_) => info!("✅ SSDP server initialized"),
Err(e) => {
warn!("❌ SSDP initialization failed: {}", e);
let kind = e.kind();
if kind == std::io::ErrorKind::AddrInUse {
error!(
"❌ SSDP initialization failed: port {} is already in use. \
Check which process listens on UDP:{} (e.g. `lsof -nP -i UDP:{}`): {}",
crate::ssdp::SSDP_PORT,
crate::ssdp::SSDP_PORT,
crate::ssdp::SSDP_PORT,
e,
);
} else {
error!("❌ SSDP initialization failed: {}", e);
}
return Err(e.into());
}
}