From 86f94fc22c165a32a4886d50dd9b93641ea6e41c Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Fri, 12 Jun 2026 16:15:51 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20s=C3=A9lecteur=20de=20microphone=20dans?= =?UTF-8?q?=20l'interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Select dropdown peuplé via enumerateDevices() au chargement - Mise à jour automatique quand un périphérique est branché/débranché (devicechange) - Le deviceId sélectionné est passé à getUserMedia() via exact constraint - Option 'Micro par défaut' pour le comportement d'origine --- app/static/app.js | 34 +++++++++++++++++++++++++++++++--- app/static/index.html | 3 +++ app/static/style.css | 14 ++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/app/static/app.js b/app/static/app.js index 31695e1..f2d74c7 100644 --- a/app/static/app.js +++ b/app/static/app.js @@ -28,13 +28,37 @@ function getSpeakerColorIndex(speaker) { return speakerMap[speaker]; } +// --------------------------------------------------------- sélection micro + +async function populateMicList() { + const sel = $('mic-select'); + try { + const devices = await navigator.mediaDevices.enumerateDevices(); + const mics = devices.filter((d) => d.kind === 'audioinput'); + // Garder la sélection courante si possible + const current = sel.value; + sel.innerHTML = ''; + for (const mic of mics) { + const opt = document.createElement('option'); + opt.value = mic.deviceId; + opt.textContent = mic.label || `Micro ${sel.options.length}`; + sel.appendChild(opt); + } + if (current) sel.value = current; + } catch (err) { + console.warn('Impossible d\'énumérer les micros :', err); + } +} + // ----------------------------------------------------------- enregistrement async function startRecording() { + const audioConstraints = { echoCancellation: true, noiseSuppression: true, autoGainControl: true }; + const selectedMic = $('mic-select').value; + if (selectedMic) audioConstraints.deviceId = { exact: selectedMic }; + try { - state.mediaStream = await navigator.mediaDevices.getUserMedia({ - audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true }, - }); + state.mediaStream = await navigator.mediaDevices.getUserMedia({ audio: audioConstraints }); } catch (err) { const causes = { NotAllowedError: "Permission refusée. Autorisez le micro pour ce site (icône à gauche de l'adresse), et vérifiez que la page est servie en HTTPS.", @@ -240,3 +264,7 @@ $('record-btn').onclick = () => (state.recording ? stopRecording() : startRecord $('copy-btn').onclick = copyTranscript; $('delete-btn').onclick = deleteCurrentMeeting; loadMeetings(); + +// Peupler la liste des micros au chargement (et quand un périphérique change) +populateMicList(); +navigator.mediaDevices?.addEventListener('devicechange', populateMicList); diff --git a/app/static/index.html b/app/static/index.html index c794b5c..12dac25 100644 --- a/app/static/index.html +++ b/app/static/index.html @@ -22,6 +22,9 @@
+ 00:00