Autorise la configuration des noms UPNP.

This commit is contained in:
2025-12-28 11:36:20 +01:00
parent 876b1e0147
commit 0c64ed1a9a
8 changed files with 126 additions and 11 deletions

31
.claude/CLAUDE.md Normal file
View File

@@ -0,0 +1,31 @@
# PMOMusic Project Configuration
## Version Control
Ce projet utilise **Jujutsu (jj)** pour le contrôle de version, PAS git.
- Utiliser les commandes `jj` au lieu des commandes `git`
- Bookmark principal : `main`
- Ne jamais suggérer de commandes git
## Environnement
Le PATH et les variables d'environnement sont configurés dans `.claude-env` à la racine du projet.
## Configuration de l'application
- Fichier de configuration principal : `.pmomusic/config.yaml`
- Configuration UPNP personnalisable pour différencier les instances en développement
## Développement
Pendant le développement, plusieurs serveurs PMOMusic peuvent tourner en parallèle. Utiliser la configuration UPNP dans `.pmomusic/config.yaml` pour différencier les instances :
```yaml
host:
upnp:
manufacturer: "PMOMusic-Dev1"
udn_prefix: "pmomusic-dev1"
model_name_prefix: "PMOMusic-Dev1"
friendly_name_prefix: "PMOMusic-Dev1"
```
## Architecture
- Projet Rust multi-crates avec workspaces
- Crates principales : pmoupnp, pmomediaserver, pmomediarenderer, pmoconfig
- Pattern d'extension de configuration via traits (voir pmocache/src/config_ext.rs)

1
Cargo.lock generated
View File

@@ -4021,6 +4021,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
"serde_yaml",
"socket2 0.5.10",
"thiserror 2.0.17",
"tokio",

View File

@@ -1,5 +1,10 @@
host:
http_port: "8080"
upnp:
manufacturer: "PMOMusic"
udn_prefix: "pmomusic"
model_name_prefix: "PMOMusic"
friendly_name_prefix: "PMOMusic"
cover_cache:
directory: "cache_covers"
size: 2000

View File

@@ -41,16 +41,13 @@ use pmoupnp::devices::Device;
/// }
/// ```
pub static MEDIA_RENDERER: Lazy<Arc<Device>> = Lazy::new(|| {
let mut device = Device::new(
let mut device = Device::new_from_config(
"PMO_MediaRenderer".to_string(),
"MediaRenderer".to_string(),
"PMOMusic Audio Renderer".to_string(),
"Audio Renderer".to_string(),
);
device.set_manufacturer("PMOMusic".to_string());
device.set_model_name("PMOMusic Audio Renderer".to_string());
device.set_model_description("UPnP AV MediaRenderer for audio streaming".to_string());
device.set_udn_prefix("pmomusic".to_string());
// Ajouter les trois services obligatoires
device

View File

@@ -37,16 +37,13 @@ use pmoupnp::devices::Device;
/// }
/// ```
pub static MEDIA_SERVER: Lazy<Arc<Device>> = Lazy::new(|| {
let mut device = Device::new(
let mut device = Device::new_from_config(
"PMO_MediaServer".to_string(),
"MediaServer".to_string(),
"PMOMusic Media Server".to_string(),
"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

View File

@@ -36,6 +36,7 @@ reqwest = "0.12.23"
utoipa = { version = "5.3", features = ["axum_extras"] }
socket2 = "0.5"
get_if_addrs = "0.5"
serde_yaml = "0.9"
[features]
default = ["server"]

View File

@@ -122,6 +122,84 @@ impl Device {
}
}
/// Crée un nouveau modèle de device en utilisant la configuration.
///
/// Cette factory method charge les préfixes depuis pmoconfig et construit
/// automatiquement les noms finaux en combinant les préfixes avec les suffixes.
///
/// # Arguments
///
/// * `name` - Nom unique du device
/// * `device_type` - Type UPnP du device (ex: "MediaServer", "MediaRenderer")
/// * `friendly_name_suffix` - Suffixe pour le nom convivial (sera combiné avec le préfixe)
///
/// # Examples
///
/// ```ignore
/// use pmoupnp::devices::Device;
///
/// let device = Device::new_from_config(
/// "PMO_MediaServer".to_string(),
/// "MediaServer".to_string(),
/// "Media Server".to_string(),
/// );
/// // Avec config par défaut :
/// // - manufacturer = "PMOMusic"
/// // - udn_prefix = "pmomusic"
/// // - model_name = "PMOMusic Media Server"
/// // - friendly_name = "PMOMusic Media Server"
/// ```
pub fn new_from_config(
name: String,
device_type: String,
friendly_name_suffix: String,
) -> Self {
use crate::config_ext::UpnpConfigExt;
let config = pmoconfig::get_config();
// Charger les valeurs depuis la config (avec fallback aux defaults)
let manufacturer = config
.get_upnp_manufacturer()
.unwrap_or_else(|_| "PMOMusic".to_string());
let udn_prefix = config
.get_upnp_udn_prefix()
.unwrap_or_else(|_| "pmomusic".to_string());
let model_name_prefix = config
.get_upnp_model_name_prefix()
.unwrap_or_else(|_| "PMOMusic".to_string());
let friendly_name_prefix = config
.get_upnp_friendly_name_prefix()
.unwrap_or_else(|_| "PMOMusic".to_string());
// Construire les noms finaux
let model_name = format!("{} {}", model_name_prefix, device_type);
let friendly_name = format!("{} {}", friendly_name_prefix, friendly_name_suffix);
Self {
object: UpnpObjectType {
name: name.clone(),
object_type: "Device".to_string(),
},
device_type,
version: 1,
friendly_name,
manufacturer,
manufacturer_url: None,
model_description: None,
model_name,
model_number: None,
model_url: None,
serial_number: None,
udn_prefix,
upc: None,
icon_url: None,
presentation_url: None,
services: RwLock::new(HashMap::new()),
devices: RwLock::new(HashMap::new()),
}
}
/// Retourne le type de device UPnP.
///
/// Format: `urn:schemas-upnp-org:device:{type}:{version}`

View File

@@ -261,7 +261,12 @@ impl UpnpServerExt for Server {
if self.ssdp_enabled() {
let ssdp_opt = SSDP_SERVER.read().unwrap();
if let Some(ref ssdp) = *ssdp_opt {
let ssdp_device = di.to_ssdp_device("PMOMusic", "1.0");
use crate::config_ext::UpnpConfigExt;
let config = pmoconfig::get_config();
let manufacturer = config
.get_upnp_manufacturer()
.unwrap_or_else(|_| "PMOMusic".to_string());
let ssdp_device = di.to_ssdp_device(&manufacturer, "1.0");
ssdp.add_device(ssdp_device);
info!("✅ SSDP announcement for {}", di.udn());
}