Spaces:
Sleeping
Sleeping
ranar110
Major Update: Added File Upload support, merged audio players, and consolidated all fixes
f1e4300
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Voice Detection Verification Tool</title> | |
| <style> | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background-color: #f4f4f9; | |
| color: #333; | |
| padding: 20px; | |
| } | |
| .container { | |
| max-width: 600px; | |
| margin: 0 auto; | |
| background: #fff; | |
| padding: 30px; | |
| border-radius: 8px; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); | |
| } | |
| h1 { | |
| text-align: center; | |
| color: #444; | |
| } | |
| .form-group { | |
| margin-bottom: 20px; | |
| } | |
| label { | |
| display: block; | |
| margin-bottom: 5px; | |
| font-weight: bold; | |
| } | |
| input[type="text"], | |
| input[type="url"], | |
| textarea { | |
| width: 100%; | |
| padding: 10px; | |
| border: 1px solid #ddd; | |
| border-radius: 4px; | |
| box-sizing: border-box; | |
| } | |
| input[type="file"] { | |
| border: 1px solid #ddd; | |
| padding: 10px; | |
| width: 100%; | |
| border-radius: 4px; | |
| background: #fafafa; | |
| } | |
| button { | |
| width: 100%; | |
| padding: 12px; | |
| background-color: #007bff; | |
| color: white; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| } | |
| button:hover { | |
| background-color: #0056b3; | |
| } | |
| #response { | |
| margin-top: 20px; | |
| background: #f8f9fa; | |
| padding: 15px; | |
| border-radius: 4px; | |
| border: 1px solid #ddd; | |
| word-wrap: break-word; | |
| white-space: pre-wrap; | |
| display: none; | |
| } | |
| .audio-player { | |
| margin-top: 15px; | |
| padding: 15px; | |
| background: #f0f8ff; | |
| border-radius: 4px; | |
| border: 1px solid #b3d9ff; | |
| display: none; | |
| } | |
| .audio-player audio { | |
| width: 100%; | |
| margin-top: 10px; | |
| } | |
| .audio-label { | |
| font-weight: bold; | |
| color: #0066cc; | |
| margin-bottom: 5px; | |
| } | |
| .error { | |
| color: #dc3545; | |
| } | |
| .success { | |
| color: #28a745; | |
| } | |
| .tabs { | |
| display: flex; | |
| margin-bottom: 20px; | |
| border-bottom: 1px solid #ddd; | |
| } | |
| .tab { | |
| padding: 10px 20px; | |
| cursor: pointer; | |
| border-bottom: 2px solid transparent; | |
| } | |
| .tab.active { | |
| border-bottom: 2px solid #007bff; | |
| color: #007bff; | |
| font-weight: bold; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>Voice Detection Tool</h1> | |
| <div style="background: #e9ecef; padding: 15px; border-radius: 4px; margin-bottom: 20px;"> | |
| <h3>Generate AI Audio (Murf.ai)</h3> | |
| <div class="form-group"> | |
| <label for="murfText">Text to Speak</label> | |
| <input type="text" id="murfText" placeholder="Hello, this is a test."> | |
| </div> | |
| <div class="form-group"> | |
| <label for="murfKey">Murf API Key</label> | |
| <input type="text" id="murfKey" placeholder="Enter Murf API Key"> | |
| </div> | |
| <button onclick="generateAudio()" style="background-color: #28a745;">Generate Audio</button> | |
| <div id="genStatus" style="margin-top:10px;"></div> | |
| <!-- Audio Player for Generated AI Audio --> | |
| <div id="generatedAudioPlayer" class="audio-player"> | |
| <div class="audio-label">🎵 Generated AI Audio (Click to Play)</div> | |
| <audio id="generatedAudio" controls> | |
| Your browser does not support the audio element. | |
| </audio> | |
| </div> | |
| </div> | |
| <div class="form-group"> | |
| <label for="apiUrl">API Endpoint URL</label> | |
| <input type="text" id="apiUrl" value="/detect" placeholder="/detect"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="apiKey">API Key (Authorization)</label> | |
| <input type="text" id="apiKey" value="my_secret_key_123" placeholder="Enter API Key"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="message">Test Message (Optional)</label> | |
| <textarea id="message" rows="2" placeholder="Describe this test..."></textarea> | |
| </div> | |
| <h3>Select Audio Source</h3> | |
| <div class="tabs"> | |
| <div class="tab active" onclick="switchTab('url')">Audio URL</div> | |
| <div class="tab" onclick="switchTab('file')">Upload File</div> | |
| </div> | |
| <!-- URL Input Section --> | |
| <div id="urlSection" class="form-group"> | |
| <label for="audioUrl">Audio File URL (MP3)</label> | |
| <input type="url" id="audioUrl" placeholder="https://example.com/sample.mp3" | |
| oninput="updateTestAudioPlayerFromUrl()"> | |
| </div> | |
| <!-- File Input Section --> | |
| <div id="fileSection" class="form-group" style="display:none;"> | |
| <label for="audioFile">Upload Audio File (MP3, WAV, M4A)</label> | |
| <input type="file" id="audioFile" accept="audio/*" onchange="updateTestAudioPlayerFromFile()"> | |
| </div> | |
| <!-- Audio Player for Test Audio --> | |
| <div id="testAudioPlayer" class="audio-player"> | |
| <div class="audio-label">🎧 Test Audio Preview (Click to Play)</div> | |
| <audio id="testAudio" controls> | |
| Your browser does not support the audio element. | |
| </audio> | |
| </div> | |
| <button onclick="testEndpoint()">Test Endpoint</button> | |
| <div id="response"></div> | |
| </div> | |
| <script> | |
| let currentMode = 'url'; // 'url' or 'file' | |
| function switchTab(mode) { | |
| currentMode = mode; | |
| // Update tabs | |
| document.querySelectorAll('.tab').forEach(t => t.classList.remove('active')); | |
| document.querySelector(`.tab[onclick="switchTab('${mode}')"]`).classList.add('active'); | |
| // Show/hide sections | |
| if (mode === 'url') { | |
| document.getElementById('urlSection').style.display = 'block'; | |
| document.getElementById('fileSection').style.display = 'none'; | |
| updateTestAudioPlayerFromUrl(); | |
| } else { | |
| document.getElementById('urlSection').style.display = 'none'; | |
| document.getElementById('fileSection').style.display = 'block'; | |
| updateTestAudioPlayerFromFile(); | |
| } | |
| } | |
| async function generateAudio() { | |
| const text = document.getElementById('murfText').value; | |
| const murfKey = document.getElementById('murfKey').value; | |
| const statusDiv = document.getElementById('genStatus'); | |
| const audioUrlInput = document.getElementById('audioUrl'); | |
| if (!text || !murfKey) { | |
| alert("Please enter Text and Murf API Key"); | |
| return; | |
| } | |
| statusDiv.innerHTML = "Generating..."; | |
| statusDiv.className = ''; | |
| try { | |
| const response = await fetch('/generate', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| text: text, | |
| murf_api_key: murfKey | |
| }) | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| statusDiv.innerHTML = "Generated! URL copied to input below."; | |
| statusDiv.className = 'success'; | |
| // Switch to URL mode | |
| switchTab('url'); | |
| audioUrlInput.value = data.audio_url; | |
| // Show and load the generated audio player | |
| const generatedPlayer = document.getElementById('generatedAudioPlayer'); | |
| const generatedAudio = document.getElementById('generatedAudio'); | |
| generatedAudio.src = data.audio_url; | |
| generatedPlayer.style.display = 'block'; | |
| // Also update the test audio player | |
| updateTestAudioPlayerFromUrl(); | |
| } else { | |
| statusDiv.innerHTML = "Error: " + (data.detail || JSON.stringify(data)); | |
| statusDiv.className = 'error'; | |
| } | |
| } catch (e) { | |
| statusDiv.innerHTML = "Error: " + e.message; | |
| statusDiv.className = 'error'; | |
| } | |
| } | |
| function updateTestAudioPlayerFromUrl() { | |
| const audioUrl = document.getElementById('audioUrl').value; | |
| const testPlayer = document.getElementById('testAudioPlayer'); | |
| const testAudio = document.getElementById('testAudio'); | |
| if (currentMode === 'url' && audioUrl && audioUrl.trim() !== '') { | |
| testAudio.src = audioUrl; | |
| testPlayer.style.display = 'block'; | |
| } else if (currentMode === 'url') { | |
| testPlayer.style.display = 'none'; | |
| } | |
| } | |
| function updateTestAudioPlayerFromFile() { | |
| const fileInput = document.getElementById('audioFile'); | |
| const testPlayer = document.getElementById('testAudioPlayer'); | |
| const testAudio = document.getElementById('testAudio'); | |
| if (currentMode === 'file' && fileInput.files && fileInput.files[0]) { | |
| const file = fileInput.files[0]; | |
| const fileUrl = URL.createObjectURL(file); | |
| testAudio.src = fileUrl; | |
| testPlayer.style.display = 'block'; | |
| } else if (currentMode === 'file') { | |
| testPlayer.style.display = 'none'; | |
| } | |
| } | |
| async function testEndpoint() { | |
| const apiUrl = document.getElementById('apiUrl').value; | |
| const apiKey = document.getElementById('apiKey').value; | |
| const message = document.getElementById('message').value; | |
| const responseDiv = document.getElementById('response'); | |
| responseDiv.style.display = 'block'; | |
| responseDiv.innerHTML = 'Sending request...'; | |
| responseDiv.className = ''; | |
| const formData = new FormData(); | |
| formData.append('message', message); | |
| // Add authorization header | |
| const headers = { | |
| 'x-api-key': apiKey | |
| }; | |
| if (currentMode === 'url') { | |
| const audioUrl = document.getElementById('audioUrl').value; | |
| if (!audioUrl) { | |
| alert("Please enter an Audio URL"); | |
| return; | |
| } | |
| formData.append('audio_url', audioUrl); | |
| } else { | |
| const fileInput = document.getElementById('audioFile'); | |
| if (!fileInput.files || !fileInput.files[0]) { | |
| alert("Please select a file to upload"); | |
| return; | |
| } | |
| formData.append('file', fileInput.files[0]); | |
| } | |
| try { | |
| const response = await fetch(apiUrl, { | |
| method: 'POST', | |
| headers: headers, | |
| body: formData | |
| }); | |
| const data = await response.json(); | |
| if (response.ok) { | |
| responseDiv.innerHTML = '<strong>Success!</strong>\n' + JSON.stringify(data, null, 2); | |
| responseDiv.className = 'success'; | |
| } else { | |
| responseDiv.innerHTML = '<strong>Error ' + response.status + ':</strong>\n' + JSON.stringify(data, null, 2); | |
| responseDiv.className = 'error'; | |
| } | |
| } catch (error) { | |
| responseDiv.innerHTML = '<strong>Network Error:</strong> ' + error.message; | |
| responseDiv.className = 'error'; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |