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