feat: auto-select STT engine (Azure → Google Cloud → Whisper) and show provider in results
Removes user-facing engine/model/key/beam controls. The server now picks the best available engine automatically: 1. Microsoft Azure Speech — short clips (≤1MB, no diarization, audio/*) 2. Google Cloud Speech v2 — long audio, diarization, all languages 3. OpenAI Whisper GPU — local fallback Results display which provider was used (e.g. "Transcribed with Google Cloud Speech") via transcript-engine-badge and traceMeta. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+25
-182
@@ -402,17 +402,6 @@ function syncOutputLanguage(lang) {
|
||||
|
||||
const TRANSCRIBE_I18N = {
|
||||
en: {
|
||||
engine: 'Engine',
|
||||
engineGpuLabel: 'GPU (cuttlefish RTX 3060)',
|
||||
engineOpenaiLabel: 'OpenAI Whisper API',
|
||||
engineAzureLabel: 'Azure AI Speech (nb-NO)',
|
||||
apiKey: 'API Key',
|
||||
apiKeyHint: 'Used for this request only, never stored. Max 25 MB.',
|
||||
region: 'Region',
|
||||
model: 'Model',
|
||||
modelFastest: 'Fastest',
|
||||
modelBalanced: 'Balanced',
|
||||
modelBest: 'Best quality',
|
||||
transcribeLang: 'Audio language',
|
||||
autoDetectHint: '(may confuse nb/da/sv)',
|
||||
speakers: 'Speakers',
|
||||
@@ -433,26 +422,12 @@ const TRANSCRIBE_I18N = {
|
||||
uploadHint: 'max 200 MB per file',
|
||||
uploadAddFiles: '+ Add files',
|
||||
uploadClearQueue: '× Clear queue',
|
||||
expertSettings: 'Advanced settings',
|
||||
task: 'Task',
|
||||
taskTranscribe: 'Transcribe',
|
||||
taskTranslate: 'Translate to English',
|
||||
beamSize: 'Beam size',
|
||||
beamFastest: '(fastest)',
|
||||
beamBest: '(best)',
|
||||
beamSizeHint: 'Controls search breadth — higher values improve accuracy but take longer. 5 is recommended for legal recordings.',
|
||||
vadFilter: 'VAD filter',
|
||||
vadFilterLabel: 'Remove silence',
|
||||
vadFilterHint: 'Voice Activity Detection — skips silent passages before transcribing. Speeds up processing and prevents the model hallucinating on silence.',
|
||||
run: 'Run',
|
||||
running: 'Transcribing…',
|
||||
runningOther: 'Running…',
|
||||
readyTitle: 'Ready',
|
||||
readyDesc: 'Select a tool, run a request, and the result appears here.',
|
||||
noFileSelected: 'Select at least one audio file before transcribing.',
|
||||
missingOpenaiKey: 'Enter a valid OpenAI API key (sk-…) before running.',
|
||||
openaiFileTooLarge: (f) => `OpenAI Whisper has a 25 MB limit. Use the GPU engine for ${f}.`,
|
||||
missingAzureKey: 'Enter an Azure Speech API key before running.',
|
||||
clipLabel: (i, total) => total > 1 ? `Clip ${i}/${total}` : 'Transcribing',
|
||||
transcribeFailed: (s) => `Transcription failed (HTTP ${s}).`,
|
||||
errorLabel: (clip) => `Error – ${clip}`,
|
||||
@@ -460,25 +435,14 @@ const TRANSCRIBE_I18N = {
|
||||
fileSizeExceeded: (name, mb) => `${name} (${mb} MB — max 200 MB)`,
|
||||
filesInQueue: (n) => `${n} file${n !== 1 ? 's' : ''} in queue.`,
|
||||
done: (n, dur) => n > 1 ? `Done · ${n} clips · Total audio: ${dur}` : `Done · Audio: ${dur}`,
|
||||
traceUploadLabel: (clip, eng) => `${clip} — uploading to ${eng}`,
|
||||
traceUploadDetail: (eng) => eng === 'gpu' ? 'Sending audio to cuttlefish GPU…' : `Sending audio to ${eng}…`,
|
||||
traceProcessingLabel: (clip, eng) => `${clip} — ${eng} transcribing`,
|
||||
traceProcessingDetail: (eng) => eng === 'gpu' ? 'Whisper transcribing. Large files take 1–3 minutes.' : `${eng} processing audio.`,
|
||||
traceUploadLabel: (clip) => `${clip} — uploading`,
|
||||
traceUploadDetail: () => 'Sending to transcription service…',
|
||||
traceProcessingLabel: (clip) => `${clip} — transcribing`,
|
||||
traceProcessingDetail: () => 'Processing audio. Large files may take 1–3 minutes.',
|
||||
traceStillLabel: (clip) => `${clip} — still processing…`,
|
||||
traceStillDetail: (e) => { const m = Math.floor(e / 60), s = e % 60; return m > 0 ? `${m}m ${s}s elapsed — working through the audio.` : `${e}s elapsed — processing.`; },
|
||||
},
|
||||
no: {
|
||||
engine: 'Motor',
|
||||
engineGpuLabel: 'GPU (cuttlefish RTX 3060)',
|
||||
engineOpenaiLabel: 'OpenAI Whisper API',
|
||||
engineAzureLabel: 'Azure AI Speech (nb-NO)',
|
||||
apiKey: 'API-nøkkel',
|
||||
apiKeyHint: 'Brukes kun for denne forespørselen, lagres aldri. Maks 25 MB.',
|
||||
region: 'Region',
|
||||
model: 'Modell',
|
||||
modelFastest: 'Raskest',
|
||||
modelBalanced: 'Balansert',
|
||||
modelBest: 'Beste kvalitet',
|
||||
transcribeLang: 'Språk i lydfil',
|
||||
autoDetectHint: '(kan forveksle nb/da/sv)',
|
||||
speakers: 'Talere',
|
||||
@@ -499,26 +463,12 @@ const TRANSCRIBE_I18N = {
|
||||
uploadHint: 'maks 200 MB per fil',
|
||||
uploadAddFiles: '+ Legg til filer',
|
||||
uploadClearQueue: '× Tøm kø',
|
||||
expertSettings: 'Ekspertinnstillinger',
|
||||
task: 'Oppgave',
|
||||
taskTranscribe: 'Transkriber',
|
||||
taskTranslate: 'Oversett til engelsk',
|
||||
beamSize: 'Beam size',
|
||||
beamFastest: '(raskest)',
|
||||
beamBest: '(best)',
|
||||
beamSizeHint: 'Styrer søkebredde — høyere verdier gir bedre nøyaktighet men tar lengre tid. 5 anbefales for juridiske opptak.',
|
||||
vadFilter: 'VAD-filter',
|
||||
vadFilterLabel: 'Fjern stillhet',
|
||||
vadFilterHint: 'Taleaktivitetsdeteksjon — hopper over stille partier før transkripsjon. Raskere behandling og forhindrer hallusinasjon på stillhet.',
|
||||
run: 'Kjør',
|
||||
running: 'Transkriberer…',
|
||||
runningOther: 'Kjører…',
|
||||
readyTitle: 'Klar',
|
||||
readyDesc: 'Velg et verktøy, kjør en forespørsel, og svaret vises her.',
|
||||
noFileSelected: 'Velg minst én lydfil før transkripsjon.',
|
||||
missingOpenaiKey: 'Legg inn en gyldig OpenAI API-nøkkel (sk-…) før du kjører.',
|
||||
openaiFileTooLarge: (f) => `OpenAI Whisper har 25 MB-grense. Bruk GPU-motor for ${f}.`,
|
||||
missingAzureKey: 'Legg inn Azure Speech API-nøkkel før du kjører.',
|
||||
clipLabel: (i, total) => total > 1 ? `Klipp ${i}/${total}` : 'Transkriberer',
|
||||
transcribeFailed: (s) => `Transkripsjon feilet (HTTP ${s}).`,
|
||||
errorLabel: (clip) => `Feil – ${clip}`,
|
||||
@@ -526,25 +476,13 @@ const TRANSCRIBE_I18N = {
|
||||
fileSizeExceeded: (name, mb) => `${name} (${mb} MB — maks 200 MB)`,
|
||||
filesInQueue: (n) => `${n} fil${n !== 1 ? 'er' : ''} i køen.`,
|
||||
done: (n, dur) => n > 1 ? `Ferdig · ${n} klipp · Total lyd: ${dur}` : `Ferdig · Lyd: ${dur}`,
|
||||
traceUploadLabel: (clip, eng) => `${clip} — laster opp til ${eng}`,
|
||||
traceUploadDetail: (eng) => eng === 'gpu' ? 'Sender lyd til cuttlefish GPU…' : `Sender lyd til ${eng}…`,
|
||||
traceProcessingLabel: (clip, eng) => `${clip} — ${eng} transkriberer`,
|
||||
traceProcessingDetail: (eng) => eng === 'gpu' ? 'Whisper transkriberer. Store filer tar 1–3 minutter.' : `${eng} behandler lyden.`,
|
||||
traceStillLabel: (clip) => `${clip} — behandler fortsatt…`,
|
||||
traceUploadLabel: (clip) => `${clip} — laster opp`,
|
||||
traceUploadDetail: () => 'Sender til transkripsjonsleverandør…',
|
||||
traceProcessingLabel: (clip) => `${clip} — transkriberer`,
|
||||
traceProcessingDetail: () => 'Behandler lyden. Store filer tar 1–3 minutter.', traceStillLabel: (clip) => `${clip} — behandler fortsatt…`,
|
||||
traceStillDetail: (e) => { const m = Math.floor(e / 60), s = e % 60; return m > 0 ? `${m} min ${s}s gått — jobber gjennom lyden.` : `${e}s gått — behandler.`; },
|
||||
},
|
||||
uk: {
|
||||
engine: 'Рушій',
|
||||
engineGpuLabel: 'GPU (cuttlefish RTX 3060)',
|
||||
engineOpenaiLabel: 'OpenAI Whisper API',
|
||||
engineAzureLabel: 'Azure AI Speech (nb-NO)',
|
||||
apiKey: 'API-ключ',
|
||||
apiKeyHint: 'Використовується лише для цього запиту, ніколи не зберігається. Макс 25 МБ.',
|
||||
region: 'Регіон',
|
||||
model: 'Модель',
|
||||
modelFastest: 'Найшвидша',
|
||||
modelBalanced: 'Збалансована',
|
||||
modelBest: 'Найкраща якість',
|
||||
transcribeLang: 'Мова аудіо',
|
||||
autoDetectHint: '(може плутати nb/da/sv)',
|
||||
speakers: 'Мовці',
|
||||
@@ -565,26 +503,12 @@ const TRANSCRIBE_I18N = {
|
||||
uploadHint: 'макс 200 МБ на файл',
|
||||
uploadAddFiles: '+ Додати файли',
|
||||
uploadClearQueue: '× Очистити чергу',
|
||||
expertSettings: 'Розширені налаштування',
|
||||
task: 'Завдання',
|
||||
taskTranscribe: 'Транскрибувати',
|
||||
taskTranslate: 'Перекласти англійською',
|
||||
beamSize: 'Розмір пучка',
|
||||
beamFastest: '(найшвидший)',
|
||||
beamBest: '(найкращий)',
|
||||
beamSizeHint: 'Ширина пошуку — більше значення підвищує точність, але займає більше часу. 5 рекомендовано для юридичних записів.',
|
||||
vadFilter: 'VAD-фільтр',
|
||||
vadFilterLabel: 'Видалити тишу',
|
||||
vadFilterHint: 'Виявлення мовної активності — пропускає тихі ділянки перед транскрипцією. Прискорює обробку та запобігає галюцинаціям на тиші.',
|
||||
run: 'Запустити',
|
||||
running: 'Транскрибування…',
|
||||
runningOther: 'Виконання…',
|
||||
readyTitle: 'Готово',
|
||||
readyDesc: 'Виберіть інструмент, запустіть запит — результат з\'явиться тут.',
|
||||
noFileSelected: 'Виберіть хоч б один аудіофайл перед транскрибуванням.',
|
||||
missingOpenaiKey: 'Введіть дійсний ключ OpenAI API (sk-…) перед запуском.',
|
||||
openaiFileTooLarge: (f) => `OpenAI Whisper має обмеження 25 МБ. Використовуйте GPU для ${f}.`,
|
||||
missingAzureKey: 'Введіть ключ Azure Speech API перед запуском.',
|
||||
clipLabel: (i, total) => total > 1 ? `Кліп ${i}/${total}` : 'Транскрибування',
|
||||
transcribeFailed: (s) => `Транскрибування не вдалося (HTTP ${s}).`,
|
||||
errorLabel: (clip) => `Помилка – ${clip}`,
|
||||
@@ -592,25 +516,13 @@ const TRANSCRIBE_I18N = {
|
||||
fileSizeExceeded: (name, mb) => `${name} (${mb} МБ — макс 200 МБ)`,
|
||||
filesInQueue: (n) => `${n} файл${n !== 1 ? 'ів' : ''} у черзі.`,
|
||||
done: (n, dur) => n > 1 ? `Готово · ${n} кліпи · Загальне аудіо: ${dur}` : `Готово · Аудіо: ${dur}`,
|
||||
traceUploadLabel: (clip, eng) => `${clip} — завантаження до ${eng}`,
|
||||
traceUploadDetail: (eng) => eng === 'gpu' ? 'Відправка аудіо на cuttlefish GPU…' : `Відправка аудіо до ${eng}…`,
|
||||
traceProcessingLabel: (clip, eng) => `${clip} — ${eng} транскрибує`,
|
||||
traceProcessingDetail: (eng) => eng === 'gpu' ? 'Whisper транскрибує. Великі файли займають 1–3 хвилини.' : `${eng} обробляє аудіо.`,
|
||||
traceStillLabel: (clip) => `${clip} — ще обробляється…`,
|
||||
traceUploadLabel: (clip) => `${clip} — завантаження`,
|
||||
traceUploadDetail: () => 'Відправка до сервісу транскрипції…',
|
||||
traceProcessingLabel: (clip) => `${clip} — транскрибування`,
|
||||
traceProcessingDetail: () => 'Обробка аудіо. Великі файли займають 1–3 хвилини.', traceStillLabel: (clip) => `${clip} — ще обробляється…`,
|
||||
traceStillDetail: (e) => { const m = Math.floor(e / 60), s = e % 60; return m > 0 ? `Минуло ${m} хв ${s} с — обробка.` : `Минуло ${e} с — обробка.`; },
|
||||
},
|
||||
pl: {
|
||||
engine: 'Silnik',
|
||||
engineGpuLabel: 'GPU (cuttlefish RTX 3060)',
|
||||
engineOpenaiLabel: 'OpenAI Whisper API',
|
||||
engineAzureLabel: 'Azure AI Speech (nb-NO)',
|
||||
apiKey: 'Klucz API',
|
||||
apiKeyHint: 'Używany tylko dla tego żądania, nigdy nie przechowywany. Maks 25 MB.',
|
||||
region: 'Region',
|
||||
model: 'Model',
|
||||
modelFastest: 'Najszybszy',
|
||||
modelBalanced: 'Zrównoważony',
|
||||
modelBest: 'Najlepsza jakość',
|
||||
transcribeLang: 'Język audio',
|
||||
autoDetectHint: '(może mylić nb/da/sv)',
|
||||
speakers: 'Mówcy',
|
||||
@@ -631,26 +543,12 @@ const TRANSCRIBE_I18N = {
|
||||
uploadHint: 'maks 200 MB na plik',
|
||||
uploadAddFiles: '+ Dodaj pliki',
|
||||
uploadClearQueue: '× Wyczyść kolejkę',
|
||||
expertSettings: 'Ustawienia zaawansowane',
|
||||
task: 'Zadanie',
|
||||
taskTranscribe: 'Transkrybuj',
|
||||
taskTranslate: 'Przetłumacz na angielski',
|
||||
beamSize: 'Rozmiar wiązki',
|
||||
beamFastest: '(najszybszy)',
|
||||
beamBest: '(najlepszy)',
|
||||
beamSizeHint: 'Kontroluje szerokość wyszukiwania — wyższe wartości poprawiają dokładność, ale wydłużają czas. 5 zalecane dla nagrań prawnych.',
|
||||
vadFilter: 'Filtr VAD',
|
||||
vadFilterLabel: 'Usuń ciszę',
|
||||
vadFilterHint: 'Wykrywanie aktywności głosowej — pomija ciche fragmenty przed transkrypcją. Przyspiesza przetwarzanie i zapobiega halucynacjom na ciszy.',
|
||||
run: 'Uruchom',
|
||||
running: 'Transkrybowanie…',
|
||||
runningOther: 'Uruchamianie…',
|
||||
readyTitle: 'Gotowe',
|
||||
readyDesc: 'Wybierz narzędzie, uruchom żądanie — wynik pojawi się tutaj.',
|
||||
noFileSelected: 'Wybierz co najmniej jeden plik audio przed transkrypcją.',
|
||||
missingOpenaiKey: 'Wprowadź prawidłowy klucz API OpenAI (sk-…) przed uruchomieniem.',
|
||||
openaiFileTooLarge: (f) => `OpenAI Whisper ma limit 25 MB. Użyj silnika GPU dla ${f}.`,
|
||||
missingAzureKey: 'Wprowadź klucz Azure Speech API przed uruchomieniem.',
|
||||
clipLabel: (i, total) => total > 1 ? `Klip ${i}/${total}` : 'Transkrybowanie',
|
||||
transcribeFailed: (s) => `Transkrypcja nie powiodła się (HTTP ${s}).`,
|
||||
errorLabel: (clip) => `Błąd – ${clip}`,
|
||||
@@ -658,11 +556,10 @@ const TRANSCRIBE_I18N = {
|
||||
fileSizeExceeded: (name, mb) => `${name} (${mb} MB — maks 200 MB)`,
|
||||
filesInQueue: (n) => `${n} plik${n !== 1 ? 'i' : ''} w kolejce.`,
|
||||
done: (n, dur) => n > 1 ? `Gotowe · ${n} klipy · Łączne audio: ${dur}` : `Gotowe · Audio: ${dur}`,
|
||||
traceUploadLabel: (clip, eng) => `${clip} — przesyłanie do ${eng}`,
|
||||
traceUploadDetail: (eng) => eng === 'gpu' ? 'Wysyłanie audio do cuttlefish GPU…' : `Wysyłanie audio do ${eng}…`,
|
||||
traceProcessingLabel: (clip, eng) => `${clip} — ${eng} transkrybuje`,
|
||||
traceProcessingDetail: (eng) => eng === 'gpu' ? 'Whisper transkrybuje. Duże pliki zajmują 1–3 minuty.' : `${eng} przetwarza audio.`,
|
||||
traceStillLabel: (clip) => `${clip} — nadal przetwarza…`,
|
||||
traceUploadLabel: (clip) => `${clip} — przesyłanie`,
|
||||
traceUploadDetail: () => 'Wysyłanie do serwisu transkrypcji…',
|
||||
traceProcessingLabel: (clip) => `${clip} — transkrybowanie`,
|
||||
traceProcessingDetail: () => 'Przetwarzanie audio. Duże pliki zajmują 1–3 minuty.', traceStillLabel: (clip) => `${clip} — nadal przetwarza…`,
|
||||
traceStillDetail: (e) => { const m = Math.floor(e / 60), s = e % 60; return m > 0 ? `Minęło ${m} min ${s} s — przetwarzanie audio.` : `Minęło ${e} s — przetwarzanie.`; },
|
||||
},
|
||||
};
|
||||
@@ -1556,18 +1453,6 @@ function exportTimelineCSV(events) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function currentTranscribeEngine() {
|
||||
const el = document.querySelector('input[name="engine"]:checked');
|
||||
return el ? el.value : 'gpu';
|
||||
}
|
||||
function currentTranscribeModel() {
|
||||
const el = document.querySelector('input[name="model"]:checked');
|
||||
return el ? el.value : 'small';
|
||||
}
|
||||
function currentBeamSize() {
|
||||
const el = document.querySelector('input[name="beam_size"]:checked');
|
||||
return el ? el.value : '5';
|
||||
}
|
||||
function currentTask() {
|
||||
const el = document.querySelector('input[name="task"]:checked');
|
||||
return el ? el.value : 'transcribe';
|
||||
@@ -1579,28 +1464,6 @@ async function runTranscribe() {
|
||||
return;
|
||||
}
|
||||
|
||||
const engine = currentTranscribeEngine();
|
||||
|
||||
if (engine === 'openai') {
|
||||
const key = document.getElementById('openaiKeyInput')?.value?.trim();
|
||||
if (!key || !key.startsWith('sk-')) {
|
||||
els.status.textContent = currentUiT('missingOpenaiKey');
|
||||
return;
|
||||
}
|
||||
const oversized = audioQueue.find((item) => item.file.size > 25 * 1024 * 1024);
|
||||
if (oversized) {
|
||||
els.status.textContent = currentUiT('openaiFileTooLarge', oversized.file.name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (engine === 'azure' && !window.DBN_AZURE_SPEECH_CONFIGURED) {
|
||||
const key = document.getElementById('azureKeyInput')?.value?.trim();
|
||||
if (!key) {
|
||||
els.status.textContent = currentUiT('missingAzureKey');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setBusy(true);
|
||||
|
||||
const initPrompt = els.initPromptInput?.value?.trim() || '';
|
||||
@@ -1635,16 +1498,13 @@ async function runTranscribe() {
|
||||
const s = elapsed % 60;
|
||||
const t = m > 0 ? `${m}:${pad2(s)}` : `${s}s`;
|
||||
els.status.textContent = `${clipLabel}… ${t}`;
|
||||
updateTranscribeTrace(elapsed, engine, clipLabel);
|
||||
updateTranscribeTrace(elapsed, clipLabel);
|
||||
}, 1000);
|
||||
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('audio', item.file);
|
||||
formData.append('engine', engine);
|
||||
formData.append('language', currentTranscribeLang());
|
||||
formData.append('model', currentTranscribeModel());
|
||||
formData.append('beam_size', currentBeamSize());
|
||||
formData.append('task', currentTask());
|
||||
formData.append('time_offset', String(cumulativeOffset));
|
||||
if (vadFilter) formData.append('vad_filter', '1');
|
||||
@@ -1653,13 +1513,6 @@ async function runTranscribe() {
|
||||
formData.append('diarize', '1');
|
||||
if (numSpeakers >= 2) formData.append('num_speakers', String(numSpeakers));
|
||||
}
|
||||
if (engine === 'openai') {
|
||||
formData.append('openai_key', document.getElementById('openaiKeyInput')?.value?.trim());
|
||||
}
|
||||
if (engine === 'azure') {
|
||||
formData.append('azure_key', document.getElementById('azureKeyInput')?.value?.trim());
|
||||
formData.append('azure_region', document.getElementById('azureRegionInput')?.value?.trim() || 'norwayeast');
|
||||
}
|
||||
|
||||
const resp = await fetch('api/transcribe.php', {
|
||||
method: 'POST',
|
||||
@@ -1721,23 +1574,21 @@ async function runTranscribe() {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
function updateTranscribeTrace(elapsed, engine, clipLabel) {
|
||||
function updateTranscribeTrace(elapsed, clipLabel) {
|
||||
if (!clipLabel) clipLabel = currentUiT('clipLabel', 1, 1);
|
||||
const engineLabel = engine === 'openai' ? 'OpenAI API' : engine === 'azure' ? 'Azure Speech' : 'Whisper GPU';
|
||||
let label, detail;
|
||||
if (elapsed < 10) {
|
||||
label = currentUiT('traceUploadLabel', clipLabel, engineLabel);
|
||||
detail = currentUiT('traceUploadDetail', engine);
|
||||
label = currentUiT('traceUploadLabel', clipLabel);
|
||||
detail = currentUiT('traceUploadDetail');
|
||||
} else if (elapsed < 60) {
|
||||
label = currentUiT('traceProcessingLabel', clipLabel, engineLabel);
|
||||
detail = currentUiT('traceProcessingDetail', engine);
|
||||
label = currentUiT('traceProcessingLabel', clipLabel);
|
||||
detail = currentUiT('traceProcessingDetail');
|
||||
} else {
|
||||
label = currentUiT('traceStillLabel', clipLabel);
|
||||
detail = currentUiT('traceStillDetail', elapsed);
|
||||
}
|
||||
renderTrace([{ label, detail, status: 'running' }]);
|
||||
}
|
||||
|
||||
function renderTranscriptResults(data) {
|
||||
const speakerRoles = data.speaker_roles || {};
|
||||
const segments = data.segments || [];
|
||||
@@ -1776,6 +1627,7 @@ function renderTranscriptResults(data) {
|
||||
els.results.innerHTML = `
|
||||
<section class="result-section">
|
||||
<h3>Transcript</h3>
|
||||
${data.model ? `<p class="transcript-engine-badge">Transcribed with <strong>${escapeHtml(data.model)}</strong></p>` : ''}
|
||||
${rolesHtml}
|
||||
<div class="transcript-box"><pre class="transcript-text">${escapeHtml(data.transcript)}</pre></div>
|
||||
${segmentsHtml}
|
||||
@@ -1791,7 +1643,7 @@ function renderTranscriptResults(data) {
|
||||
if (data.duration_sec) traceMeta.push({ label: `Duration: ${Math.round(data.duration_sec)}s`, detail: '', status: 'complete' });
|
||||
if (data.language) traceMeta.push({ label: `Language: ${data.language}`, detail: '', status: 'complete' });
|
||||
if (data.num_speakers > 1) traceMeta.push({ label: `Speakers detected: ${data.num_speakers}`, detail: Object.entries(speakerRoles).map(([id, r]) => `${id}: ${r}`).join(', ') || '', status: 'complete' });
|
||||
if (data.model) traceMeta.push({ label: `Model: ${data.model}`, detail: '', status: 'complete' });
|
||||
if (data.model) traceMeta.push({ label: data.model, detail: '', status: 'complete' });
|
||||
renderTrace(traceMeta.length ? traceMeta : [{ label: 'Transcribed', detail: '', status: 'complete' }]);
|
||||
}
|
||||
|
||||
@@ -1935,16 +1787,7 @@ function setupAudio() {
|
||||
}
|
||||
|
||||
function setupTranscribeControls() {
|
||||
document.querySelectorAll('input[name="engine"]').forEach((radio) => {
|
||||
radio.addEventListener('change', () => {
|
||||
const engine = currentTranscribeEngine();
|
||||
document.getElementById('openaiKeyControl')?.classList.toggle('is-hidden', engine !== 'openai');
|
||||
// Hide azure key row if server has a pre-configured key
|
||||
const azureNeedsKey = engine === 'azure' && !window.DBN_AZURE_SPEECH_CONFIGURED;
|
||||
document.getElementById('azureKeyControl')?.classList.toggle('is-hidden', !azureNeedsKey);
|
||||
document.getElementById('modelControl')?.classList.toggle('is-hidden', engine === 'openai' || engine === 'azure');
|
||||
});
|
||||
});
|
||||
// engine auto-selected server-side
|
||||
}
|
||||
|
||||
function setupVocabPresets() {
|
||||
|
||||
Reference in New Issue
Block a user