Spaces:
Sleeping
Sleeping
| import { Client } from "@gradio/client"; | |
| // Add these variables at the top of the file | |
| let audioCache = {}; | |
| let currentTrack = null; | |
| // // Add this function at the beginning of your script.js file | |
| // function handleSharedUrl() { | |
| // const urlParams = new URLSearchParams(window.location.search); | |
| // const sharedUrl = urlParams.get('url'); | |
| // if (sharedUrl) { | |
| // console.log('Shared URL detected:', sharedUrl); | |
| // fetchMp3(sharedUrl); | |
| // } | |
| // } | |
| document.addEventListener("DOMContentLoaded", async function() { | |
| const audioPlayer = document.getElementById('player'); | |
| const playButton = document.getElementById('playButton'); | |
| const skipBackwardButton = document.getElementById('skipBackward'); | |
| const skipForwardButton = document.getElementById('skipForward'); | |
| const settingsBtn = document.getElementById('settingsBtn'); | |
| const settingsModal = document.getElementById('settingsModal'); | |
| const closeBtn = document.querySelector('.close'); | |
| const saveSettingsBtn = document.getElementById('saveSettings'); | |
| const apiKeyInput = document.getElementById('apiKey'); | |
| const toggleApiKeyBtn = document.getElementById('toggleApiKey'); | |
| const apiServerInput = document.getElementById('apiServer'); | |
| let originalApiKey = ''; | |
| let originalApiServer = ''; | |
| // Load saved settings on page load | |
| const savedApiKey = localStorage.getItem('openaiApiKey'); | |
| const savedApiServer = localStorage.getItem('apiServer') || 'http://127.0.0.1:7860'; | |
| if (savedApiKey) { | |
| apiKeyInput.value = savedApiKey; | |
| originalApiKey = savedApiKey; | |
| } | |
| apiServerInput.value = savedApiServer; | |
| originalApiServer = savedApiServer; | |
| // Toggle API key visibility | |
| toggleApiKeyBtn.onclick = function() { | |
| if (apiKeyInput.type === "password") { | |
| apiKeyInput.type = "text"; | |
| toggleApiKeyBtn.textContent = "🔒"; | |
| } else { | |
| apiKeyInput.type = "password"; | |
| toggleApiKeyBtn.textContent = "👁️"; | |
| } | |
| } | |
| // Open settings modal | |
| settingsBtn.onclick = function() { | |
| originalApiKey = apiKeyInput.value; | |
| originalApiServer = apiServerInput.value; | |
| settingsModal.style.display = "block"; | |
| apiKeyInput.focus(); | |
| } | |
| // Close settings modal | |
| function closeModal() { | |
| settingsModal.style.display = "none"; | |
| apiKeyInput.value = originalApiKey; // Revert to original value | |
| } | |
| closeBtn.onclick = closeModal; | |
| // Close modal if clicked outside | |
| window.onclick = function(event) { | |
| if (event.target == settingsModal) { | |
| closeModal(); | |
| } | |
| } | |
| // Handle keydown events | |
| document.addEventListener('keydown', function(event) { | |
| if (settingsModal.style.display === "block") { | |
| if (event.key === "Escape") { | |
| closeModal(); | |
| } else if (event.key === "Enter") { | |
| saveSettings(); | |
| } | |
| } | |
| }); | |
| // Save settings | |
| function saveSettings() { | |
| const apiKey = apiKeyInput.value.trim(); | |
| const apiServer = apiServerInput.value.trim(); | |
| if (apiKey && apiServer) { | |
| localStorage.setItem('openaiApiKey', apiKey); | |
| localStorage.setItem('apiServer', apiServer); | |
| originalApiKey = apiKey; | |
| originalApiServer = apiServer; | |
| alert('Settings saved successfully!'); | |
| closeModal(); | |
| } else { | |
| alert('Please enter both a valid API key and API server.'); | |
| } | |
| } | |
| saveSettingsBtn.onclick = saveSettings; | |
| const historyList = document.getElementById('historyList'); | |
| const clearHistoryBtn = document.getElementById('clearHistory'); | |
| // Load audio cache from localStorage and Cache API | |
| await loadAudioCache(); | |
| // Update history list | |
| updateHistoryList(); | |
| // Function to fetch MP3 from API endpoint when a link is shared | |
| async function fetchMp3(link) { | |
| console.log('Starting fetchMp3 function with link:', link); | |
| const loadingIndicator = document.getElementById('loadingIndicator'); | |
| const audioPlayer = document.getElementById('player'); | |
| const playButton = document.getElementById('playButton'); | |
| const transcriptionContainer = document.getElementById('transcriptionContainer'); | |
| const transcriptionElement = document.getElementById('transcription'); | |
| if (loadingIndicator) loadingIndicator.style.display = 'block'; | |
| if (transcriptionContainer) transcriptionContainer.style.display = 'none'; | |
| try { | |
| // Check if the link is already in the cache | |
| if (audioCache[link]) { | |
| console.log('Loading audio from cache'); | |
| await loadAudioFromCache(link); | |
| return; | |
| } | |
| const apiKey = localStorage.getItem('openaiApiKey'); | |
| const apiServer = localStorage.getItem('apiServer'); | |
| console.log('Retrieved API key and server from localStorage'); | |
| console.log('API Server:', apiServer); | |
| if (!apiKey) { | |
| throw new Error("API key not set. Please set your OpenAI API key in the settings."); | |
| } | |
| if (!apiServer) { | |
| throw new Error("API server not set. Please set the API server in the settings."); | |
| } | |
| console.log('Attempting to connect to Gradio app...'); | |
| // Connect to local Gradio app | |
| const client = await Client.connect(apiServer); | |
| //connect to HF deployed one OK | |
| //const client = await Client.connect("Mightypeacock/webtoaudio"); | |
| console.log('Gradio client created successfully'); | |
| console.log(await client.view_api()) | |
| console.log('Preparing to make prediction...'); | |
| // Make the prediction | |
| const result = await client.predict("/generate_audio", { | |
| url:link, | |
| openai_api_key: apiKey, | |
| text_model: "gpt-4o-mini", | |
| audio_model: "tts-1", | |
| speaker_1_voice: "alloy", | |
| speaker_2_voice: "echo", | |
| api_base: null, // api_base | |
| edited_transcript: "", // edited_transcript | |
| user_feedback: "", // user_feedback | |
| original_text: "summary" // original_text | |
| // debug: true, | |
| }); | |
| console.log('Raw result from predict:', result); | |
| console.log('Result data:', result.data); | |
| console.log('Prediction made successfully'); | |
| // Check if result.data is an array and has at least one element | |
| if (!Array.isArray(result.data) || result.data.length === 0) { | |
| throw new Error('Unexpected result format from server'); | |
| } | |
| // Assuming the audio file URL is the second item in the result | |
| const audioFileUrl = result.data[0].url; | |
| console.log('Received audio file URL:', audioFileUrl); | |
| // Check if the URL is valid | |
| if (typeof audioFileUrl !== 'string' || !audioFileUrl.startsWith('http')) { | |
| throw new Error(`Invalid audio file URL received: ${audioFileUrl}`); | |
| } | |
| // After successful API call, add to cache | |
| audioCache[link] = { | |
| audioUrl: audioFileUrl, | |
| transcription: result.data[1], | |
| lastPosition: 0 | |
| }; | |
| await saveAudioCache(link, audioFileUrl); | |
| updateHistoryList(); | |
| await loadAudioFromCache(link); | |
| // Update media session metadata | |
| updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio'); | |
| } catch (error) { | |
| console.error('Error in fetchMp3:', error); | |
| console.error('Error stack:', error.stack); | |
| alert(`Error fetching MP3: ${error.message}`); | |
| // Clear the audio player source and hide the play button | |
| if (audioPlayer) audioPlayer.src = ''; | |
| if (playButton) playButton.style.display = 'none'; | |
| if (transcriptionContainer) transcriptionContainer.style.display = 'none'; | |
| } finally { | |
| if (loadingIndicator) | |
| loadingIndicator.style.display = 'none'; | |
| } | |
| } | |
| async function loadAudioFromCache(link) { | |
| const cachedAudio = audioCache[link]; | |
| if (!cachedAudio) return; | |
| const audioPlayer = document.getElementById('player'); | |
| const playButton = document.getElementById('playButton'); | |
| const transcriptionContainer = document.getElementById('transcriptionContainer'); | |
| const transcriptionElement = document.getElementById('transcription'); | |
| // Fetch the audio file from the Cache API | |
| const cache = await caches.open('audio-cache'); | |
| const response = await cache.match(cachedAudio.audioUrl); | |
| if (response) { | |
| const blob = await response.blob(); | |
| audioPlayer.src = URL.createObjectURL(blob); | |
| } else { | |
| audioPlayer.src = cachedAudio.audioUrl; | |
| } | |
| audioPlayer.currentTime = cachedAudio.lastPosition; | |
| currentTrack = link; | |
| if (playButton) { | |
| playButton.style.display = 'block'; | |
| playButton.onclick = () => audioPlayer.play(); | |
| } | |
| if (transcriptionElement && transcriptionContainer) { | |
| transcriptionElement.textContent = cachedAudio.transcription; | |
| transcriptionContainer.style.display = 'block'; | |
| } | |
| console.log('Audio loaded from cache and ready for playback'); | |
| // Update media session metadata | |
| updateMediaSessionMetadata(link, 'Web to Audio', 'Generated Audio'); | |
| } | |
| async function saveAudioCache(link, audioUrl) { | |
| // Save metadata to localStorage | |
| localStorage.setItem('audioCache', JSON.stringify(audioCache)); | |
| // Save audio file to Cache API | |
| const cache = await caches.open('audio-cache'); | |
| await cache.add(audioUrl); | |
| } | |
| async function loadAudioCache() { | |
| const savedCache = localStorage.getItem('audioCache'); | |
| if (savedCache) { | |
| audioCache = JSON.parse(savedCache); | |
| } | |
| // Verify that all cached audio files are still in the Cache API | |
| const cache = await caches.open('audio-cache'); | |
| for (const link in audioCache) { | |
| const response = await cache.match(audioCache[link].audioUrl); | |
| if (!response) { | |
| console.log(`Audio file for ${link} not found in cache, removing entry`); | |
| delete audioCache[link]; | |
| } | |
| } | |
| // Save the cleaned-up cache back to localStorage | |
| localStorage.setItem('audioCache', JSON.stringify(audioCache)); | |
| } | |
| async function updateHistoryList() { | |
| historyList.innerHTML = ''; | |
| Object.keys(audioCache).forEach(link => { | |
| const li = document.createElement('li'); | |
| const playBtn = document.createElement('button'); | |
| playBtn.textContent = 'Play'; | |
| playBtn.onclick = () => loadAudioFromCache(link); | |
| const removeBtn = document.createElement('button'); | |
| removeBtn.textContent = 'Remove'; | |
| removeBtn.onclick = () => removeFromCache(link); | |
| li.appendChild(document.createTextNode(link + ' ')); | |
| li.appendChild(playBtn); | |
| li.appendChild(removeBtn); | |
| historyList.appendChild(li); | |
| }); | |
| } | |
| async function removeFromCache(link) { | |
| const cache = await caches.open('audio-cache'); | |
| await cache.delete(audioCache[link].audioUrl); | |
| delete audioCache[link]; | |
| localStorage.setItem('audioCache', JSON.stringify(audioCache)); | |
| updateHistoryList(); | |
| } | |
| clearHistoryBtn.onclick = async function() { | |
| const cache = await caches.open('audio-cache'); | |
| for (const link in audioCache) { | |
| await cache.delete(audioCache[link].audioUrl); | |
| } | |
| audioCache = {}; | |
| localStorage.setItem('audioCache', JSON.stringify(audioCache)); | |
| updateHistoryList(); | |
| }; | |
| // Save current position every 5 seconds | |
| setInterval(() => { | |
| if (currentTrack && audioPlayer.currentTime > 0) { | |
| audioCache[currentTrack].lastPosition = audioPlayer.currentTime; | |
| localStorage.setItem('audioCache', JSON.stringify(audioCache)); | |
| } | |
| }, 5000); | |
| // Call handleSharedUrl instead of directly checking for the URL parameter | |
| // handleSharedUrl(); | |
| // Get the link from the shared URL | |
| const queryParams = new URLSearchParams(window.location.search); | |
| const sharedLink = queryParams.get('url'); | |
| console.log('Shared link from URL:', sharedLink); | |
| // Only call the API to get MP3 if a valid URL is provided | |
| if (sharedLink) { | |
| console.log('Valid URL provided, calling fetchMp3'); | |
| fetchMp3(sharedLink); | |
| } else { | |
| console.log("No URL provided. Waiting for user input."); | |
| // You might want to update the UI here to indicate that the user needs to provide a URL | |
| } | |
| // Add this function to update media session metadata | |
| function updateMediaSessionMetadata(title, artist, album) { | |
| if ('mediaSession' in navigator) { | |
| navigator.mediaSession.metadata = new MediaMetadata({ | |
| title: title || 'Unknown Title', | |
| artist: artist || 'Unknown Artist', | |
| album: album || 'Unknown Album', | |
| artwork: [ | |
| { src: '/icons/imagepodcast-transp500.png', sizes: '500x500', type: 'image/png' }, | |
| { src: '/icons/imagepodcast.png', sizes: '1024x1024', type: 'image/png' } | |
| ] | |
| }); | |
| } | |
| } | |
| // Add this function to set up media session handlers | |
| function setupMediaSessionHandlers() { | |
| if ('mediaSession' in navigator) { | |
| navigator.mediaSession.setActionHandler('play', () => { | |
| audioPlayer.play(); | |
| playButton.textContent = 'Pause'; | |
| }); | |
| navigator.mediaSession.setActionHandler('pause', () => { | |
| audioPlayer.pause(); | |
| playButton.textContent = 'Play'; | |
| }); | |
| navigator.mediaSession.setActionHandler('seekbackward', (details) => { | |
| const skipTime = details.seekOffset || 10; | |
| audioPlayer.currentTime = Math.max(audioPlayer.currentTime - skipTime, 0); | |
| }); | |
| navigator.mediaSession.setActionHandler('seekforward', (details) => { | |
| const skipTime = details.seekOffset || 10; | |
| audioPlayer.currentTime = Math.min(audioPlayer.currentTime + skipTime, audioPlayer.duration); | |
| }); | |
| navigator.mediaSession.setActionHandler('seekto', (details) => { | |
| if (details.fastSeek && 'fastSeek' in audioPlayer) { | |
| audioPlayer.fastSeek(details.seekTime); | |
| return; | |
| } | |
| audioPlayer.currentTime = details.seekTime; | |
| }); | |
| navigator.mediaSession.setActionHandler('previoustrack', () => { | |
| audioPlayer.currentTime = 0; | |
| }); | |
| } | |
| } | |
| // Call this function to set up the media session handlers | |
| setupMediaSessionHandlers(); | |
| }); |