update du mediaserver pour le passer en mode stateless

This commit is contained in:
2025-10-19 11:25:00 +02:00
parent 2c814bd7c0
commit d2fe0a1bf6
12 changed files with 122 additions and 167 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -104,24 +104,8 @@
/// - Initialisation paresseuse via `Lazy` (thread-safe) /// - Initialisation paresseuse via `Lazy` (thread-safe)
#[macro_export] #[macro_export]
macro_rules! define_action { macro_rules! define_action {
// Variante sans arguments avec options `stateless` et handler // Variante stateless avec arguments
(pub static $name:ident = $action_name:literal $(stateless)? $(with handler $handler:expr)?) => { (pub static $name:ident = $action_name:literal stateless {
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)? {
$( $(
$direction:ident $arg_name:literal => $var_ref:expr $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>> = pub static $name: once_cell::sync::Lazy<std::sync::Arc<$crate::actions::Action>> =
once_cell::sync::Lazy::new(|| { once_cell::sync::Lazy::new(|| {
let mut ac = $crate::actions::Action::new($action_name.to_string()); let mut ac = $crate::actions::Action::new($action_name.to_string());
ac.set_stateful(false);
define_action!(@maybe_stateless ac $(stateless)?);
$( $(
ac.add_argument( ac.add_argument(
@@ -148,11 +131,64 @@ macro_rules! define_action {
}); });
}; };
(@maybe_stateless $ac:ident stateless) => { // Variante stateless sans arguments
$ac.set_stateful(false); (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 // Helper interne pour créer un argument d'entrée
(@arg in $name:literal, $var:expr) => { (@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> { async fn create_upnp_server() -> Result<Server, anyhow::Error> {
use pmoserver::ServerBuilder; use pmoserver::ServerBuilder;
use tracing::{info, warn}; use tracing::{error, info, warn};
// 1. Créer le serveur depuis la config // 1. Créer le serveur depuis la config
info!("🔧 Creating UPnP server from configuration..."); info!("🔧 Creating UPnP server from configuration...");
@@ -421,7 +421,19 @@ impl UpnpServerExt for Server {
match server.init_ssdp() { match server.init_ssdp() {
Ok(_) => info!("✅ SSDP server initialized"), Ok(_) => info!("✅ SSDP server initialized"),
Err(e) => { 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()); return Err(e.into());
} }
} }