Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Online Audio Recorder</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .waveform { | |
| background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 50%, #ec4899 100%); | |
| height: 100px; | |
| border-radius: 0.5rem; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .waveform::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: linear-gradient(90deg, | |
| rgba(255,255,255,0.1) 0%, | |
| rgba(255,255,255,0.3) 50%, | |
| rgba(255,255,255,0.1) 100%); | |
| animation: wave 2s linear infinite; | |
| transform: translateX(-100%); | |
| } | |
| @keyframes wave { | |
| 100% { | |
| transform: translateX(100%); | |
| } | |
| } | |
| .recording-animation { | |
| animation: pulse 1.5s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.7); | |
| } | |
| 70% { | |
| box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); | |
| } | |
| 100% { | |
| box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); | |
| } | |
| } | |
| .format-option:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 10px 20px rgba(0,0,0,0.1); | |
| } | |
| .format-option { | |
| transition: all 0.3s ease; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <div class="max-w-3xl mx-auto"> | |
| <div class="text-center mb-8"> | |
| <h1 class="text-4xl font-bold text-gray-800 mb-2">Online Audio Recorder</h1> | |
| <p class="text-gray-600">Record, play and download high-quality audio in multiple formats</p> | |
| </div> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8"> | |
| <div class="p-6"> | |
| <div class="waveform mb-6" id="waveform"></div> | |
| <div class="flex flex-col sm:flex-row justify-center items-center gap-4 mb-6"> | |
| <button id="recordBtn" class="bg-red-500 hover:bg-red-600 text-white font-bold py-3 px-6 rounded-full flex items-center gap-2 transition-all"> | |
| <i class="fas fa-microphone"></i> Start Recording | |
| </button> | |
| <button id="stopBtn" disabled class="bg-gray-300 text-gray-600 font-bold py-3 px-6 rounded-full flex items-center gap-2 transition-all"> | |
| <i class="fas fa-stop"></i> Stop | |
| </button> | |
| <button id="playBtn" disabled class="bg-blue-500 hover:bg-blue-600 text-white font-bold py-3 px-6 rounded-full flex items-center gap-2 transition-all"> | |
| <i class="fas fa-play"></i> Play | |
| </button> | |
| </div> | |
| <div class="text-center text-gray-500 mb-4"> | |
| <span id="timer">00:00</span> | |
| </div> | |
| </div> | |
| <div class="bg-gray-50 p-6 border-t border-gray-200"> | |
| <h3 class="text-lg font-semibold text-gray-800 mb-4">Download Options</h3> | |
| <div class="grid grid-cols-2 sm:grid-cols-4 gap-4"> | |
| <div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="wav"> | |
| <i class="fas fa-file-audio text-3xl text-blue-500 mb-2"></i> | |
| <p class="font-medium">WAV</p> | |
| <p class="text-sm text-gray-500">High quality</p> | |
| </div> | |
| <div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="mp3"> | |
| <i class="fas fa-file-audio text-3xl text-purple-500 mb-2"></i> | |
| <p class="font-medium">MP3</p> | |
| <p class="text-sm text-gray-500">Compressed</p> | |
| </div> | |
| <div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="ogg"> | |
| <i class="fas fa-file-audio text-3xl text-green-500 mb-2"></i> | |
| <p class="font-medium">OGG</p> | |
| <p class="text-sm text-gray-500">Web optimized</p> | |
| </div> | |
| <div class="format-option bg-white p-4 rounded-lg border border-gray-200 text-center cursor-pointer" data-format="aac"> | |
| <i class="fas fa-file-audio text-3xl text-red-500 mb-2"></i> | |
| <p class="font-medium">AAC</p> | |
| <p class="text-sm text-gray-500">Mobile friendly</p> | |
| </div> | |
| </div> | |
| <button id="downloadBtn" disabled class="mt-6 w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-3 px-4 rounded-lg flex items-center justify-center gap-2 transition-all"> | |
| <i class="fas fa-download"></i> Download Audio | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bg-white rounded-xl shadow-lg overflow-hidden"> | |
| <div class="p-6"> | |
| <h3 class="text-lg font-semibold text-gray-800 mb-4">Recording History</h3> | |
| <div id="recordingsList" class="space-y-3"> | |
| <div class="text-center text-gray-500 py-8"> | |
| <i class="fas fa-microphone-slash text-3xl mb-2"></i> | |
| <p>No recordings yet</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Audio recording variables | |
| let mediaRecorder; | |
| let audioChunks = []; | |
| let audioBlob; | |
| let audioUrl; | |
| let audioElement; | |
| let timerInterval; | |
| let seconds = 0; | |
| // DOM elements | |
| const recordBtn = document.getElementById('recordBtn'); | |
| const stopBtn = document.getElementById('stopBtn'); | |
| const playBtn = document.getElementById('playBtn'); | |
| const downloadBtn = document.getElementById('downloadBtn'); | |
| const timer = document.getElementById('timer'); | |
| const recordingsList = document.getElementById('recordingsList'); | |
| const formatOptions = document.querySelectorAll('.format-option'); | |
| let selectedFormat = 'wav'; // Default format | |
| // Format selection | |
| formatOptions.forEach(option => { | |
| option.addEventListener('click', function() { | |
| formatOptions.forEach(opt => opt.classList.remove('ring-2', 'ring-indigo-500')); | |
| this.classList.add('ring-2', 'ring-indigo-500'); | |
| selectedFormat = this.dataset.format; | |
| }); | |
| }); | |
| // Set first format as selected by default | |
| formatOptions[0].classList.add('ring-2', 'ring-indigo-500'); | |
| // Request permission to use microphone | |
| recordBtn.addEventListener('click', async function() { | |
| try { | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| mediaRecorder = new MediaRecorder(stream); | |
| audioChunks = []; | |
| mediaRecorder.ondataavailable = function(event) { | |
| audioChunks.push(event.data); | |
| }; | |
| mediaRecorder.onstop = function() { | |
| audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); | |
| audioUrl = URL.createObjectURL(audioBlob); | |
| // Create audio element for playback | |
| audioElement = new Audio(audioUrl); | |
| // Enable play and download buttons | |
| playBtn.disabled = false; | |
| downloadBtn.disabled = false; | |
| // Add to recordings list | |
| addRecordingToList(audioUrl); | |
| }; | |
| mediaRecorder.start(10); // Collect data every 10ms | |
| // Start timer | |
| seconds = 0; | |
| updateTimer(); | |
| timerInterval = setInterval(updateTimer, 1000); | |
| // Update UI | |
| recordBtn.disabled = true; | |
| recordBtn.classList.remove('bg-red-500', 'hover:bg-red-600'); | |
| recordBtn.classList.add('bg-gray-300', 'text-gray-600'); | |
| stopBtn.disabled = false; | |
| stopBtn.classList.remove('bg-gray-300', 'text-gray-600'); | |
| stopBtn.classList.add('bg-gray-800', 'hover:bg-gray-900', 'text-white'); | |
| // Add recording animation | |
| recordBtn.classList.add('recording-animation'); | |
| } catch (error) { | |
| console.error('Error accessing microphone:', error); | |
| alert('Could not access microphone. Please check permissions.'); | |
| } | |
| }); | |
| // Stop recording | |
| stopBtn.addEventListener('click', function() { | |
| if (mediaRecorder && mediaRecorder.state !== 'inactive') { | |
| mediaRecorder.stop(); | |
| // Stop all tracks in the stream | |
| mediaRecorder.stream.getTracks().forEach(track => track.stop()); | |
| // Clear timer | |
| clearInterval(timerInterval); | |
| // Update UI | |
| recordBtn.disabled = false; | |
| recordBtn.classList.remove('bg-gray-300', 'text-gray-600'); | |
| recordBtn.classList.add('bg-red-500', 'hover:bg-red-600'); | |
| stopBtn.disabled = true; | |
| stopBtn.classList.remove('bg-gray-800', 'hover:bg-gray-900', 'text-white'); | |
| stopBtn.classList.add('bg-gray-300', 'text-gray-600'); | |
| // Remove recording animation | |
| recordBtn.classList.remove('recording-animation'); | |
| } | |
| }); | |
| // Play recording | |
| playBtn.addEventListener('click', function() { | |
| if (audioElement) { | |
| if (audioElement.paused) { | |
| audioElement.play(); | |
| playBtn.innerHTML = '<i class="fas fa-pause"></i> Pause'; | |
| } else { | |
| audioElement.pause(); | |
| playBtn.innerHTML = '<i class="fas fa-play"></i> Play'; | |
| } | |
| } | |
| }); | |
| // Download recording | |
| downloadBtn.addEventListener('click', function() { | |
| if (audioBlob) { | |
| const a = document.createElement('a'); | |
| a.style.display = 'none'; | |
| a.href = audioUrl; | |
| a.download = `recording_${new Date().toISOString().slice(0, 19).replace(/[:T-]/g, '_')}.${selectedFormat}`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| setTimeout(() => { | |
| document.body.removeChild(a); | |
| window.URL.revokeObjectURL(audioUrl); | |
| }, 100); | |
| } | |
| }); | |
| // Update timer display | |
| function updateTimer() { | |
| seconds++; | |
| const minutes = Math.floor(seconds / 60); | |
| const remainingSeconds = seconds % 60; | |
| timer.textContent = `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`; | |
| } | |
| // Add recording to history list | |
| function addRecordingToList(url) { | |
| // Remove placeholder if it exists | |
| if (recordingsList.querySelector('.text-center')) { | |
| recordingsList.innerHTML = ''; | |
| } | |
| const recordingItem = document.createElement('div'); | |
| recordingItem.className = 'bg-gray-50 p-4 rounded-lg border border-gray-200 flex items-center justify-between'; | |
| recordingItem.innerHTML = ` | |
| <div class="flex items-center gap-3"> | |
| <i class="fas fa-microphone text-indigo-500"></i> | |
| <div> | |
| <p class="font-medium">Recording ${recordingsList.children.length + 1}</p> | |
| <p class="text-sm text-gray-500">${new Date().toLocaleString()}</p> | |
| </div> | |
| </div> | |
| <div class="flex gap-2"> | |
| <button class="play-history-btn bg-blue-500 hover:bg-blue-600 text-white p-2 rounded-full"> | |
| <i class="fas fa-play text-xs"></i> | |
| </button> | |
| <button class="download-history-btn bg-indigo-500 hover:bg-indigo-600 text-white p-2 rounded-full"> | |
| <i class="fas fa-download text-xs"></i> | |
| </button> | |
| </div> | |
| `; | |
| recordingsList.prepend(recordingItem); | |
| // Add event listeners to the new buttons | |
| const playBtn = recordingItem.querySelector('.play-history-btn'); | |
| const downloadBtn = recordingItem.querySelector('.download-history-btn'); | |
| const audio = new Audio(url); | |
| playBtn.addEventListener('click', function() { | |
| if (audio.paused) { | |
| // Stop all other audio elements first | |
| document.querySelectorAll('audio').forEach(a => { | |
| if (a !== audio) a.pause(); | |
| }); | |
| audio.play(); | |
| playBtn.innerHTML = '<i class="fas fa-pause text-xs"></i>'; | |
| } else { | |
| audio.pause(); | |
| playBtn.innerHTML = '<i class="fas fa-play text-xs"></i>'; | |
| } | |
| }); | |
| downloadBtn.addEventListener('click', function() { | |
| const a = document.createElement('a'); | |
| a.style.display = 'none'; | |
| a.href = url; | |
| a.download = `recording_${new Date().toISOString().slice(0, 19).replace(/[:T-]/g, '_')}.${selectedFormat}`; | |
| document.body.appendChild(a); | |
| a.click(); | |
| setTimeout(() => { | |
| document.body.removeChild(a); | |
| }, 100); | |
| }); | |
| // Reset play button when audio ends | |
| audio.addEventListener('ended', function() { | |
| playBtn.innerHTML = '<i class="fas fa-play text-xs"></i>'; | |
| }); | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=NeoPy/audio-recorder" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |