diff --git a/client/src/Admin.jsx b/client/src/Admin.jsx
index 43a6629..96e7c86 100644
--- a/client/src/Admin.jsx
+++ b/client/src/Admin.jsx
@@ -19,6 +19,10 @@ function Admin() {
const [selectedOutputDevice, setSelectedOutputDevice] = useState(null);
const [selectedSampleRate, setSelectedSampleRate] = useState(48000);
+ // Channel names (Phase 2.5)
+ const [channelNames, setChannelNames] = useState({ inputs: {}, outputs: {} });
+ const [editingChannelNames, setEditingChannelNames] = useState(false);
+
// Gestion formulaire nouveau groupe
const [showGroupForm, setShowGroupForm] = useState(false);
const [editingGroup, setEditingGroup] = useState(null);
@@ -85,16 +89,19 @@ function Admin() {
};
const loadAudioDevices = async () => {
- const [devicesRes, currentDeviceRes] = await Promise.all([
+ const [devicesRes, currentDeviceRes, channelNamesRes] = await Promise.all([
fetch(`${API_URL}/admin/audio/devices`),
- fetch(`${API_URL}/admin/audio/device`)
+ fetch(`${API_URL}/admin/audio/device`),
+ fetch(`${API_URL}/admin/audio/channels/names`)
]);
const devicesData = await devicesRes.json();
const currentData = await currentDeviceRes.json();
+ const channelNamesData = await channelNamesRes.json();
setAudioDevices(devicesData.devices || []);
setCurrentDevice(currentData.device || {});
+ setChannelNames(channelNamesData.channelNames || { inputs: {}, outputs: {} });
// Initialiser les sélections avec les valeurs actuelles
setSelectedInputDevice(currentData.device?.inputDeviceId ?? null);
@@ -221,6 +228,38 @@ function Admin() {
// ========== Gestion audio devices (Phase 2.5) ==========
+ const handleSaveChannelNames = async () => {
+ try {
+ const res = await fetch(`${API_URL}/admin/audio/channels/names`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify(channelNames)
+ });
+
+ if (res.ok) {
+ alert('Noms de canaux sauvegardés avec succès!');
+ setEditingChannelNames(false);
+ await loadAudioDevices();
+ } else {
+ const error = await res.json();
+ alert(`Erreur: ${error.error}`);
+ }
+ } catch (err) {
+ console.error('Erreur sauvegarde noms canaux:', err);
+ alert('Erreur lors de la sauvegarde');
+ }
+ };
+
+ const updateChannelName = (type, channelId, name) => {
+ setChannelNames(prev => ({
+ ...prev,
+ [type]: {
+ ...prev[type],
+ [channelId]: name
+ }
+ }));
+ };
+
const handleSaveAudioDevice = async () => {
try {
const res = await fetch(`${API_URL}/admin/audio/device`, {
@@ -529,6 +568,80 @@ function Admin() {
+
+
+
Nommage des canaux physiques
+ {!editingChannelNames ? (
+
+ ) : (
+
+
+
+
+ )}
+
+
+
+
+
Entrées (Inputs)
+
+ {Array.from({length: 8}, (_, i) => (
+
+ {i}
+ updateChannelName('inputs', i, e.target.value)}
+ placeholder={`Input ${i}`}
+ disabled={!editingChannelNames}
+ style={{
+ padding: 'var(--spacing-sm)',
+ background: editingChannelNames ? 'var(--color-bg)' : 'var(--color-surface-hover)',
+ border: '1px solid var(--color-border)',
+ borderRadius: '6px',
+ color: 'var(--color-text)',
+ fontSize: '0.9rem'
+ }}
+ />
+
+ ))}
+
+
+
+
+
Sorties (Outputs)
+
+ {Array.from({length: 8}, (_, i) => (
+
+ {i}
+ updateChannelName('outputs', i, e.target.value)}
+ placeholder={`Output ${i}`}
+ disabled={!editingChannelNames}
+ style={{
+ padding: 'var(--spacing-sm)',
+ background: editingChannelNames ? 'var(--color-bg)' : 'var(--color-surface-hover)',
+ border: '1px solid var(--color-border)',
+ borderRadius: '6px',
+ color: 'var(--color-text)',
+ fontSize: '0.9rem'
+ }}
+ />
+
+ ))}
+
+
+
+
+
{currentDevice && Object.keys(currentDevice).length > 0 && (
Configuration actuelle
diff --git a/server/api/admin.js b/server/api/admin.js
index 5132520..64c559e 100644
--- a/server/api/admin.js
+++ b/server/api/admin.js
@@ -516,6 +516,68 @@ router.get('/audio/device', (req, res) => {
}
});
+/**
+ * GET /admin/audio/channels/names
+ * Récupère les noms personnalisés des canaux physiques
+ */
+router.get('/audio/channels/names', (req, res) => {
+ try {
+ const config = configManager.get();
+ const channelNames = config.audio?.channelNames || { inputs: {}, outputs: {} };
+
+ res.json({
+ channelNames
+ });
+ } catch (error) {
+ console.error('Erreur GET /admin/audio/channels/names:', error);
+ res.status(500).json({ error: 'Failed to load channel names' });
+ }
+});
+
+/**
+ * PUT /admin/audio/channels/names
+ * Sauvegarde les noms personnalisés des canaux physiques
+ * Body: { inputs: { "0": "Micro Principal", ... }, outputs: { "0": "Retour Scène", ... } }
+ */
+router.put('/audio/channels/names', (req, res) => {
+ try {
+ const { inputs, outputs } = req.body;
+
+ if (!inputs && !outputs) {
+ return res.status(400).json({
+ error: 'Missing required fields: inputs or outputs'
+ });
+ }
+
+ const config = configManager.get();
+
+ if (!config.audio.channelNames) {
+ config.audio.channelNames = { inputs: {}, outputs: {} };
+ }
+
+ if (inputs) {
+ config.audio.channelNames.inputs = inputs;
+ }
+
+ if (outputs) {
+ config.audio.channelNames.outputs = outputs;
+ }
+
+ configManager.save(config);
+
+ addLog('info', 'Channel names updated', { inputCount: Object.keys(inputs || {}).length, outputCount: Object.keys(outputs || {}).length });
+
+ res.json({
+ message: 'Channel names updated',
+ channelNames: config.audio.channelNames
+ });
+
+ } catch (error) {
+ console.error('Erreur PUT /admin/audio/channels/names:', error);
+ res.status(500).json({ error: 'Failed to update channel names' });
+ }
+});
+
/**
* POST /admin/audio/device
* Sélectionne et configure une carte son