feat: système hot-reload bridge audio avec ConfigManager (Phase 2.5)
- ConfigManager: gestionnaire centralisé config avec EventEmitter - AudioBridgeManager: gestion bridge avec auto-reload sur changement config - Intégration dans serveur principal (index.js) - Événements 'audio-device-updated' et 'config-updated' - Reload automatique du bridge sans redémarrer serveur - Mode placeholder pour développement (vrai bridge Phase 3)
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
/**
|
||||
* ConfigManager.js
|
||||
* Gestionnaire centralisé de configuration avec support événements
|
||||
* Phase 2.5
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import YAML from 'yaml';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname } from 'path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const configPath = join(__dirname, 'config.yaml');
|
||||
|
||||
/**
|
||||
* Génère un ID slug à partir d'un nom
|
||||
*/
|
||||
function slugify(text) {
|
||||
return text
|
||||
.toString()
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/\s+/g, '-')
|
||||
.replace(/[^\w-]+/g, '')
|
||||
.replace(/--+/g, '-');
|
||||
}
|
||||
|
||||
class ConfigManager extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
this.config = null;
|
||||
this.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge la configuration depuis le fichier YAML
|
||||
*/
|
||||
load() {
|
||||
try {
|
||||
const configFile = readFileSync(configPath, 'utf8');
|
||||
this.config = YAML.parse(configFile);
|
||||
|
||||
// Générer les IDs pour les groupes et canaux
|
||||
this.config.groups = this.config.groups.map(group => {
|
||||
const groupId = slugify(group.name);
|
||||
return {
|
||||
...group,
|
||||
id: groupId,
|
||||
channels: group.channels ? group.channels.map(channel => ({
|
||||
...channel,
|
||||
id: channel.id || `${groupId}-${slugify(channel.name)}`
|
||||
})) : []
|
||||
};
|
||||
});
|
||||
|
||||
return this.config;
|
||||
} catch (error) {
|
||||
console.error('Erreur chargement configuration:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère la configuration actuelle
|
||||
*/
|
||||
get() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sauvegarde la configuration dans le fichier YAML
|
||||
* Ne sauvegarde PAS les IDs (ils sont générés dynamiquement)
|
||||
*/
|
||||
save(config) {
|
||||
try {
|
||||
// Nettoyer les IDs avant de sauvegarder
|
||||
const cleanConfig = {
|
||||
...config,
|
||||
groups: config.groups.map(group => {
|
||||
const { id, ...groupWithoutId } = group;
|
||||
return {
|
||||
...groupWithoutId,
|
||||
channels: group.channels ? group.channels.map(channel => {
|
||||
const { id: channelId, ...channelWithoutId } = channel;
|
||||
return channelWithoutId;
|
||||
}) : []
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
const yamlContent = YAML.stringify(cleanConfig);
|
||||
writeFileSync(configPath, yamlContent, 'utf8');
|
||||
|
||||
// Recharger pour synchroniser
|
||||
this.load();
|
||||
|
||||
// Émettre événement de changement
|
||||
this.emit('config-updated', this.config);
|
||||
|
||||
return this.config;
|
||||
} catch (error) {
|
||||
console.error('Erreur sauvegarde configuration:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la configuration audio device
|
||||
*/
|
||||
updateAudioDevice(deviceConfig) {
|
||||
if (!this.config.audio) {
|
||||
this.config.audio = {};
|
||||
}
|
||||
|
||||
if (!this.config.audio.device) {
|
||||
this.config.audio.device = {};
|
||||
}
|
||||
|
||||
// Mettre à jour les paramètres fournis
|
||||
if (deviceConfig.inputDeviceId !== undefined) {
|
||||
this.config.audio.device.inputDeviceId = deviceConfig.inputDeviceId;
|
||||
}
|
||||
if (deviceConfig.outputDeviceId !== undefined) {
|
||||
this.config.audio.device.outputDeviceId = deviceConfig.outputDeviceId;
|
||||
}
|
||||
if (deviceConfig.sampleRate !== undefined) {
|
||||
this.config.audio.device.sampleRate = deviceConfig.sampleRate;
|
||||
this.config.audio.sampleRate = deviceConfig.sampleRate; // Sync avec config globale
|
||||
}
|
||||
if (deviceConfig.bufferSize !== undefined) {
|
||||
this.config.audio.device.bufferSize = deviceConfig.bufferSize;
|
||||
}
|
||||
|
||||
this.save(this.config);
|
||||
|
||||
// Émettre événement spécifique
|
||||
this.emit('audio-device-updated', this.config.audio.device);
|
||||
|
||||
return this.config.audio.device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la configuration audio globale
|
||||
*/
|
||||
updateAudioConfig(audioConfig) {
|
||||
if (!this.config.audio) {
|
||||
this.config.audio = {};
|
||||
}
|
||||
|
||||
if (audioConfig.sampleRate !== undefined) {
|
||||
this.config.audio.sampleRate = audioConfig.sampleRate;
|
||||
}
|
||||
if (audioConfig.defaultBitrate !== undefined) {
|
||||
this.config.audio.defaultBitrate = audioConfig.defaultBitrate;
|
||||
}
|
||||
if (audioConfig.jitterBufferMs !== undefined) {
|
||||
this.config.audio.jitterBufferMs = audioConfig.jitterBufferMs;
|
||||
}
|
||||
|
||||
this.save(this.config);
|
||||
|
||||
return this.config.audio;
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton
|
||||
const configManager = new ConfigManager();
|
||||
|
||||
export default configManager;
|
||||
Reference in New Issue
Block a user