| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>OllamaStream - Local AI Media Tools</title> |
| <link rel="preconnect" href="https://fonts.googleapis.com"> |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| :root { |
| --bg-dark: #0f0f13; |
| --bg-panel: #18181f; |
| --bg-panel-hover: #22222b; |
| --primary: #6366f1; |
| --primary-glow: rgba(99, 102, 241, 0.4); |
| --accent: #10b981; |
| --text-main: #ffffff; |
| --text-muted: #9ca3af; |
| --border: #2d2d3a; |
| --glass: rgba(24, 24, 31, 0.7); |
| --glass-border: rgba(255, 255, 255, 0.08); |
| --danger: #ef4444; |
| } |
| |
| * { |
| box-sizing: border-box; |
| margin: 0; |
| padding: 0; |
| outline: none; |
| } |
| |
| body { |
| font-family: 'Inter', sans-serif; |
| background-color: var(--bg-dark); |
| color: var(--text-main); |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| overflow-x: hidden; |
| } |
| |
| |
| header { |
| height: 70px; |
| border-bottom: 1px solid var(--border); |
| display: flex; |
| align-items: center; |
| justify-content: space-between; |
| padding: 0 2rem; |
| background: var(--glass); |
| backdrop-filter: blur(12px); |
| position: sticky; |
| top: 0; |
| z-index: 100; |
| } |
| |
| .logo { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| font-weight: 700; |
| font-size: 1.2rem; |
| background: linear-gradient(135deg, #fff 0%, #a5b4fc 100%); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| } |
| |
| .logo i { |
| background: var(--primary); |
| -webkit-background-clip: text; |
| -webkit-text-fill-color: transparent; |
| font-size: 1.5rem; |
| } |
| |
| .status-indicator { |
| display: flex; |
| align-items: center; |
| gap: 8px; |
| font-size: 0.85rem; |
| color: var(--text-muted); |
| background: rgba(255,255,255,0.05); |
| padding: 6px 12px; |
| border-radius: 20px; |
| } |
| |
| .status-dot { |
| width: 8px; |
| height: 8px; |
| border-radius: 50%; |
| background-color: var(--accent); |
| box-shadow: 0 0 8px var(--accent); |
| animation: pulse 2s infinite; |
| } |
| |
| .built-with { |
| font-size: 0.85rem; |
| color: var(--text-muted); |
| text-decoration: none; |
| border: 1px solid var(--border); |
| padding: 6px 12px; |
| border-radius: 6px; |
| transition: all 0.3s ease; |
| } |
| |
| .built-with:hover { |
| border-color: var(--primary); |
| color: var(--primary); |
| } |
| |
| |
| .app-container { |
| display: flex; |
| flex: 1; |
| height: calc(100vh - 70px); |
| } |
| |
| |
| aside { |
| width: 260px; |
| background: var(--bg-panel); |
| border-right: 1px solid var(--border); |
| padding: 2rem 1rem; |
| display: flex; |
| flex-direction: column; |
| gap: 0.5rem; |
| } |
| |
| .nav-item { |
| display: flex; |
| align-items: center; |
| gap: 12px; |
| padding: 12px 16px; |
| border-radius: 10px; |
| color: var(--text-muted); |
| cursor: pointer; |
| transition: all 0.2s ease; |
| font-weight: 500; |
| } |
| |
| .nav-item:hover { |
| background: var(--bg-panel-hover); |
| color: var(--text-main); |
| } |
| |
| .nav-item.active { |
| background: rgba(99, 102, 241, 0.1); |
| color: var(--primary); |
| border: 1px solid rgba(99, 102, 241, 0.2); |
| } |
| |
| .nav-item i { |
| width: 20px; |
| text-align: center; |
| } |
| |
| |
| main { |
| flex: 1; |
| padding: 2rem; |
| overflow-y: auto; |
| position: relative; |
| } |
| |
| .panel-header { |
| margin-bottom: 2rem; |
| } |
| |
| .panel-header h1 { |
| font-size: 2rem; |
| margin-bottom: 0.5rem; |
| } |
| |
| .panel-header p { |
| color: var(--text-muted); |
| } |
| |
| |
| .upload-zone { |
| border: 2px dashed var(--border); |
| border-radius: 16px; |
| padding: 4rem 2rem; |
| text-align: center; |
| background: rgba(255,255,255,0.02); |
| transition: all 0.3s ease; |
| cursor: pointer; |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .upload-zone:hover, .upload-zone.drag-over { |
| border-color: var(--primary); |
| background: rgba(99, 102, 241, 0.05); |
| } |
| |
| .upload-zone i { |
| font-size: 3rem; |
| color: var(--primary); |
| margin-bottom: 1rem; |
| } |
| |
| .upload-zone h3 { |
| margin-bottom: 0.5rem; |
| } |
| |
| .upload-zone p { |
| color: var(--text-muted); |
| font-size: 0.9rem; |
| } |
| |
| .file-info { |
| margin-top: 1rem; |
| display: none; |
| background: var(--bg-panel); |
| padding: 1rem; |
| border-radius: 8px; |
| border: 1px solid var(--border); |
| align-items: center; |
| justify-content: space-between; |
| } |
| |
| .file-details { |
| display: flex; |
| align-items: center; |
| gap: 10px; |
| } |
| |
| |
| .controls-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
| gap: 1.5rem; |
| margin-top: 2rem; |
| } |
| |
| .input-group label { |
| display: block; |
| margin-bottom: 0.5rem; |
| font-size: 0.9rem; |
| color: var(--text-muted); |
| } |
| |
| .form-select, .form-input { |
| width: 100%; |
| background: var(--bg-panel); |
| border: 1px solid var(--border); |
| color: var(--text-main); |
| padding: 12px; |
| border-radius: 8px; |
| font-family: inherit; |
| transition: border-color 0.2s; |
| } |
| |
| .form-select:focus, .form-input:focus { |
| border-color: var(--primary); |
| } |
| |
| |
| .action-btn { |
| margin-top: 2rem; |
| width: 100%; |
| padding: 16px; |
| background: linear-gradient(90deg, var(--primary) 0%, #4f46e5 100%); |
| color: white; |
| border: none; |
| border-radius: 12px; |
| font-size: 1rem; |
| font-weight: 600; |
| cursor: pointer; |
| transition: all 0.3s ease; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| gap: 10px; |
| box-shadow: 0 4px 20px var(--primary-glow); |
| } |
| |
| .action-btn:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 8px 30px var(--primary-glow); |
| } |
| |
| .action-btn:disabled { |
| opacity: 0.5; |
| cursor: not-allowed; |
| transform: none; |
| } |
| |
| |
| .results-area { |
| margin-top: 3rem; |
| display: none; |
| animation: fadeIn 0.5s ease; |
| } |
| |
| .result-card { |
| background: var(--bg-panel); |
| border: 1px solid var(--border); |
| border-radius: 12px; |
| overflow: hidden; |
| } |
| |
| .result-header { |
| padding: 1rem 1.5rem; |
| border-bottom: 1px solid var(--border); |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| } |
| |
| .result-content { |
| padding: 1.5rem; |
| min-height: 200px; |
| max-height: 400px; |
| overflow-y: auto; |
| font-family: 'JetBrains Mono', monospace; |
| font-size: 0.9rem; |
| line-height: 1.6; |
| color: var(--text-muted); |
| } |
| |
| |
| .audio-player-wrapper { |
| padding: 2rem; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| gap: 1rem; |
| } |
| |
| .waveform { |
| width: 100%; |
| height: 60px; |
| background: rgba(255,255,255,0.03); |
| border-radius: 8px; |
| position: relative; |
| overflow: hidden; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| gap: 3px; |
| } |
| |
| .bar { |
| width: 4px; |
| background: var(--primary); |
| border-radius: 2px; |
| animation: wave 1s ease-in-out infinite; |
| } |
| |
| |
| .progress-container { |
| width: 100%; |
| background-color: rgba(255,255,255,0.1); |
| border-radius: 10px; |
| margin-top: 1rem; |
| height: 6px; |
| overflow: hidden; |
| display: none; |
| } |
| |
| .progress-bar { |
| width: 0%; |
| height: 100%; |
| background: var(--primary); |
| border-radius: 10px; |
| transition: width 0.3s linear; |
| } |
| |
| .status-text { |
| margin-top: 10px; |
| font-size: 0.9rem; |
| color: var(--text-muted); |
| text-align: center; |
| min-height: 1.5em; |
| } |
| |
| |
| @keyframes pulse { |
| 0% { opacity: 1; } |
| 50% { opacity: 0.5; } |
| 100% { opacity: 1; } |
| } |
| |
| @keyframes wave { |
| 0%, 100% { height: 10%; } |
| 50% { height: 100%; } |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| |
| |
| @media (max-width: 768px) { |
| .app-container { |
| flex-direction: column; |
| height: auto; |
| } |
| |
| aside { |
| width: 100%; |
| flex-direction: row; |
| overflow-x: auto; |
| padding: 1rem; |
| border-bottom: 1px solid var(--border); |
| border-right: none; |
| } |
| |
| .nav-item { |
| white-space: nowrap; |
| } |
| |
| header { |
| padding: 0 1rem; |
| } |
| |
| .built-with { |
| display: none; |
| } |
| } |
| </style> |
| </head> |
| <body> |
|
|
| <header> |
| <div class="logo"> |
| <i class="fa-solid fa-microphone-lines"></i> |
| OllamaStream |
| </div> |
| <div class="status-indicator"> |
| <div class="status-dot"></div> |
| <span>Ollama Server Connected (Local)</span> |
| </div> |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">Built with anycoder</a> |
| </header> |
|
|
| <div class="app-container"> |
| <aside> |
| <div class="nav-item active" onclick="switchTab('transcribe')"> |
| <i class="fa-solid fa-file-audio"></i> |
| Transcribe |
| </div> |
| <div class="nav-item" onclick="switchTab('translate')"> |
| <i class="fa-solid fa-language"></i> |
| Translate |
| </div> |
| <div class="nav-item" onclick="switchTab('dub')"> |
| <i class="fa-solid fa-wand-magic-sparkles"></i> |
| Dub Video |
| </div> |
| </aside> |
|
|
| <main id="main-content"> |
| |
| </main> |
| </div> |
|
|
| <script> |
| // Application State |
| const state = { |
| activeTab: 'transcribe', |
| uploadedFile: null, |
| isProcessing: false, |
| progress: 0 |
| }; |
| |
| // Available Models (Simulated) |
| const models = { |
| transcribe: ['whisper', 'whisper-large', 'faster-whisper'], |
| llm: ['llama3', 'mistral', 'gemma'] |
| }; |
| |
| // DOM Elements |
| const mainContent = document.getElementById('main-content'); |
| |
| // Initial Render |
| renderInterface(); |
| |
| // --- Functions --- |
| |
| function switchTab(tabName) { |
| state.activeTab = tabName; |
| state.uploadedFile = null; // Reset file on tab switch for simplicity |
| state.progress = 0; |
| |
| // Update UI tabs |
| document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active')); |
| event.currentTarget.classList.add('active'); |
| |
| renderInterface(); |
| } |
| |
| function renderInterface() { |
| let html = ''; |
| |
| if (state.activeTab === 'transcribe') { |
| html = getTranscribeTemplate(); |
| } else if (state.activeTab === 'translate') { |
| html = getTranslateTemplate(); |
| } else if (state.activeTab === 'dub') { |
| html = getDubTemplate(); |
| } |
| |
| mainContent.innerHTML = html; |
| |
| // Re-attach listeners after render |
| attachListeners(); |
| } |
| |
| // --- Templates --- |
| |
| function getTranscribeTemplate() { |
| return ` |
| <div class="panel-header"> |
| <h1>Audio Transcription</h1> |
| <p>Convert audio to text using local Whisper models via Ollama.</p> |
| </div> |
| |
| <div class="upload-zone" id="drop-zone"> |
| <i class="fa-solid fa-cloud-arrow-up"></i> |
| <h3>Drag & Drop Audio File</h3> |
| <p>MP3, WAV, M4A, FLAC supported</p> |
| <input type="file" id="file-input" hidden accept="audio/*"> |
| </div> |
| |
| <div class="file-info" id="file-info"> |
| <div class="file-details"> |
| <i class="fa-solid fa-file-audio" style="color: var(--primary)"></i> |
| <div> |
| <div id="filename" style="font-weight:500">filename.mp3</div> |
| <div id="filesize" style="font-size:0.8rem; color:var(--text-muted)">2.4 MB</div> |
| </div> |
| </div> |
| <i class="fa-solid fa-xmark" style="cursor:pointer; color:var(--text-muted)" onclick="removeFile()"></i> |
| </div> |
| |
| <div class="controls-grid"> |
| <div class="input-group"> |
| <label>Model</label> |
| <select class="form-select" id="model-select"> |
| ${models.transcribe.map(m => `<option value="${m}">${m}</option>`).join('')} |
| </select> |
| </div> |
| <div class="input-group"> |
| <label>Language (Optional)</label> |
| <select class="form-select"> |
| <option value="">Auto Detect</option> |
| <option value="en">English</option> |
| <option value="es">Spanish</option> |
| <option value="fr">French</option> |
| <option value="de">German</option> |
| </select> |
| </div> |
| </div> |
| |
| <button class="action-btn" id="process-btn" onclick="processTask('transcribe')"> |
| <i class="fa-solid fa-play"></i> Start Transcription |
| </button> |
| |
| ${getProgressTemplate()} |
| ${getResultsTemplate('transcription')} |
| `; |
| } |
| |
| function getTranslateTemplate() { |
| return ` |
| <div class="panel-header"> |
| <h1>Smart Translate</h1> |
| <p>Translate content using Llama 3 for context-aware accuracy.</p> |
| </div> |
| |
| <div class="upload-zone" id="drop-zone"> |
| <i class="fa-solid fa-file-import"></i> |
| <h3>Drag Audio or Text File</h3> |
| <p>Supports SRT, VTT, TXT, and Audio files</p> |
| <input type="file" id="file-input" hidden> |
| </div> |
| |
| <div class="file-info" id="file-info"> |
| <div class="file-details"> |
| <i class="fa-solid fa-file" style="color: var(--accent)"></i> |
| <div> |
| <div id="filename" style="font-weight:500">interview.srt</div> |
| </div> |
| </div> |
| <i class="fa-solid fa-xmark" style="cursor:pointer; color:var(--text-muted)" onclick="removeFile()"></i> |
| </div> |
| |
| <div class="controls-grid"> |
| <div class="input-group"> |
| <label>Source Language</label> |
| <select class="form-select"> |
| <option value="auto">Auto Detect</option> |
| <option value="en">English</option> |
| <option value="es">Spanish</option> |
| </select> |
| </div> |
| <div class="input-group"> |
| <label>Target Language</label> |
| <select class="form-select"> |
| <option value="es">Spanish</option> |
| <option value="fr">French</option> |
| <option value="de">German</option> |
| <option value="jp">Japanese</option> |
| </select> |
| </div> |
| <div class="input-group"> |
| <label>AI Model</label> |
| <select class="form-select"> |
| ${models.llm.map(m => `<option value="${m}">${m}</option>`).join('')} |
| </select> |
| </div> |
| </div> |
| |
| <button class="action-btn" id="process-btn" onclick="processTask('translate')"> |
| <i class="fa-solid fa-language"></i> Translate Content |
| </button> |
| |
| ${getProgressTemplate()} |
| ${getResultsTemplate('translation')} |
| `; |
| } |
| |
| function getDubTemplate() { |
| return ` |
| <div class="panel-header"> |
| <h1>AI Dubbing</h1> |
| <p>Replace original voice track with a generated AI voice clone.</p> |
| </div> |
| |
| <div class="upload-zone" id="drop-zone"> |
| <i class="fa-solid fa-video"></i> |
| <h3>Drag Video File</h3> |
| <p>MP4, MOV, WEBM supported</p> |
| <input type="file" id="file-input" hidden accept="video/*"> |
| </div> |
| |
| <div class="file-info" id="file-info"> |
| <div class="file-details"> |
| <i class="fa-solid fa-film" style="color: var(--danger)"></i> |
| <div> |
| <div id="filename" style="font-weight:500">presentation.mp4</div> |
| <div id="filesize" style="font-size:0.8rem; color:var(--text-muted)">15 MB</div> |
| </div> |
| </div> |
| <i class="fa-solid fa-xmark" style="cursor:pointer; color:var(--text-muted)" onclick="removeFile()"></i> |
| </div> |
| |
| <div class="controls-grid"> |
| <div class="input-group"> |
| <label>Target Language</label> |
| <select class="form-select"> |
| <option value="en-US">English (US)</option> |
| <option value="en-UK">English (UK)</option> |
| <option value="es-ES">Spanish (Spain)</option> |
| <option value="jp-JP">Japanese</option> |
| </select> |
| </div> |
| <div class="input-group"> |
| <label>Voice Model</label> |
| <select class="form-select"> |
| <option value="alloy">Alloy (Neutral)</option> |
| <option value="echo">Echo (British)</option> |
| <option value="shimmer">Shimmer (Female)</option> |
| </select> |
| </div> |
| </div> |
| |
| <button class="action-btn" id="process-btn" onclick="processTask('dub')"> |
| <i class="fa-solid fa-wand-magic-sparkles"></i> Generate Dub |
| </button> |
| |
| ${getProgressTemplate()} |
| ${getResultsTemplate('dub')} |
| `; |
| } |
| |
| function getProgressTemplate() { |
| return ` |
| <div class="progress-container" id="progress-container"> |
| <div class="progress-bar" id="progress-bar"></div> |
| </div> |
| <div class="status-text" id="status-text"></div> |
| `; |
| } |
| |
| function getResultsTemplate(type) { |
| let content = ''; |
| if (type === 'transcription') { |
| content = `<p style="color:var(--text-muted); text-align:center; padding:2rem;">No transcription generated yet.</p>`; |
| } else if (type === 'translation') { |
| content = `<p style="color:var(--text-muted); text-align:center; padding:2rem;">No translation generated yet.</p>`; |
| } else if (type === 'dub') { |
| content = ` |
| <div class="audio-player-wrapper"> |
| <div class="waveform" id="waveform"> |
| |
| </div> |
| <audio controls style="width: 100%; margin-top:1rem;"> |
| <source src="" type="audio/mpeg"> |
| Your browser does not support the audio element. |
| </audio> |
| <div style="display:flex; gap:10px; margin-top:10px;"> |
| <button class="form-select" style="width:auto; cursor:pointer;">Download MP3</button> |
| <button class="form-select" style="width:auto; cursor:pointer;">Download Video</button> |
| </div> |
| </div> |
| `; |
| } |
| |
| return ` |
| <div class="results-area" id="results-area"> |
| <div class="result-card"> |
| <div class="result-header"> |
| <span style="font-weight:600">Output Result</span> |
| <button class="form-select" style="width:auto; padding:4px 8px; cursor:pointer; font-size:0.8rem;">Copy / Save</button> |
| </div> |
| <div class="result-content" id="result-content"> |
| ${content} |
| </div> |
| </div> |
| </div> |
| `; |
| } |
| |
| // --- Logic --- |
| |
| function attachListeners() { |
| const dropZone = document.getElementById('drop-zone'); |
| const fileInput = document.getElementById('file-input'); |
| |
| if(dropZone) { |
| dropZone.addEventListener('click', () => fileInput.click()); |
| |
| dropZone.addEventListener('dragover', (e) => { |
| e.preventDefault(); |
| dropZone.classList.add('drag-over'); |
| }); |
| |
| dropZone.addEventListener('dragleave', () => { |
| dropZone.classList.remove('drag-over'); |
| }); |
| |
| dropZone.addEventListener('drop', (e) => { |
| e.preventDefault(); |
| dropZone.classList.remove('drag-over'); |
| if (e.dataTransfer.files.length) { |
| handleFile(e.dataTransfer.files[0]); |
| } |
| }); |
| } |
| |
| if(fileInput) { |
| fileInput.addEventListener('change', (e) => { |
| if (e.target.files.length) { |
| handleFile(e.target.files[0]); |
| } |
| }); |
| } |
| } |
| |
| function handleFile(file) { |
| state.uploadedFile = file; |
| |
| // Update UI |
| document.getElementById('filename').innerText = file.name; |
| document.getElementById('filesize') ? document.getElementById('filesize').innerText = (file.size / 1024 / 1024).toFixed(2) + ' MB' : null; |
| |
| document.querySelector('.upload-zone').style.display = 'none'; |
| document.getElementById('file-info').style.display = 'flex'; |
| } |
| |
| function removeFile() { |
| state.uploadedFile = null; |
| document.querySelector('.upload-zone').style.display = 'block'; |
| document.getElementById('file-info').style.display = 'none'; |
| document.getElementById('file-input').value = ''; |
| } |
| |
| function processTask(task) { |
| if (!state.uploadedFile) { |
| alert('Please upload a file first.'); |
| return; |
| } |
| |
| const btn = document.getElementById('process-btn'); |
| const progressContainer = document.getElementById('progress-container'); |
| const progressBar = document.getElementById('progress-bar'); |
| const statusText = document.getElementById('status-text'); |
| const resultsArea = document.getElementById('results-area'); |
| |
| // Reset UI |
| btn.disabled = true; |
| btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Processing...'; |
| progressContainer.style.display = 'block'; |
| resultsArea.style.display = 'none'; |
| state.progress = 0; |
| |
| // Simulation Loop |
| const interval = setInterval(() => { |
| state.progress += Math.random() * 10; |
| |
| if (state.progress > 100) state.progress = 100; |
| |
| progressBar.style.width = state.progress + '%'; |
| |
| if (state.progress < 30) { |
| statusText.innerText = `Loading model context...`; |
| } else if (state.progress < 70) { |
| statusText.innerText = `Analyzing ${state.uploadedFile.name}...`; |
| } else if (state.progress < 95) { |
| statusText.innerText = task === 'dub' ? 'Synthesizing audio track...' : 'Generating text...'; |
| } else { |
| statusText.innerText = 'Finalizing...'; |
| } |
| |
| if (state.progress >= 100) { |
| clearInterval(interval); |
| finishTask(task); |
| } |
| }, 300); |
| } |
| |
| function finishTask(task) { |
| const btn = document.getElementById('process-btn'); |
| const statusText = document.getElementById('status-text'); |
| const resultsArea = document.getElementById('results-area'); |
| const resultContent = document.getElementById('result-content'); |
| |
| btn.disabled = false; |
| btn.innerHTML = `<i class="fa-solid fa-check"></i> Done`; |
| statusText.innerText = 'Processing Complete'; |
| |
| resultsArea.style.display = 'block'; |
| |
| // Simulate results based on task |
| if (task === 'transcribe') { |
| resultContent.innerHTML = ` |
| <div style="margin-bottom:10px; border-bottom:1px solid #333; padding-bottom:10px;"> |
| <span style="color:var(--primary); font-size:0.8rem;">00:00:05</span> |
| <p>Welcome to the demonstration of the new AI system.</p> |
| </div> |
| <div style="margin-bottom:10px; border-bottom:1px solid #333; padding-bottom:10px;"> |
| <span style="color:var(--primary); font-size:0.8rem;">00:00:12</span> |
| <p>Today we are going to explore how local models can process media.</p> |
| </div> |
| <div> |
| <span style="color:var(--primary); font-size:0.8rem;">00:00:20</span> |
| <p>This is a simulated transcription for the UI demo.</p> |
| </div> |
| `; |
| } else if (task === 'translate') { |
| resultContent.innerHTML = ` |
| <p><strong>Original:</strong> This is a simulated transcript.</p> |
| <br> |
| <p><strong>Translated (Spanish):</strong> Esta es una transcripción simulada.</p> |
| `; |
| } else if (task === 'dub') { |
| // Generate fake waveform bars |
| const waveform = document.getElementById('waveform'); |
| if(waveform) { |
| for(let i=0; i<30; i++) { |
| let h = Math.floor(Math.random() * 40) + 10; |
| let bar = document.createElement('div'); |
| bar.className = 'bar'; |
| bar.style.height = h + '%'; |
| bar.style.animationDelay = (i * 0.1) + 's'; |
| waveform.appendChild(bar); |
| } |
| } |
| // Result content is already in template, just make sure it shows |
| } |
| } |
| </script> |
| </body> |
| </html> |