7.2 KiB
Radio Paradise UPnP Media Server - Plan d'implémentation
État actuel
Le squelette du module mediaserver a été créé mais ne compile pas car il n'utilise pas correctement l'API de pmoupnp.
Architecture pmoupnp
Après étude du code existant (notamment pmoupnp/src/mediarenderer/connectionmanager), voici comment pmoupnp fonctionne :
1. Macros à utiliser
Pmoupnp fournit 3 macros essentielles :
// Définir une variable d'état
define_variable! {
pub static VAR_NAME: Type = "VariableName" {
evented: true, // optionnel
default: "value", // optionnel
allowed: ["val1", "val2"], // optionnel
}
}
// Définir une action
define_action! {
pub static ACTION_NAME = "ActionName" {
in "ParamName" => VARIABLE_REF,
out "ResultName" => RESULT_VAR,
}
}
// Définir un service
define_service! {
pub static SERVICE_NAME = "ServiceName" {
variables: [VAR1, VAR2, ...],
actions: [ACTION1, ACTION2, ...],
}
}
2. Structure des fichiers
Pour chaque service, créer cette structure :
src/mediaserver/
├── content_directory/
│ ├── mod.rs # Utilise define_service!
│ ├── variables/
│ │ ├── mod.rs
│ │ ├── system_update_id.rs
│ │ ├── container_update_ids.rs
│ │ ├── a_arg_type_objectid.rs
│ │ └── ... (une variable par fichier)
│ └── actions/
│ ├── mod.rs
│ ├── browse.rs # Utilise define_action!
│ ├── get_search_capabilities.rs
│ └── ...
└── connection_manager/
└── ... (même structure)
3. Implémentation de Browse (action complexe)
L'action Browse nécessite un handler custom pour générer le DIDL-Lite dynamiquement :
// Dans content_directory/actions/browse.rs
use crate::define_action;
use crate::actions::ActionHandler;
use pmoupnp::action_handler;
// Définir les variables d'argument
use super::super::variables::{
A_ARG_TYPE_OBJECTID,
A_ARG_TYPE_BROWSEFLAG,
A_ARG_TYPE_FILTER,
// ... etc
};
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,
}
with handler action_handler!(|instance, data| {
// Accéder au client Radio Paradise depuis le contexte
// Générer le DIDL-Lite
// Retourner les résultats
Ok(())
})
}
4. Contexte pour le client Radio Paradise
Le problème : comment passer Arc<RwLock<RadioParadiseClient>> aux handlers ?
Solution : Utiliser le DeviceInstance pour stocker le client :
// Dans server.rs
// Créer une structure qui wrappe le client
struct RadioParadiseContext {
client: Arc<RwLock<RadioParadiseClient>>,
}
// L'attacher au DeviceInstance via son contexte
// (à voir comment pmoupnp gère le contexte custom)
Ou alternative : utiliser un registre global thread-safe comme DEVICE_REGISTRY dans pmoupnp.
5. Intégration pmodidl
Pour générer le DIDL-Lite, il faut utiliser pmodidl correctement :
// Les types corrects sont :
use pmodidl::{Container, Item, Object};
// Pas DIDLObject, DIDLContainer, etc.
let mut container = Container::new();
container.set_id("0".to_string());
container.set_parent_id("-1".to_string());
container.set_title("Radio Paradise".to_string());
// Sérialiser en XML DIDL-Lite
let didl_xml = container.to_didl();
6. Intégration pmoserver
Le ServerBuilder de pmoserver prend 3 arguments :
let server = pmoserver::ServerBuilder::new(
"RadioParadise", // name
"http://localhost:8080", // base_url
8080 // port
).build()?;
Pas de méthode with_port() - le port est dans le constructeur.
7. Méthode Device::set_udn
N'existe pas ! À la place :
device.set_udn_prefix("uuid:");
// L'UDN complet sera généré automatiquement
Ou vérifier s'il faut utiliser set_uuid().
Plan d'implémentation corrigé
Phase 1 : ConnectionManager simple (sans handler)
- Créer
src/mediaserver/connection_manager/mod.rs - Créer les variables dans
connection_manager/variables/*.rs - Créer les actions simples dans
connection_manager/actions/*.rs - Utiliser
define_service!pour assembler
Phase 2 : ContentDirectory avec handler
- Créer la structure de fichiers pour ContentDirectory
- Implémenter toutes les variables d'argument
- Implémenter GetSearchCapabilities, GetSortCapabilities (sans handler)
- Implémenter Browse avec un handler custom
- Résoudre le problème du contexte (client RP)
Phase 3 : Serveur principal
- Corriger
server.rspour utiliser la bonne API ServerBuilder - Corriger
Device::set_udn - Instancier les services correctement
- Gérer le cycle de vie du serveur
Phase 4 : Tests
- Tester ConnectionManager seul
- Tester ContentDirectory avec des données mock
- Tester l'intégration complète
- Tester avec un client DLNA réel
Fichiers à modifier
À supprimer/réécrire complètement :
src/mediaserver/content_directory.rs(approche incorrecte)src/mediaserver/connection_manager.rs(approche incorrecte)
À créer :
src/mediaserver/connection_manager/mod.rssrc/mediaserver/connection_manager/variables/mod.rssrc/mediaserver/connection_manager/variables/*.rs(une variable par fichier)src/mediaserver/connection_manager/actions/mod.rssrc/mediaserver/connection_manager/actions/*.rs(une action par fichier)src/mediaserver/content_directory/(même structure)
À corriger :
src/mediaserver/server.rs(API ServerBuilder, Device::set_udn)
Références
Fichiers pmoupnp à étudier :
pmoupnp/src/mediarenderer/connectionmanager/mod.rs- Exemple completpmoupnp/src/mediarenderer/connectionmanager/variables/*.rs- Variablespmoupnp/src/mediarenderer/connectionmanager/actions/*.rs- Actionspmoupnp/src/services/macros.rs- Macro define_service!pmoupnp/src/state_variables/macros.rs- Macro define_variable!pmoupnp/src/actions/macros.rs- Macro define_action!pmoupnp/src/actions/action_handler.rs- ActionHandler trait
Estimation
Temps estimé pour une implémentation correcte :
- Phase 1 (ConnectionManager) : 2-3 heures
- Phase 2 (ContentDirectory) : 4-6 heures
- Phase 3 (Serveur) : 1-2 heures
- Phase 4 (Tests) : 2-3 heures
Total : 9-14 heures de développement
Conclusion
L'implémentation actuelle doit être entièrement réécrite pour utiliser les macros de pmoupnp. C'est un travail substantiel qui nécessite de bien comprendre l'architecture de pmoupnp avant de commencer.
Le squelette créé (structure de modules, Cargo.toml, exemple) est valide et peut être conservé,
mais tout le code des services doit être réécrit en suivant le pattern de mediarenderer/connectionmanager.