From 552d8d90fbcee26fce8691fbab6879f90b6ab846 Mon Sep 17 00:00:00 2001 From: Eric Coissac Date: Fri, 17 Oct 2025 08:55:29 +0200 Subject: [PATCH] implemente le squelette de pmomediaserver --- Cargo.lock | 10 ++ Cargo.toml | 2 +- pmomediaserver/Cargo.toml | 11 ++ pmomediaserver/README.md | 73 +++++++++++ .../actions/getcurrentconnectionids.rs | 8 ++ .../actions/getcurrentconnectioninfo.rs | 18 +++ .../actions/getprotocolinfo.rs | 9 ++ .../src/connectionmanager/actions/mod.rs | 7 ++ pmomediaserver/src/connectionmanager/mod.rs | 99 +++++++++++++++ .../variables/a_arg_type_avtransportid.rs | 7 ++ .../variables/a_arg_type_connectionid.rs | 7 ++ .../variables/a_arg_type_connectionstatus.rs | 8 ++ .../variables/a_arg_type_direction.rs | 8 ++ .../variables/a_arg_type_protocolinfo.rs | 7 ++ .../variables/a_arg_type_rcsid.rs | 7 ++ .../variables/currentconnectionids.rs | 7 ++ .../src/connectionmanager/variables/mod.rs | 19 +++ .../variables/sinkprotocolinfo.rs | 8 ++ .../variables/sourceprotocolinfo.rs | 8 ++ .../src/contentdirectory/actions/browse.rs | 21 ++++ .../actions/getsearchcapabilities.rs | 8 ++ .../actions/getsortcapabilities.rs | 8 ++ .../actions/getsystemupdateid.rs | 8 ++ .../src/contentdirectory/actions/mod.rs | 11 ++ .../src/contentdirectory/actions/search.rs | 21 ++++ pmomediaserver/src/contentdirectory/mod.rs | 116 ++++++++++++++++++ .../variables/a_arg_type_browseflag.rs | 8 ++ .../variables/a_arg_type_count.rs | 7 ++ .../variables/a_arg_type_filter.rs | 7 ++ .../variables/a_arg_type_index.rs | 7 ++ .../variables/a_arg_type_objectid.rs | 7 ++ .../variables/a_arg_type_result.rs | 7 ++ .../variables/a_arg_type_searchcriteria.rs | 7 ++ .../variables/a_arg_type_sortcriteria.rs | 7 ++ .../variables/a_arg_type_updateid.rs | 7 ++ .../src/contentdirectory/variables/mod.rs | 25 ++++ .../variables/searchcapabilities.rs | 7 ++ .../variables/sortcapabilities.rs | 7 ++ .../variables/systemupdateid.rs | 7 ++ pmomediaserver/src/device.rs | 62 ++++++++++ pmomediaserver/src/lib.rs | 33 +++++ 41 files changed, 720 insertions(+), 1 deletion(-) create mode 100644 pmomediaserver/Cargo.toml create mode 100644 pmomediaserver/README.md create mode 100644 pmomediaserver/src/connectionmanager/actions/getcurrentconnectionids.rs create mode 100644 pmomediaserver/src/connectionmanager/actions/getcurrentconnectioninfo.rs create mode 100644 pmomediaserver/src/connectionmanager/actions/getprotocolinfo.rs create mode 100644 pmomediaserver/src/connectionmanager/actions/mod.rs create mode 100644 pmomediaserver/src/connectionmanager/mod.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/a_arg_type_avtransportid.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionid.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionstatus.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/a_arg_type_direction.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/a_arg_type_protocolinfo.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/a_arg_type_rcsid.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/currentconnectionids.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/mod.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/sinkprotocolinfo.rs create mode 100644 pmomediaserver/src/connectionmanager/variables/sourceprotocolinfo.rs create mode 100644 pmomediaserver/src/contentdirectory/actions/browse.rs create mode 100644 pmomediaserver/src/contentdirectory/actions/getsearchcapabilities.rs create mode 100644 pmomediaserver/src/contentdirectory/actions/getsortcapabilities.rs create mode 100644 pmomediaserver/src/contentdirectory/actions/getsystemupdateid.rs create mode 100644 pmomediaserver/src/contentdirectory/actions/mod.rs create mode 100644 pmomediaserver/src/contentdirectory/actions/search.rs create mode 100644 pmomediaserver/src/contentdirectory/mod.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_browseflag.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_count.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_filter.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_index.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_objectid.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_result.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_searchcriteria.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_sortcriteria.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/a_arg_type_updateid.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/mod.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/searchcapabilities.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/sortcapabilities.rs create mode 100644 pmomediaserver/src/contentdirectory/variables/systemupdateid.rs create mode 100644 pmomediaserver/src/device.rs create mode 100644 pmomediaserver/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index ac31d726..c8c8780d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2344,6 +2344,16 @@ dependencies = [ "pmoupnp", ] +[[package]] +name = "pmomediaserver" +version = "0.1.0" +dependencies = [ + "bevy_reflect", + "once_cell", + "pmodidl", + "pmoupnp", +] + [[package]] name = "pmoparadise" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index fecdecd1..3ea3095c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "3" -members = ["PMOMusic", "pmoupnp", "pmomediarenderer", "pmoconfig", "pmoutils", "pmodidl", "pmoserver", "pmoapp", "pmocache", "pmocovers", "pmoaudiocache", "pmoaudio", "pmoqobuz", "pmoparadise", "pmosource", "pmoplaylist"] +members = ["PMOMusic", "pmoupnp", "pmomediarenderer", "pmomediaserver", "pmoconfig", "pmoutils", "pmodidl", "pmoserver", "pmoapp", "pmocache", "pmocovers", "pmoaudiocache", "pmoaudio", "pmoqobuz", "pmoparadise", "pmosource", "pmoplaylist"] diff --git a/pmomediaserver/Cargo.toml b/pmomediaserver/Cargo.toml new file mode 100644 index 00000000..48103974 --- /dev/null +++ b/pmomediaserver/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pmomediaserver" +version = "0.1.0" +edition = "2024" + +[dependencies] +pmoupnp = { path = "../pmoupnp" } +pmodidl = { path = "../pmodidl" } + +once_cell = "1.20" +bevy_reflect = "0.17.1" diff --git a/pmomediaserver/README.md b/pmomediaserver/README.md new file mode 100644 index 00000000..1837f7f3 --- /dev/null +++ b/pmomediaserver/README.md @@ -0,0 +1,73 @@ +# pmomediaserver + +Implémentation d'un MediaServer UPnP conforme à la spécification UPnP AV Architecture. + +## Description + +Cette crate fournit un MediaServer UPnP qui permet d'exposer et de servir du contenu audio à des clients UPnP (MediaRenderer). Elle suit le même modèle architectural que la crate `pmomediarenderer`. + +## Architecture + +Le MediaServer est composé de deux services obligatoires : + +- **ContentDirectory** : Gestion du contenu et de la navigation dans la bibliothèque musicale +- **ConnectionManager** : Gestion des connexions et des protocoles supportés + +## Device UPnP + +- Type : `urn:schemas-upnp-org:device:MediaServer:1` +- Services : ContentDirectory:1, ConnectionManager:1 + +## Utilisation + +```rust +use pmomediaserver::MEDIA_SERVER; +use pmoupnp::UpnpServer; + +// Le device est déjà configuré avec tous ses services +let server = MEDIA_SERVER.clone(); + +// Créer une instance du server +let instance = server.create_instance(); + +// Enregistrer le server sur un serveur UPnP +upnp_server.register_device(server).await?; +``` + +## Dépendances + +- `pmoupnp` : Fournit l'infrastructure UPnP de base (devices, services, actions, state variables) +- `pmodidl` : Pour la gestion des métadonnées DIDL-Lite +- `once_cell` : Pour les initialisations lazy +- `bevy_reflect` : Pour la réflexion et l'introspection + +## Services + +### ContentDirectory + +Service de gestion du contenu conforme UPnP ContentDirectory:1. Permet de naviguer et rechercher dans la bibliothèque musicale. + +**Actions supportées :** +- Browse +- Search +- GetSearchCapabilities +- GetSortCapabilities +- GetSystemUpdateID +- CreateObject (optionnel) +- DestroyObject (optionnel) +- UpdateObject (optionnel) + +### ConnectionManager + +Service de gestion des connexions conforme UPnP ConnectionManager:1. Gère les protocoles supportés. + +**Actions supportées :** +- GetProtocolInfo +- GetCurrentConnectionIDs +- GetCurrentConnectionInfo +- PrepareForConnection (optionnel) +- ConnectionComplete (optionnel) + +## Licence + +Voir le fichier LICENSE à la racine du projet. diff --git a/pmomediaserver/src/connectionmanager/actions/getcurrentconnectionids.rs b/pmomediaserver/src/connectionmanager/actions/getcurrentconnectionids.rs new file mode 100644 index 00000000..12e0ba94 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/actions/getcurrentconnectionids.rs @@ -0,0 +1,8 @@ +use crate::connectionmanager::variables::CURRENTCONNECTIONIDS; +use pmoupnp::define_action; + +define_action! { + pub static GETCURRENTCONNECTIONIDS = "GetCurrentConnectionIDs" { + out "ConnectionIDs" => CURRENTCONNECTIONIDS, + } +} diff --git a/pmomediaserver/src/connectionmanager/actions/getcurrentconnectioninfo.rs b/pmomediaserver/src/connectionmanager/actions/getcurrentconnectioninfo.rs new file mode 100644 index 00000000..817b15f9 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/actions/getcurrentconnectioninfo.rs @@ -0,0 +1,18 @@ +use crate::connectionmanager::variables::{ + A_ARG_TYPE_CONNECTIONID, A_ARG_TYPE_RCSID, A_ARG_TYPE_AVTRANSPORTID, + A_ARG_TYPE_PROTOCOLINFO, A_ARG_TYPE_DIRECTION, A_ARG_TYPE_CONNECTIONSTATUS, +}; +use pmoupnp::define_action; + +define_action! { + pub static GETCURRENTCONNECTIONINFO = "GetCurrentConnectionInfo" { + in "ConnectionID" => A_ARG_TYPE_CONNECTIONID, + out "RcsID" => A_ARG_TYPE_RCSID, + out "AVTransportID" => A_ARG_TYPE_AVTRANSPORTID, + out "ProtocolInfo" => A_ARG_TYPE_PROTOCOLINFO, + out "PeerConnectionManager" => A_ARG_TYPE_CONNECTIONID, + out "PeerConnectionID" => A_ARG_TYPE_CONNECTIONID, + out "Direction" => A_ARG_TYPE_DIRECTION, + out "Status" => A_ARG_TYPE_CONNECTIONSTATUS, + } +} diff --git a/pmomediaserver/src/connectionmanager/actions/getprotocolinfo.rs b/pmomediaserver/src/connectionmanager/actions/getprotocolinfo.rs new file mode 100644 index 00000000..c9370ded --- /dev/null +++ b/pmomediaserver/src/connectionmanager/actions/getprotocolinfo.rs @@ -0,0 +1,9 @@ +use crate::connectionmanager::variables::{SOURCEPROTOCOLINFO, SINKPROTOCOLINFO}; +use pmoupnp::define_action; + +define_action! { + pub static GETPROTOCOLINFO = "GetProtocolInfo" { + out "Source" => SOURCEPROTOCOLINFO, + out "Sink" => SINKPROTOCOLINFO, + } +} diff --git a/pmomediaserver/src/connectionmanager/actions/mod.rs b/pmomediaserver/src/connectionmanager/actions/mod.rs new file mode 100644 index 00000000..b9ccd37b --- /dev/null +++ b/pmomediaserver/src/connectionmanager/actions/mod.rs @@ -0,0 +1,7 @@ +mod getprotocolinfo; +mod getcurrentconnectionids; +mod getcurrentconnectioninfo; + +pub use getprotocolinfo::GETPROTOCOLINFO; +pub use getcurrentconnectionids::GETCURRENTCONNECTIONIDS; +pub use getcurrentconnectioninfo::GETCURRENTCONNECTIONINFO; diff --git a/pmomediaserver/src/connectionmanager/mod.rs b/pmomediaserver/src/connectionmanager/mod.rs new file mode 100644 index 00000000..b0fee00d --- /dev/null +++ b/pmomediaserver/src/connectionmanager/mod.rs @@ -0,0 +1,99 @@ +//! # ConnectionManager Service - Service de gestion des connexions UPnP +//! +//! Ce module implémente le service ConnectionManager:1 selon la spécification UPnP AV. +//! Le service ConnectionManager gère les connexions entre MediaServer et MediaRenderer, +//! et expose les protocoles et formats supportés. +//! +//! ## Fonctionnalités +//! +//! Le service ConnectionManager permet : +//! - **Énumération des protocoles** : GetProtocolInfo +//! - **Gestion des connexions** : GetCurrentConnectionIDs, GetCurrentConnectionInfo +//! - Support des formats audio (MP3, FLAC, WAV, etc.) +//! +//! ## Conformité UPnP +//! +//! Cette implémentation suit la spécification **UPnP ConnectionManager:1 Service Template**. +//! Toutes les actions obligatoires (Required) sont implémentées : +//! +//! - ✅ GetProtocolInfo +//! - ✅ GetCurrentConnectionIDs +//! - ✅ GetCurrentConnectionInfo +//! +//! ## Variables d'état +//! +//! Le service expose les variables d'état conformes à la spécification : +//! +//! ### Informations de protocole +//! - [`SOURCEPROTOCOLINFO`] : Protocoles source supportés (http-get:*:audio/mpeg:*, etc.) +//! - [`SINKPROTOCOLINFO`] : Protocoles sink (vide pour un server) +//! - [`CURRENTCONNECTIONIDS`] : IDs des connexions actives +//! +//! ### Arguments +//! - [`A_ARG_TYPE_CONNECTIONID`] : ID de connexion +//! - [`A_ARG_TYPE_CONNECTIONSTATUS`] : Statut de connexion +//! - [`A_ARG_TYPE_DIRECTION`] : Direction (Input/Output) +//! - [`A_ARG_TYPE_PROTOCOLINFO`] : Information de protocole +//! - [`A_ARG_TYPE_RCSID`] : ID RenderingControl +//! - [`A_ARG_TYPE_AVTRANSPORTID`] : ID AVTransport +//! +//! ## Différences avec MediaRenderer +//! +//! Pour un MediaServer : +//! - **SourceProtocolInfo** : Liste des protocoles que le server peut **fournir** (Output) +//! - **SinkProtocolInfo** : Vide (le server ne **consomme** pas de contenu) +//! +//! Pour un MediaRenderer (inverse) : +//! - **SourceProtocolInfo** : Vide (le renderer ne fournit pas de contenu) +//! - **SinkProtocolInfo** : Liste des protocoles que le renderer peut **consommer** (Input) +//! +//! ## Examples +//! +//! ```rust +//! use pmomediaserver::connectionmanager::CONNECTIONMANAGER; +//! +//! // Accéder au service +//! let service = &*CONNECTIONMANAGER; +//! println!("Service: {}", service.name()); +//! println!("Type: {}", service.service_type()); +//! ``` +//! +//! ## Références +//! +//! - [UPnP ConnectionManager:1 Service Template](https://upnp.org/specs/av/UPnP-av-ConnectionManager-v1-Service.pdf) +//! - [UPnP AV Architecture](https://upnp.org/specs/av/) + +use pmoupnp::define_service; + +pub mod variables; +pub mod actions; + +use actions::{GETCURRENTCONNECTIONIDS, GETCURRENTCONNECTIONINFO, GETPROTOCOLINFO}; +use 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 +}; + +// Service ConnectionManager:1 conforme à la spécification UPnP AV pour MediaServer +// Voir la documentation du module pour plus de détails +define_service! { + pub static CONNECTIONMANAGER = "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, + ], + actions: [ + GETCURRENTCONNECTIONIDS, + GETCURRENTCONNECTIONINFO, + GETPROTOCOLINFO, + ] + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/a_arg_type_avtransportid.rs b/pmomediaserver/src/connectionmanager/variables/a_arg_type_avtransportid.rs new file mode 100644 index 00000000..eab0306d --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/a_arg_type_avtransportid.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_AVTRANSPORTID: I4 = "A_ARG_TYPE_AVTransportID" { + evented: false, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionid.rs b/pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionid.rs new file mode 100644 index 00000000..18e1e733 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionid.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_CONNECTIONID: I4 = "A_ARG_TYPE_ConnectionID" { + evented: false, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionstatus.rs b/pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionstatus.rs new file mode 100644 index 00000000..ed6430c8 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/a_arg_type_connectionstatus.rs @@ -0,0 +1,8 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_CONNECTIONSTATUS: String = "A_ARG_TYPE_ConnectionStatus" { + allowed: ["OK", "ContentFormatMismatch", "InsufficientBandwidth", "UnreliableChannel", "Unknown"], + evented: false, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/a_arg_type_direction.rs b/pmomediaserver/src/connectionmanager/variables/a_arg_type_direction.rs new file mode 100644 index 00000000..ec5d1c23 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/a_arg_type_direction.rs @@ -0,0 +1,8 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_DIRECTION: String = "A_ARG_TYPE_Direction" { + allowed: ["Input", "Output"], + evented: false, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/a_arg_type_protocolinfo.rs b/pmomediaserver/src/connectionmanager/variables/a_arg_type_protocolinfo.rs new file mode 100644 index 00000000..4c337eb4 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/a_arg_type_protocolinfo.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_PROTOCOLINFO: String = "A_ARG_TYPE_ProtocolInfo" { + evented: false, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/a_arg_type_rcsid.rs b/pmomediaserver/src/connectionmanager/variables/a_arg_type_rcsid.rs new file mode 100644 index 00000000..b4cd87ab --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/a_arg_type_rcsid.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_RCSID: I4 = "A_ARG_TYPE_RcsID" { + evented: false, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/currentconnectionids.rs b/pmomediaserver/src/connectionmanager/variables/currentconnectionids.rs new file mode 100644 index 00000000..83929ea6 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/currentconnectionids.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static CURRENTCONNECTIONIDS: String = "CurrentConnectionIDs" { + evented: true, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/mod.rs b/pmomediaserver/src/connectionmanager/variables/mod.rs new file mode 100644 index 00000000..f2c1ff30 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/mod.rs @@ -0,0 +1,19 @@ +mod a_arg_type_connectionid; +mod a_arg_type_connectionstatus; +mod a_arg_type_direction; +mod a_arg_type_protocolinfo; +mod a_arg_type_rcsid; +mod a_arg_type_avtransportid; +mod currentconnectionids; +mod sourceprotocolinfo; +mod sinkprotocolinfo; + +pub use a_arg_type_connectionid::A_ARG_TYPE_CONNECTIONID; +pub use a_arg_type_connectionstatus::A_ARG_TYPE_CONNECTIONSTATUS; +pub use a_arg_type_direction::A_ARG_TYPE_DIRECTION; +pub use a_arg_type_protocolinfo::A_ARG_TYPE_PROTOCOLINFO; +pub use a_arg_type_rcsid::A_ARG_TYPE_RCSID; +pub use a_arg_type_avtransportid::A_ARG_TYPE_AVTRANSPORTID; +pub use currentconnectionids::CURRENTCONNECTIONIDS; +pub use sourceprotocolinfo::SOURCEPROTOCOLINFO; +pub use sinkprotocolinfo::SINKPROTOCOLINFO; diff --git a/pmomediaserver/src/connectionmanager/variables/sinkprotocolinfo.rs b/pmomediaserver/src/connectionmanager/variables/sinkprotocolinfo.rs new file mode 100644 index 00000000..e698fc83 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/sinkprotocolinfo.rs @@ -0,0 +1,8 @@ +use pmoupnp::define_variable; + +// Pour un MediaServer, SinkProtocolInfo est vide (le server ne consomme pas de contenu) +define_variable! { + pub static SINKPROTOCOLINFO: String = "SinkProtocolInfo" { + evented: true, + } +} diff --git a/pmomediaserver/src/connectionmanager/variables/sourceprotocolinfo.rs b/pmomediaserver/src/connectionmanager/variables/sourceprotocolinfo.rs new file mode 100644 index 00000000..07721fa3 --- /dev/null +++ b/pmomediaserver/src/connectionmanager/variables/sourceprotocolinfo.rs @@ -0,0 +1,8 @@ +use pmoupnp::define_variable; + +// Pour un MediaServer, SourceProtocolInfo liste les protocoles qu'il peut servir +define_variable! { + pub static SOURCEPROTOCOLINFO: String = "SourceProtocolInfo" { + evented: true, + } +} diff --git a/pmomediaserver/src/contentdirectory/actions/browse.rs b/pmomediaserver/src/contentdirectory/actions/browse.rs new file mode 100644 index 00000000..0177e20b --- /dev/null +++ b/pmomediaserver/src/contentdirectory/actions/browse.rs @@ -0,0 +1,21 @@ +use crate::contentdirectory::variables::{ + A_ARG_TYPE_OBJECTID, A_ARG_TYPE_BROWSEFLAG, A_ARG_TYPE_FILTER, + A_ARG_TYPE_SORTCRITERIA, A_ARG_TYPE_INDEX, A_ARG_TYPE_COUNT, + A_ARG_TYPE_RESULT, A_ARG_TYPE_UPDATEID, +}; +use pmoupnp::define_action; + +define_action! { + pub static BROWSE = "Browse" { + in "ObjectID" => A_ARG_TYPE_OBJECTID, + in "BrowseFlag" => A_ARG_TYPE_BROWSEFLAG, + in "Filter" => A_ARG_TYPE_FILTER, + in "StartingIndex" => A_ARG_TYPE_INDEX, + in "RequestedCount" => A_ARG_TYPE_COUNT, + in "SortCriteria" => A_ARG_TYPE_SORTCRITERIA, + out "Result" => A_ARG_TYPE_RESULT, + out "NumberReturned" => A_ARG_TYPE_COUNT, + out "TotalMatches" => A_ARG_TYPE_COUNT, + out "UpdateID" => A_ARG_TYPE_UPDATEID, + } +} diff --git a/pmomediaserver/src/contentdirectory/actions/getsearchcapabilities.rs b/pmomediaserver/src/contentdirectory/actions/getsearchcapabilities.rs new file mode 100644 index 00000000..9da4d5d0 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/actions/getsearchcapabilities.rs @@ -0,0 +1,8 @@ +use crate::contentdirectory::variables::SEARCHCAPABILITIES; +use pmoupnp::define_action; + +define_action! { + pub static GETSEARCHCAPABILITIES = "GetSearchCapabilities" { + out "SearchCaps" => SEARCHCAPABILITIES, + } +} diff --git a/pmomediaserver/src/contentdirectory/actions/getsortcapabilities.rs b/pmomediaserver/src/contentdirectory/actions/getsortcapabilities.rs new file mode 100644 index 00000000..dbad6c74 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/actions/getsortcapabilities.rs @@ -0,0 +1,8 @@ +use crate::contentdirectory::variables::SORTCAPABILITIES; +use pmoupnp::define_action; + +define_action! { + pub static GETSORTCAPABILITIES = "GetSortCapabilities" { + out "SortCaps" => SORTCAPABILITIES, + } +} diff --git a/pmomediaserver/src/contentdirectory/actions/getsystemupdateid.rs b/pmomediaserver/src/contentdirectory/actions/getsystemupdateid.rs new file mode 100644 index 00000000..76db9134 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/actions/getsystemupdateid.rs @@ -0,0 +1,8 @@ +use crate::contentdirectory::variables::SYSTEMUPDATEID; +use pmoupnp::define_action; + +define_action! { + pub static GETSYSTEMUPDATEID = "GetSystemUpdateID" { + out "Id" => SYSTEMUPDATEID, + } +} diff --git a/pmomediaserver/src/contentdirectory/actions/mod.rs b/pmomediaserver/src/contentdirectory/actions/mod.rs new file mode 100644 index 00000000..32d95832 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/actions/mod.rs @@ -0,0 +1,11 @@ +mod browse; +mod search; +mod getsearchcapabilities; +mod getsortcapabilities; +mod getsystemupdateid; + +pub use browse::BROWSE; +pub use search::SEARCH; +pub use getsearchcapabilities::GETSEARCHCAPABILITIES; +pub use getsortcapabilities::GETSORTCAPABILITIES; +pub use getsystemupdateid::GETSYSTEMUPDATEID; diff --git a/pmomediaserver/src/contentdirectory/actions/search.rs b/pmomediaserver/src/contentdirectory/actions/search.rs new file mode 100644 index 00000000..ea1c0159 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/actions/search.rs @@ -0,0 +1,21 @@ +use crate::contentdirectory::variables::{ + A_ARG_TYPE_OBJECTID, A_ARG_TYPE_SEARCHCRITERIA, A_ARG_TYPE_FILTER, + A_ARG_TYPE_SORTCRITERIA, A_ARG_TYPE_INDEX, A_ARG_TYPE_COUNT, + A_ARG_TYPE_RESULT, A_ARG_TYPE_UPDATEID, +}; +use pmoupnp::define_action; + +define_action! { + pub static SEARCH = "Search" { + in "ContainerID" => A_ARG_TYPE_OBJECTID, + in "SearchCriteria" => A_ARG_TYPE_SEARCHCRITERIA, + in "Filter" => A_ARG_TYPE_FILTER, + in "StartingIndex" => A_ARG_TYPE_INDEX, + in "RequestedCount" => A_ARG_TYPE_COUNT, + in "SortCriteria" => A_ARG_TYPE_SORTCRITERIA, + out "Result" => A_ARG_TYPE_RESULT, + out "NumberReturned" => A_ARG_TYPE_COUNT, + out "TotalMatches" => A_ARG_TYPE_COUNT, + out "UpdateID" => A_ARG_TYPE_UPDATEID, + } +} diff --git a/pmomediaserver/src/contentdirectory/mod.rs b/pmomediaserver/src/contentdirectory/mod.rs new file mode 100644 index 00000000..4ac8a3fe --- /dev/null +++ b/pmomediaserver/src/contentdirectory/mod.rs @@ -0,0 +1,116 @@ +//! # ContentDirectory Service - Service de gestion du contenu UPnP +//! +//! Ce module implémente le service ContentDirectory:1 selon la spécification UPnP AV. +//! Le service ContentDirectory permet de naviguer et rechercher dans une bibliothèque +//! de contenu musical. +//! +//! ## Fonctionnalités +//! +//! Le service ContentDirectory permet : +//! - **Navigation** : Browse pour parcourir la hiérarchie de contenu +//! - **Recherche** : Search pour rechercher du contenu selon des critères +//! - **Capacités** : GetSearchCapabilities, GetSortCapabilities +//! - **Synchronisation** : GetSystemUpdateID pour détecter les changements +//! +//! ## Conformité UPnP +//! +//! Cette implémentation suit la spécification **UPnP ContentDirectory:1 Service Template**. +//! Toutes les actions obligatoires (Required) sont implémentées : +//! +//! - ✅ Browse +//! - ✅ GetSearchCapabilities +//! - ✅ GetSortCapabilities +//! - ✅ GetSystemUpdateID +//! +//! Et certaines actions optionnelles : +//! - ✅ Search +//! +//! ## Variables d'état +//! +//! Le service expose les variables d'état conformes à la spécification : +//! +//! ### Variables principales +//! - [`SYSTEMUPDATEID`] : ID de mise à jour du système (évènementiel) +//! - [`SEARCHCAPABILITIES`] : Capacités de recherche supportées +//! - [`SORTCAPABILITIES`] : Capacités de tri supportées +//! +//! ### Arguments d'action +//! - [`A_ARG_TYPE_OBJECTID`] : ID d'un objet (container ou item) +//! - [`A_ARG_TYPE_BROWSEFLAG`] : Type de browsing (Metadata ou DirectChildren) +//! - [`A_ARG_TYPE_FILTER`] : Filtre de propriétés à retourner +//! - [`A_ARG_TYPE_SORTCRITERIA`] : Critères de tri +//! - [`A_ARG_TYPE_SEARCHCRITERIA`] : Critères de recherche +//! - [`A_ARG_TYPE_INDEX`] : Index de départ +//! - [`A_ARG_TYPE_COUNT`] : Nombre d'éléments +//! - [`A_ARG_TYPE_UPDATEID`] : ID de mise à jour +//! - [`A_ARG_TYPE_RESULT`] : Résultat au format DIDL-Lite +//! +//! ## Examples +//! +//! ```rust +//! use pmomediaserver::contentdirectory::CONTENTDIRECTORY; +//! use pmoupnp::UpnpTyped; +//! +//! // Accéder au service +//! let service = &*CONTENTDIRECTORY; +//! println!("Service: {}", service.name()); +//! println!("Type: {}", service.service_type()); +//! +//! // Lister les actions disponibles +//! for action in service.actions() { +//! println!(" Action: {}", action.get_name()); +//! } +//! +//! // Lister les variables d'état +//! for variable in service.variables() { +//! println!(" Variable: {}", variable.get_name()); +//! } +//! ``` +//! +//! ## Références +//! +//! - [UPnP ContentDirectory:1 Service Template](https://upnp.org/specs/av/UPnP-av-ContentDirectory-v1-Service.pdf) +//! - [UPnP AV Architecture](https://upnp.org/specs/av/) + +use pmoupnp::define_service; + +pub mod variables; +pub mod actions; + +use actions::{ + BROWSE, SEARCH, GETSEARCHCAPABILITIES, GETSORTCAPABILITIES, GETSYSTEMUPDATEID +}; +use variables::{ + A_ARG_TYPE_OBJECTID, A_ARG_TYPE_BROWSEFLAG, A_ARG_TYPE_FILTER, + A_ARG_TYPE_SORTCRITERIA, A_ARG_TYPE_INDEX, A_ARG_TYPE_COUNT, + A_ARG_TYPE_UPDATEID, A_ARG_TYPE_RESULT, A_ARG_TYPE_SEARCHCRITERIA, + SEARCHCAPABILITIES, SORTCAPABILITIES, SYSTEMUPDATEID +}; + +// Service ContentDirectory:1 conforme à la spécification UPnP AV pour MediaServer +// Voir la documentation du module pour plus de détails +define_service! { + pub static CONTENTDIRECTORY = "ContentDirectory" { + variables: [ + A_ARG_TYPE_OBJECTID, + A_ARG_TYPE_BROWSEFLAG, + A_ARG_TYPE_FILTER, + A_ARG_TYPE_SORTCRITERIA, + A_ARG_TYPE_INDEX, + A_ARG_TYPE_COUNT, + A_ARG_TYPE_UPDATEID, + A_ARG_TYPE_RESULT, + A_ARG_TYPE_SEARCHCRITERIA, + SEARCHCAPABILITIES, + SORTCAPABILITIES, + SYSTEMUPDATEID, + ], + actions: [ + BROWSE, + SEARCH, + GETSEARCHCAPABILITIES, + GETSORTCAPABILITIES, + GETSYSTEMUPDATEID, + ] + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_browseflag.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_browseflag.rs new file mode 100644 index 00000000..145fc95a --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_browseflag.rs @@ -0,0 +1,8 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_BROWSEFLAG: String = "A_ARG_TYPE_BrowseFlag" { + allowed: ["BrowseMetadata", "BrowseDirectChildren"], + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_count.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_count.rs new file mode 100644 index 00000000..35e122da --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_count.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_COUNT: UI4 = "A_ARG_TYPE_Count" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_filter.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_filter.rs new file mode 100644 index 00000000..0ef8dafa --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_filter.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_FILTER: String = "A_ARG_TYPE_Filter" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_index.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_index.rs new file mode 100644 index 00000000..7ae6419c --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_index.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_INDEX: UI4 = "A_ARG_TYPE_Index" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_objectid.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_objectid.rs new file mode 100644 index 00000000..40d06426 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_objectid.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_OBJECTID: String = "A_ARG_TYPE_ObjectID" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_result.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_result.rs new file mode 100644 index 00000000..fa79129a --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_result.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_RESULT: String = "A_ARG_TYPE_Result" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_searchcriteria.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_searchcriteria.rs new file mode 100644 index 00000000..95be09c5 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_searchcriteria.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_SEARCHCRITERIA: String = "A_ARG_TYPE_SearchCriteria" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_sortcriteria.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_sortcriteria.rs new file mode 100644 index 00000000..f7654bc0 --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_sortcriteria.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_SORTCRITERIA: String = "A_ARG_TYPE_SortCriteria" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/a_arg_type_updateid.rs b/pmomediaserver/src/contentdirectory/variables/a_arg_type_updateid.rs new file mode 100644 index 00000000..fab6134d --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/a_arg_type_updateid.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static A_ARG_TYPE_UPDATEID: UI4 = "A_ARG_TYPE_UpdateID" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/mod.rs b/pmomediaserver/src/contentdirectory/variables/mod.rs new file mode 100644 index 00000000..e1a6df5b --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/mod.rs @@ -0,0 +1,25 @@ +mod a_arg_type_objectid; +mod a_arg_type_browseflag; +mod a_arg_type_filter; +mod a_arg_type_sortcriteria; +mod a_arg_type_index; +mod a_arg_type_count; +mod a_arg_type_updateid; +mod a_arg_type_result; +mod a_arg_type_searchcriteria; +mod searchcapabilities; +mod sortcapabilities; +mod systemupdateid; + +pub use a_arg_type_objectid::A_ARG_TYPE_OBJECTID; +pub use a_arg_type_browseflag::A_ARG_TYPE_BROWSEFLAG; +pub use a_arg_type_filter::A_ARG_TYPE_FILTER; +pub use a_arg_type_sortcriteria::A_ARG_TYPE_SORTCRITERIA; +pub use a_arg_type_index::A_ARG_TYPE_INDEX; +pub use a_arg_type_count::A_ARG_TYPE_COUNT; +pub use a_arg_type_updateid::A_ARG_TYPE_UPDATEID; +pub use a_arg_type_result::A_ARG_TYPE_RESULT; +pub use a_arg_type_searchcriteria::A_ARG_TYPE_SEARCHCRITERIA; +pub use searchcapabilities::SEARCHCAPABILITIES; +pub use sortcapabilities::SORTCAPABILITIES; +pub use systemupdateid::SYSTEMUPDATEID; diff --git a/pmomediaserver/src/contentdirectory/variables/searchcapabilities.rs b/pmomediaserver/src/contentdirectory/variables/searchcapabilities.rs new file mode 100644 index 00000000..db90dd6c --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/searchcapabilities.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static SEARCHCAPABILITIES: String = "SearchCapabilities" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/sortcapabilities.rs b/pmomediaserver/src/contentdirectory/variables/sortcapabilities.rs new file mode 100644 index 00000000..31e8e46b --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/sortcapabilities.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static SORTCAPABILITIES: String = "SortCapabilities" { + evented: false, + } +} diff --git a/pmomediaserver/src/contentdirectory/variables/systemupdateid.rs b/pmomediaserver/src/contentdirectory/variables/systemupdateid.rs new file mode 100644 index 00000000..76ce96bd --- /dev/null +++ b/pmomediaserver/src/contentdirectory/variables/systemupdateid.rs @@ -0,0 +1,7 @@ +use pmoupnp::define_variable; + +define_variable! { + pub static SYSTEMUPDATEID: UI4 = "SystemUpdateID" { + evented: true, + } +} diff --git a/pmomediaserver/src/device.rs b/pmomediaserver/src/device.rs new file mode 100644 index 00000000..7ca6da16 --- /dev/null +++ b/pmomediaserver/src/device.rs @@ -0,0 +1,62 @@ +//! Définition du device MediaServer. + +use once_cell::sync::Lazy; +use std::sync::Arc; + +use pmoupnp::devices::Device; +use crate::{ + contentdirectory::CONTENTDIRECTORY, + connectionmanager::CONNECTIONMANAGER, +}; + +/// Device MediaServer UPnP. +/// +/// MediaServer conforme UPnP AV Architecture 1.0. +/// +/// # Services inclus +/// +/// - **ContentDirectory:1** : Gestion du contenu et navigation +/// - **ConnectionManager:1** : Gestion des connexions +/// +/// # Spécifications +/// +/// - Device Type : `urn:schemas-upnp-org:device:MediaServer:1` +/// - Version : 1 +/// - Manufacturer : PMOMusic +/// - Model : PMOMusic Media Server +/// +/// # Exemple +/// +/// ```ignore +/// use pmomediaserver::MEDIA_SERVER; +/// use pmoupnp::UpnpModel; +/// +/// // Créer une instance du server +/// let server_instance = MEDIA_SERVER.create_instance(); +/// +/// // Accéder aux services +/// if let Some(content_directory) = server_instance.get_service("ContentDirectory") { +/// // Gérer le contenu... +/// } +/// ``` +pub static MEDIA_SERVER: Lazy> = Lazy::new(|| { + let mut device = Device::new( + "MediaServer".to_string(), + "MediaServer".to_string(), + "PMOMusic Media Server".to_string(), + ); + + device.set_manufacturer("PMOMusic".to_string()); + device.set_model_name("PMOMusic Media Server".to_string()); + device.set_model_description("UPnP AV MediaServer for audio streaming".to_string()); + device.set_udn_prefix("pmomusic".to_string()); + + // Ajouter les deux services obligatoires + device.add_service(Arc::clone(&CONTENTDIRECTORY)) + .expect("Failed to add ContentDirectory service"); + + device.add_service(Arc::clone(&CONNECTIONMANAGER)) + .expect("Failed to add ConnectionManager service"); + + Arc::new(device) +}); diff --git a/pmomediaserver/src/lib.rs b/pmomediaserver/src/lib.rs new file mode 100644 index 00000000..bea3bdca --- /dev/null +++ b/pmomediaserver/src/lib.rs @@ -0,0 +1,33 @@ +//! Module MediaServer UPnP. +//! +//! Ce module implémente un MediaServer UPnP conforme à la spécification +//! UPnP AV Architecture. Un MediaServer permet d'exposer et de servir du contenu audio +//! à des clients UPnP (MediaRenderer). +//! +//! # Architecture +//! +//! Le MediaServer est composé de deux services obligatoires : +//! +//! - **ContentDirectory** : Gestion du contenu et navigation dans la bibliothèque musicale +//! - **ConnectionManager** : Gestion des connexions et des protocoles supportés +//! +//! # Device UPnP +//! +//! - Type : `urn:schemas-upnp-org:device:MediaServer:1` +//! - Services : ContentDirectory:1, ConnectionManager:1 +//! +//! # Utilisation +//! +//! ```ignore +//! use pmomediaserver::MEDIA_SERVER; +//! +//! // Le device est déjà configuré avec tous ses services +//! let server = MEDIA_SERVER.clone(); +//! let instance = server.create_instance(); +//! ``` + +pub mod contentdirectory; +pub mod connectionmanager; +pub mod device; + +pub use device::MEDIA_SERVER;