document.addEventListener('DOMContentLoaded', function() { // DOM Elements const ttsForm = document.getElementById('ttsForm'); const podcastForm = document.getElementById('podcastForm'); const textInput = document.getElementById('textInput'); const scenarioInput = document.getElementById('scenarioInput'); const voiceSelect = document.getElementById('voiceSelect'); const generateBtn = document.getElementById('generateBtn'); const generatePodcastBtn = document.getElementById('generatePodcastBtn'); const audioCard = document.getElementById('audioCard'); const audioPlayer = document.getElementById('audioPlayer'); const downloadBtn = document.getElementById('downloadBtn'); const newGenerationBtn = document.getElementById('newGenerationBtn'); const loadingIndicator = document.getElementById('loadingIndicator'); const progressBar = document.getElementById('progressBar'); const progressBarInner = progressBar.querySelector('.progress-bar'); const progressText = document.getElementById('progressText'); const errorAlert = document.getElementById('errorAlert'); const errorMessage = document.getElementById('errorMessage'); // Current audio file information let currentAudioUrl = null; let currentAudioFilename = null; // Populate the example scenario const exampleScenario = { "title": "Le management participatif", "language": "fr-FR", "characters": [ { "name": "Manager", "voice": "Kore", "text": "Bonjour Sophie. J'aimerais avoir ton avis sur le nouveau planning que nous voulons mettre en place." }, { "name": "Sophie", "voice": "Puck", "text": "Bonjour ! Merci de me demander. Je pense que ce nouveau planning pourrait être bénéfique, mais nous devons ajuster les horaires de l'équipe technique." }, { "name": "Manager", "voice": "Kore", "text": "Bonne remarque. Peux-tu organiser une réunion avec eux pour en discuter ?" }, { "name": "Sophie", "voice": "Puck", "text": "Avec plaisir. Je les réunis demain matin." } ] }; scenarioInput.value = JSON.stringify(exampleScenario, null, 2); // Form submission handler for simple TTS ttsForm.addEventListener('submit', async function(e) { e.preventDefault(); const text = textInput.value.trim(); const voice = voiceSelect.value; // Validation if (!text) { showError('Veuillez entrer du texte à convertir en parole.'); return; } // Show loading state setLoadingState(true); try { // Call API to generate speech const response = await fetch('/generate', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ text: text, voice: voice }) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Échec de génération de la parole'); } // Process successful response currentAudioUrl = data.audioUrl; currentAudioFilename = currentAudioUrl.split('/').pop(); // Setup audio player audioPlayer.src = currentAudioUrl; audioPlayer.load(); // Show audio card audioCard.classList.remove('d-none'); audioCard.classList.add('fade-in'); // Auto-play the audio try { await audioPlayer.play(); } catch (playError) { console.log('Lecture automatique empêchée par le navigateur.', playError); } } catch (error) { showError(error.message || 'Une erreur inattendue est survenue. Veuillez réessayer.'); } finally { setLoadingState(false); } }); // Form submission handler for podcast generation podcastForm.addEventListener('submit', async function(e) { e.preventDefault(); let scenarioText = scenarioInput.value.trim(); // Validation if (!scenarioText) { showError('Veuillez entrer un scénario JSON.'); return; } let scenario; try { scenario = JSON.parse(scenarioText); // Additional validation for required fields if (!scenario.characters || !Array.isArray(scenario.characters) || scenario.characters.length === 0) { throw new Error('Le scénario doit contenir un tableau "characters" avec au moins un personnage.'); } for (const character of scenario.characters) { if (!character.name || !character.voice || !character.text) { throw new Error('Chaque personnage doit avoir un nom, une voix et du texte.'); } } } catch (error) { showError(`Erreur dans le format JSON: ${error.message}`); return; } // Show loading state with progress bar setLoadingState(true, true); try { // Call API to start podcast generation in the background const response = await fetch('/generate-podcast', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(scenario) }); const data = await response.json(); if (!response.ok) { throw new Error(data.error || 'Échec de démarrage de la génération du podcast'); } // Start polling for podcast status const statusPoller = setInterval(async () => { try { const statusResponse = await fetch('/podcast-status'); const statusData = await statusResponse.json(); if (statusData.status === 'in_progress') { // Update progress bar updateProgress( statusData.current, statusData.total, statusData.message || 'Génération en cours...' ); } else if (statusData.status === 'complete') { // Podcast is complete clearInterval(statusPoller); // Process successful response if (statusData.audioUrl) { currentAudioUrl = statusData.audioUrl; currentAudioFilename = currentAudioUrl.split('/').pop(); // Setup audio player audioPlayer.src = currentAudioUrl; audioPlayer.load(); // Show audio card audioCard.classList.remove('d-none'); audioCard.classList.add('fade-in'); // Auto-play the audio try { await audioPlayer.play(); } catch (playError) { console.log('Lecture automatique empêchée par le navigateur.', playError); } // Turn off loading state setLoadingState(false); } else { throw new Error('Podcast généré mais URL audio manquante'); } } else if (statusData.status === 'error' || statusData.message.startsWith('Erreur')) { // Error occurred clearInterval(statusPoller); throw new Error(statusData.message || 'Erreur pendant la génération du podcast'); } } catch (pollError) { clearInterval(statusPoller); throw pollError; } }, 1000); } catch (error) { showError(error.message || 'Une erreur inattendue est survenue. Veuillez réessayer.'); setLoadingState(false); } }); // Download button handler downloadBtn.addEventListener('click', function() { if (currentAudioUrl) { const downloadLink = document.createElement('a'); downloadLink.href = `/download/${currentAudioFilename}`; downloadLink.download = 'gemini_podcast.wav'; document.body.appendChild(downloadLink); downloadLink.click(); document.body.removeChild(downloadLink); } }); // New generation button handler newGenerationBtn.addEventListener('click', function() { // Reset UI for a new generation audioCard.classList.add('d-none'); errorAlert.classList.add('d-none'); // Check which tab is currently active const activeTabPane = document.querySelector('.tab-pane.active'); if (activeTabPane.id === 'simple-tts') { textInput.focus(); } else { scenarioInput.focus(); } }); // Helper function to show error messages function showError(message) { errorMessage.textContent = message; errorAlert.classList.remove('d-none'); // Auto-hide error after 8 seconds setTimeout(() => { errorAlert.classList.add('d-none'); }, 8000); } // Helper function to set loading state function setLoadingState(isLoading, withProgress = false) { if (isLoading) { generateBtn.disabled = true; generatePodcastBtn.disabled = true; loadingIndicator.classList.remove('d-none'); errorAlert.classList.add('d-none'); if (withProgress) { progressBar.classList.remove('d-none'); progressText.classList.remove('d-none'); updateProgress(0, 100, 'Préparation du podcast...'); } else { progressBar.classList.add('d-none'); progressText.classList.add('d-none'); } } else { generateBtn.disabled = false; generatePodcastBtn.disabled = false; loadingIndicator.classList.add('d-none'); progressBar.classList.add('d-none'); progressText.classList.add('d-none'); } } // Helper function to update progress bar function updateProgress(current, total, message) { const percentage = Math.min(100, Math.max(0, Math.round((current / total) * 100))); progressBarInner.style.width = `${percentage}%`; progressBarInner.setAttribute('aria-valuenow', percentage); progressText.textContent = message || `Progression: ${percentage}%`; } // Add event listener for when audio playback is complete audioPlayer.addEventListener('ended', function() { console.log('Audio playback completed'); }); });