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 generate podcast const response = await fetch('/generate-podcast', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(scenario) }); // Setup progress event handling const reader = response.body.getReader(); const contentLength = +response.headers.get('Content-Length') || 0; // Process the stream let receivedLength = 0; let jsonData = ''; // Monitor the generation progress const progressMonitor = setInterval(() => { fetch('/generation-progress') .then(res => res.json()) .then(progress => { if (progress.status === 'in_progress') { updateProgress(progress.current, progress.total, progress.message); } }) .catch(err => console.error('Error fetching progress:', err)); }, 1000); while (true) { const { done, value } = await reader.read(); if (done) { clearInterval(progressMonitor); break; } receivedLength += value.length; const chunk = new TextDecoder().decode(value); jsonData += chunk; // Update progress if we have content length if (contentLength) { const percentComplete = Math.round((receivedLength / contentLength) * 100); updateProgress(percentComplete, 100, 'Réception des données...'); } } // Parse the response const data = JSON.parse(jsonData); if (!response.ok) { throw new Error(data.error || 'Échec de génération du podcast'); } // 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); } }); // 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'); }); });