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(); });