refactor: simplifier AudioBridge, filtrer bridge dans PWA, option aucune sortie

- AudioBridge: retire GroupAudioRouter, LiveKitClient, routing per-group
- AudioBridgeManager: génère tokens uniquement pour server_audio_users
- ServerAudioUser: outputChannel null = pas d'émission outputReady
- PWA useLiveKit: filtre les participants role=bridge de la liste
- Electron UI: page Routing sans matrices, noms canaux + server audio users
- config.yaml: nettoyé (pas de section routing)
This commit is contained in:
2026-07-03 14:55:55 +02:00
parent bf960f49bb
commit 06cb6a7dd1
12 changed files with 397 additions and 731 deletions
+46 -6
View File
@@ -454,7 +454,7 @@ app.whenReady().then(async () => {
if (users.find(u => u.name === name)) {
return { success: false, error: `Un utilisateur "${name}" existe déjà` };
}
const user = { name, group, input_channel: parseInt(input_channel), output_channel: parseInt(output_channel) };
const user = { name, group, input_channel: parseInt(input_channel), output_channel: output_channel !== null && output_channel !== '' ? parseInt(output_channel) : null };
config.server_audio_users = [...users, user];
writeConfig(config);
return { success: true, user };
@@ -469,7 +469,7 @@ app.whenReady().then(async () => {
const users = config.server_audio_users || [];
const idx = users.findIndex(u => u.name === name);
if (idx === -1) return { success: false, error: `Utilisateur "${name}" introuvable` };
config.server_audio_users[idx] = { name, group, input_channel: parseInt(input_channel), output_channel: parseInt(output_channel) };
config.server_audio_users[idx] = { name, group, input_channel: parseInt(input_channel), output_channel: output_channel !== null && output_channel !== '' ? parseInt(output_channel) : null };
writeConfig(config);
return { success: true };
} catch (error) {
@@ -497,20 +497,60 @@ app.whenReady().then(async () => {
try {
const config = readConfig();
return {
routing: config.audio?.routing || { inputToGroup: {}, groupToOutput: {}, gains: {} },
channelNames: config.audio?.channelNames || { inputs: {}, outputs: {} },
groups: config.groups || []
groups: config.groups || [],
serverAudioUsers: config.server_audio_users || []
};
} catch (error) {
return { error: error.message };
}
});
ipcMain.handle('routing:save', (event, { routing, channelNames }) => {
// ========== Devices : découverte canaux physiques ==========
ipcMain.handle('devices:getChannels', () => {
try {
const config = readConfig();
const inputDeviceName = config.audio?.device?.inputDeviceId;
const outputDeviceName = config.audio?.device?.outputDeviceId;
let inputDevice = { name: inputDeviceName || 'Non configuré', channels: 0 };
let outputDevice = { name: outputDeviceName || 'Non configuré', channels: 0 };
if (process.platform === 'darwin') {
try {
const { execSync } = require('child_process');
const raw = execSync('system_profiler SPAudioDataType -json', { encoding: 'utf8', timeout: 5000 });
const data = JSON.parse(raw);
if (data.SPAudioDataType) {
data.SPAudioDataType.forEach(item => {
(item._items || []).forEach(dev => {
const name = dev._name || '';
const inCh = parseInt(dev.coreaudio_device_input) || 0;
const outCh = parseInt(dev.coreaudio_device_output) || 0;
if (inputDeviceName && name === inputDeviceName && inCh > 0) {
inputDevice = { name, channels: inCh };
}
if (outputDeviceName && name === outputDeviceName && outCh > 0) {
outputDevice = { name, channels: outCh };
}
});
});
}
} catch (_) { /* detection failed, keep defaults */ }
}
return { inputDevice, outputDevice };
} catch (error) {
return { error: error.message, inputDevice: { name: 'Inconnu', channels: 0 }, outputDevice: { name: 'Inconnu', channels: 0 } };
}
});
ipcMain.handle('routing:save', (event, { channelNames }) => {
try {
const config = readConfig();
if (!config.audio) config.audio = {};
config.audio.routing = routing;
config.audio.channelNames = channelNames;
writeConfig(config);
return { success: true };