Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Speech-to-Speech Demo</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| } | |
| .container { | |
| background: white; | |
| padding: 30px; | |
| border-radius: 10px; | |
| box-shadow: 0 10px 30px rgba(0,0,0,0.1); | |
| } | |
| h1 { | |
| color: #333; | |
| text-align: center; | |
| margin-bottom: 30px; | |
| } | |
| .upload-section, .result-section { | |
| margin: 20px 0; | |
| padding: 20px; | |
| border: 2px dashed #ddd; | |
| border-radius: 8px; | |
| text-align: center; | |
| } | |
| .upload-section.dragover { | |
| border-color: #667eea; | |
| background-color: #f0f4ff; | |
| } | |
| input[type="file"] { | |
| margin: 10px 0; | |
| } | |
| button { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| border: none; | |
| padding: 12px 24px; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| margin: 5px; | |
| transition: transform 0.2s; | |
| } | |
| button:hover { | |
| transform: translateY(-2px); | |
| } | |
| button:disabled { | |
| background: #ccc; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| .result-text { | |
| background: #f8f9fa; | |
| padding: 15px; | |
| border-radius: 6px; | |
| margin: 10px 0; | |
| border-left: 4px solid #667eea; | |
| } | |
| .loading { | |
| display: none; | |
| color: #667eea; | |
| margin: 10px 0; | |
| } | |
| .error { | |
| color: #dc3545; | |
| background: #f8d7da; | |
| padding: 10px; | |
| border-radius: 4px; | |
| margin: 10px 0; | |
| } | |
| audio { | |
| width: 100%; | |
| margin: 10px 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🎤 Speech-to-Speech Translator</h1> | |
| <div class="upload-section" id="uploadSection"> | |
| <h3>Upload Audio File</h3> | |
| <p>Drag and drop an audio file here or click to select</p> | |
| <input type="file" id="audioFile" accept=".wav,.mp3,.m4a,.flac,.ogg"> | |
| <br> | |
| <button onclick="processAudio()" id="processBtn">🔄 Translate Speech</button> | |
| </div> | |
| <div class="loading" id="loading"> | |
| <p>🔄 Processing your audio... This may take a moment.</p> | |
| </div> | |
| <div class="result-section" id="results" style="display: none;"> | |
| <h3>Results</h3> | |
| <div id="transcription" style="display: none;"> | |
| <h4>📝 Original Text (English):</h4> | |
| <div class="result-text" id="transcriptionText"></div> | |
| </div> | |
| <div id="translation" style="display: none;"> | |
| <h4>🌍 Translation (Spanish):</h4> | |
| <div class="result-text" id="translationText"></div> | |
| </div> | |
| <div id="audioResult" style="display: none;"> | |
| <h4>🔊 Generated Audio:</h4> | |
| <audio controls id="resultAudio"></audio> | |
| <br> | |
| <button onclick="downloadAudio()" id="downloadBtn">⬇️ Download Audio</button> | |
| </div> | |
| </div> | |
| <div id="error" class="error" style="display: none;"></div> | |
| </div> | |
| <script> | |
| // const API_BASE = 'http://localhost:5000'; | |
| const API_BASE = window.location.origin; | |
| let currentAudioId = null; | |
| // Drag and drop functionality | |
| const uploadSection = document.getElementById('uploadSection'); | |
| const fileInput = document.getElementById('audioFile'); | |
| uploadSection.addEventListener('dragover', (e) => { | |
| e.preventDefault(); | |
| uploadSection.classList.add('dragover'); | |
| }); | |
| uploadSection.addEventListener('dragleave', () => { | |
| uploadSection.classList.remove('dragover'); | |
| }); | |
| uploadSection.addEventListener('drop', (e) => { | |
| e.preventDefault(); | |
| uploadSection.classList.remove('dragover'); | |
| const files = e.dataTransfer.files; | |
| if (files.length > 0) { | |
| fileInput.files = files; | |
| } | |
| }); | |
| // uploadSection.addEventListener('click', () => { | |
| // fileInput.click(); | |
| // }); | |
| uploadSection.querySelector('p').addEventListener('click', () => { | |
| fileInput.click(); | |
| }); | |
| async function processAudio() { | |
| const file = fileInput.files[0]; | |
| if (!file) { | |
| showError('Please select an audio file'); | |
| return; | |
| } | |
| showLoading(true); | |
| hideError(); | |
| hideResults(); | |
| try { | |
| const formData = new FormData(); | |
| formData.append('audio', file); | |
| formData.append('target_lang', 'es'); | |
| const response = await fetch(`${API_BASE}/speech-to-speech`, { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| const result = await response.json(); | |
| if (!response.ok) { | |
| throw new Error(result.error || 'Processing failed'); | |
| } | |
| // Display results | |
| document.getElementById('transcriptionText').textContent = result.transcription; | |
| document.getElementById('translationText').textContent = result.translation; | |
| // Set up audio | |
| currentAudioId = result.audio_id; | |
| const audioElement = document.getElementById('resultAudio'); | |
| audioElement.src = `${API_BASE}/audio/${result.audio_id}`; | |
| showResults(); | |
| } catch (error) { | |
| showError(`Error: ${error.message}`); | |
| } finally { | |
| showLoading(false); | |
| } | |
| } | |
| async function downloadAudio() { | |
| if (!currentAudioId) return; | |
| const link = document.createElement('a'); | |
| link.href = `${API_BASE}/audio/${currentAudioId}`; | |
| link.download = `translation_${currentAudioId}.mp3`; | |
| link.click(); | |
| } | |
| function showLoading(show) { | |
| document.getElementById('loading').style.display = show ? 'block' : 'none'; | |
| document.getElementById('processBtn').disabled = show; | |
| } | |
| function showError(message) { | |
| const errorDiv = document.getElementById('error'); | |
| errorDiv.textContent = message; | |
| errorDiv.style.display = 'block'; | |
| } | |
| function hideError() { | |
| document.getElementById('error').style.display = 'none'; | |
| } | |
| function showResults() { | |
| document.getElementById('results').style.display = 'block'; | |
| document.getElementById('transcription').style.display = 'block'; | |
| document.getElementById('translation').style.display = 'block'; | |
| document.getElementById('audioResult').style.display = 'block'; | |
| } | |
| function hideResults() { | |
| document.getElementById('results').style.display = 'none'; | |
| } | |
| // Clean up when page unloads | |
| window.addEventListener('beforeunload', () => { | |
| if (currentAudioId) { | |
| fetch(`${API_BASE}/cleanup/${currentAudioId}`, { method: 'DELETE' }); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |