const token = sessionStorage.getItem('token'); function authHeaders() { return token ? { 'Authorization': 'Bearer ' + token } : {}; } function apiFetch(url, options) { options = options || {}; return fetch(url, Object.assign({}, options, { headers: Object.assign({'Content-Type': 'application/json'}, authHeaders(), options.headers || {}), })); } let _devices = []; function showToast(msg) { const t = document.getElementById('toast'); t.textContent = msg; t.classList.add('show'); setTimeout(function() { t.classList.remove('show'); }, 2500); } async function loadDevices() { const r = await apiFetch('/audio/devices'); if (!r.ok) return; _devices = await r.json(); const sel = document.getElementById('audio-device'); const current = sel.value; sel.replaceChildren(new Option('Systemstandard', '')); _devices.forEach(function(d) { sel.appendChild(new Option(d.name, d.name)); }); if (current) sel.value = current; ['combined-mic', 'combined-monitor'].forEach(function(id) { const el = document.getElementById(id); el.replaceChildren(); _devices.forEach(function(d) { el.appendChild(new Option(d.name, d.name)); }); }); } async function loadOllamaModels(baseUrl, current) { try { const parsed = new URL(baseUrl); if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return; const r = await fetch(parsed.origin + '/api/tags'); if (!r.ok) return; const data = await r.json(); const sel = document.getElementById('ollama-model'); sel.replaceChildren(); (data.models || []).forEach(function(m) { sel.appendChild(new Option(m.name, m.name)); }); if (current) sel.value = current; } catch(e) {} } async function loadConfig() { const r = await apiFetch('/config'); if (!r.ok) return; const cfg = await r.json(); document.getElementById('audio-device').value = (cfg.audio && cfg.audio.device) || ''; document.getElementById('whisper-backend').value = (cfg.whisper && cfg.whisper.backend) || 'openai'; document.getElementById('whisper-url').value = (cfg.whisper && cfg.whisper.base_url) || ''; document.getElementById('whisper-model').value = (cfg.whisper && cfg.whisper.model) || 'large-v3'; const ollamaUrl = (cfg.ollama && cfg.ollama.base_url) || 'http://localhost:11434'; document.getElementById('ollama-url').value = ollamaUrl; await loadOllamaModels(ollamaUrl, cfg.ollama && cfg.ollama.model); const diarCfg = cfg.diarization || {}; document.getElementById('diar-enabled').checked = !!diarCfg.enabled; document.getElementById('diar-hf-token').value = diarCfg.hf_token || ''; document.getElementById('obsidian-vault').value = (cfg.obsidian && cfg.obsidian.vault) || ''; } document.getElementById('refresh-devices-btn').addEventListener('click', loadDevices); document.getElementById('create-combined-btn').addEventListener('click', function() { document.getElementById('combined-form').classList.toggle('visible'); }); document.getElementById('combined-cancel-btn').addEventListener('click', function() { document.getElementById('combined-form').classList.remove('visible'); }); document.getElementById('combined-confirm-btn').addEventListener('click', async function() { const mic = document.getElementById('combined-mic').value; const monitor = document.getElementById('combined-monitor').value; const r = await apiFetch('/audio/combined', { method: 'POST', body: JSON.stringify({ mic: mic, monitor: monitor }), }); if (!r.ok) { showToast('Fehler beim Erstellen'); return; } const data = await r.json(); showToast('Erstellt: ' + data.device); document.getElementById('combined-form').classList.remove('visible'); await loadDevices(); document.getElementById('audio-device').value = data.device; }); document.getElementById('ollama-url').addEventListener('change', function(e) { loadOllamaModels(e.target.value, document.getElementById('ollama-model').value); }); document.getElementById('save-btn').addEventListener('click', async function() { const body = { audio: { device: document.getElementById('audio-device').value }, whisper: { base_url: document.getElementById('whisper-url').value, model: document.getElementById('whisper-model').value, backend: document.getElementById('whisper-backend').value, }, ollama: { base_url: document.getElementById('ollama-url').value, model: document.getElementById('ollama-model').value, }, obsidian: { vault: document.getElementById('obsidian-vault').value.trim(), }, diarization: { enabled: document.getElementById('diar-enabled').checked, hf_token: document.getElementById('diar-hf-token').value.trim(), }, }; const r = await apiFetch('/config', { method: 'PUT', body: JSON.stringify(body) }); if (r.ok) { showToast('Gespeichert'); } else { showToast('Fehler beim Speichern'); } }); (async function() { if (!token) { location.href = '/login'; return; } await loadDevices(); await loadConfig(); })();