AudioTransDiar / templates /test_index.html
prthm11's picture
Upload 12 files
4207399 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Audio Recorder & Transcription UI</title>
<style>
body {
font-family: 'Segoe UI', Arial, sans-serif;
background: linear-gradient(120deg, #f5f6fa 60%, #dbeafe 100%);
margin: 0;
padding: 0;
}
.container {
max-width: 750px;
margin: 40px auto;
background: #fff;
border-radius: 14px;
box-shadow: 0 4px 24px #0002;
padding: 32px 32px 24px 32px;
}
h1 {
margin-top: 0;
font-size: 2.2em;
letter-spacing: 1px;
color: #2563eb;
text-align: center;
}
label {
display: block;
margin-top: 18px;
font-weight: 600;
color: #334155;
}
select,
input[type="number"] {
margin-top: 6px;
padding: 8px;
font-size: 1em;
border-radius: 6px;
border: 1px solid #cbd5e1;
background: #f1f5f9;
width: 100%;
box-sizing: border-box;
}
button {
margin-top: 12px;
margin-right: 10px;
padding: 10px 22px;
font-size: 1em;
font-weight: 600;
border: none;
border-radius: 6px;
background: #2563eb;
color: #fff;
cursor: pointer;
transition: background 0.2s;
}
button:disabled {
background: #94a3b8;
cursor: not-allowed;
}
.stop-btn {
background: #dc2626;
}
.status {
margin-top: 18px;
font-weight: bold;
color: #0ea5e9;
text-align: center;
font-size: 1.1em;
}
.live {
margin-top: 32px;
background: #f1f5f9;
border-radius: 8px;
padding: 18px 18px 10px 18px;
}
.live h2 {
margin-top: 0;
color: #0ea5e9;
font-size: 1.2em;
}
.chunk {
background: #e0e7ef;
margin-bottom: 8px;
padding: 8px 12px;
border-radius: 5px;
font-size: 1em;
color: #334155;
box-shadow: 0 1px 2px #0001;
}
.files {
margin-top: 32px;
background: #f1f5f9;
border-radius: 8px;
padding: 18px 18px 10px 18px;
}
.files h2 {
margin-top: 0;
color: #2563eb;
font-size: 1.2em;
}
.file {
background: #e0e7ef;
margin-bottom: 8px;
padding: 8px 12px;
border-radius: 5px;
font-size: 1em;
color: #334155;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 1px 2px #0001;
}
.file a {
color: #2563eb;
text-decoration: none;
font-weight: 500;
}
.file a:hover {
text-decoration: underline;
}
.footer {
margin-top: 36px;
text-align: center;
color: #64748b;
font-size: 0.95em;
}
@media (max-width: 600px) {
.container {
padding: 12px 4vw 12px 4vw;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Audio Recorder & Transcription</h1>
<div>
<label for="mic">Microphone Device</label>
<select id="mic" disabled>
<option value="1" selected>Microphone Device (#1)</option>
</select>
<label for="sys">System/Loopback Device (optional)</label>
<select id="sys" disabled>
<option value="16" selected>System Loopback Device (#16)</option>
</select>
<label for="chunk_secs">Chunk Length (seconds)</label>
<input type="number" id="chunk_secs" value="5" min="1" max="60" readonly>
<label for="model">Transcription Model</label>
<select id="model" disabled>
<option value="small">small</option>
<option value="medium" selected>medium</option>
<option value="large">large</option>
</select>
<div style="margin-top:18px; text-align:center;">
<button id="startBtn">Start Recording</button>
<button id="stopBtn" class="stop-btn" disabled>Stop Recording</button>
</div>
</div>
<div class="status" id="status"></div>
<div class="live">
<h2>Live Transcription</h2>
<div id="live"></div>
</div>
<div class="files">
<h2>Final Files</h2>
<div id="files"></div>
</div>
<div class="footer">
&copy; 2025 Audio Multi-Transcript UI &middot; Powered by Flask + PyAudio + Whisper
</div>
</div>
<script>
// --- Start/Stop Recording ---
let polling = null;
document.getElementById('startBtn').onclick = async function () {
const mic = 1; // static value
const sys = 16; // static value
const chunk_secs = 5; // static value
const model = "medium"; // static value
const no_transcribe = false;
document.getElementById('status').textContent = 'Starting...';
await fetch('/api/start-recording', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ mic, sys, chunk_secs, model, no_transcribe })
});
document.getElementById('startBtn').disabled = true;
document.getElementById('stopBtn').disabled = false;
pollStatus();
};
document.getElementById('stopBtn').onclick = async function () {
await fetch('/api/stop-recording', { method: 'POST' });
document.getElementById('status').textContent = 'Stopping...';
document.getElementById('stopBtn').disabled = true;
if (polling) clearInterval(polling);
setTimeout(() => { loadFiles(); document.getElementById('startBtn').disabled = false; }, 2000);
};
// --- Poll status ---
function pollStatus() {
polling = setInterval(async () => {
const res = await fetch('/api/recording-status');
const data = await res.json();
document.getElementById('status').textContent = data.recording ? 'Recording...' : 'Idle';
// --- Show live transcription ---
const liveDiv = document.getElementById('live');
liveDiv.innerHTML = '';
if (data.live_segments && data.live_segments.length) {
data.live_segments.slice(-10).forEach(seg => {
const div = document.createElement('div');
div.className = 'chunk';
div.innerHTML = `<b>${seg.speaker || 'Speaker'}:</b> [${formatTime(seg.start)} - ${formatTime(seg.end)}] ${seg.text}`;
liveDiv.appendChild(div);
});
} else {
liveDiv.textContent = 'No transcription yet.';
}
if (!data.recording) {
clearInterval(polling);
document.getElementById('startBtn').disabled = false;
document.getElementById('stopBtn').disabled = true;
loadFiles();
}
}, 1000);
}
// Helper to format time
function formatTime(s) {
if (s == null) return "0:00";
const mm = Math.floor(s / 60);
const ss = Math.floor(s % 60).toString().padStart(2, "0");
return `${mm}:${ss}`;
}
// --- Load final files ---
async function loadFiles() {
const filesDiv = document.getElementById('files');
filesDiv.innerHTML = '';
try {
const res = await fetch('/api/final-files');
const data = await res.json();
if (!data.files.length) {
filesDiv.textContent = 'No files yet.';
return;
}
data.files.forEach(f => {
const div = document.createElement('div');
div.className = 'file';
div.innerHTML = `<span>${f.name}</span> <a href="${f.url || f.path}" target="_blank">Download</a>`;
filesDiv.appendChild(div);
});
} catch (e) {
filesDiv.textContent = 'Error loading files.';
}
}
// --- On load ---
loadFiles();
</script>
</body>
</html>