Docfile commited on
Commit
57703a0
·
verified ·
1 Parent(s): ec5f15b

Upload main.js

Browse files
Files changed (1) hide show
  1. static/js/main.js +308 -0
static/js/main.js ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', function() {
2
+ // DOM Elements
3
+ const ttsForm = document.getElementById('ttsForm');
4
+ const podcastForm = document.getElementById('podcastForm');
5
+ const textInput = document.getElementById('textInput');
6
+ const scenarioInput = document.getElementById('scenarioInput');
7
+ const voiceSelect = document.getElementById('voiceSelect');
8
+ const generateBtn = document.getElementById('generateBtn');
9
+ const generatePodcastBtn = document.getElementById('generatePodcastBtn');
10
+ const audioCard = document.getElementById('audioCard');
11
+ const audioPlayer = document.getElementById('audioPlayer');
12
+ const downloadBtn = document.getElementById('downloadBtn');
13
+ const newGenerationBtn = document.getElementById('newGenerationBtn');
14
+ const loadingIndicator = document.getElementById('loadingIndicator');
15
+ const progressBar = document.getElementById('progressBar');
16
+ const progressBarInner = progressBar.querySelector('.progress-bar');
17
+ const progressText = document.getElementById('progressText');
18
+ const errorAlert = document.getElementById('errorAlert');
19
+ const errorMessage = document.getElementById('errorMessage');
20
+
21
+ // Current audio file information
22
+ let currentAudioUrl = null;
23
+ let currentAudioFilename = null;
24
+
25
+ // Populate the example scenario
26
+ const exampleScenario = {
27
+ "title": "Le management participatif",
28
+ "language": "fr-FR",
29
+ "characters": [
30
+ {
31
+ "name": "Manager",
32
+ "voice": "Kore",
33
+ "text": "Bonjour Sophie. J'aimerais avoir ton avis sur le nouveau planning que nous voulons mettre en place."
34
+ },
35
+ {
36
+ "name": "Sophie",
37
+ "voice": "Puck",
38
+ "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."
39
+ },
40
+ {
41
+ "name": "Manager",
42
+ "voice": "Kore",
43
+ "text": "Bonne remarque. Peux-tu organiser une réunion avec eux pour en discuter ?"
44
+ },
45
+ {
46
+ "name": "Sophie",
47
+ "voice": "Puck",
48
+ "text": "Avec plaisir. Je les réunis demain matin."
49
+ }
50
+ ]
51
+ };
52
+
53
+ scenarioInput.value = JSON.stringify(exampleScenario, null, 2);
54
+
55
+ // Form submission handler for simple TTS
56
+ ttsForm.addEventListener('submit', async function(e) {
57
+ e.preventDefault();
58
+
59
+ const text = textInput.value.trim();
60
+ const voice = voiceSelect.value;
61
+
62
+ // Validation
63
+ if (!text) {
64
+ showError('Veuillez entrer du texte à convertir en parole.');
65
+ return;
66
+ }
67
+
68
+ // Show loading state
69
+ setLoadingState(true);
70
+
71
+ try {
72
+ // Call API to generate speech
73
+ const response = await fetch('/generate', {
74
+ method: 'POST',
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ },
78
+ body: JSON.stringify({
79
+ text: text,
80
+ voice: voice
81
+ })
82
+ });
83
+
84
+ const data = await response.json();
85
+
86
+ if (!response.ok) {
87
+ throw new Error(data.error || 'Échec de génération de la parole');
88
+ }
89
+
90
+ // Process successful response
91
+ currentAudioUrl = data.audioUrl;
92
+ currentAudioFilename = currentAudioUrl.split('/').pop();
93
+
94
+ // Setup audio player
95
+ audioPlayer.src = currentAudioUrl;
96
+ audioPlayer.load();
97
+
98
+ // Show audio card
99
+ audioCard.classList.remove('d-none');
100
+ audioCard.classList.add('fade-in');
101
+
102
+ // Auto-play the audio
103
+ try {
104
+ await audioPlayer.play();
105
+ } catch (playError) {
106
+ console.log('Lecture automatique empêchée par le navigateur.', playError);
107
+ }
108
+
109
+ } catch (error) {
110
+ showError(error.message || 'Une erreur inattendue est survenue. Veuillez réessayer.');
111
+ } finally {
112
+ setLoadingState(false);
113
+ }
114
+ });
115
+
116
+ // Form submission handler for podcast generation
117
+ podcastForm.addEventListener('submit', async function(e) {
118
+ e.preventDefault();
119
+
120
+ let scenarioText = scenarioInput.value.trim();
121
+
122
+ // Validation
123
+ if (!scenarioText) {
124
+ showError('Veuillez entrer un scénario JSON.');
125
+ return;
126
+ }
127
+
128
+ let scenario;
129
+ try {
130
+ scenario = JSON.parse(scenarioText);
131
+
132
+ // Additional validation for required fields
133
+ if (!scenario.characters || !Array.isArray(scenario.characters) || scenario.characters.length === 0) {
134
+ throw new Error('Le scénario doit contenir un tableau "characters" avec au moins un personnage.');
135
+ }
136
+
137
+ for (const character of scenario.characters) {
138
+ if (!character.name || !character.voice || !character.text) {
139
+ throw new Error('Chaque personnage doit avoir un nom, une voix et du texte.');
140
+ }
141
+ }
142
+
143
+ } catch (error) {
144
+ showError(`Erreur dans le format JSON: ${error.message}`);
145
+ return;
146
+ }
147
+
148
+ // Show loading state with progress bar
149
+ setLoadingState(true, true);
150
+
151
+ try {
152
+ // Call API to generate podcast
153
+ const response = await fetch('/generate-podcast', {
154
+ method: 'POST',
155
+ headers: {
156
+ 'Content-Type': 'application/json',
157
+ },
158
+ body: JSON.stringify(scenario)
159
+ });
160
+
161
+ // Setup progress event handling
162
+ const reader = response.body.getReader();
163
+ const contentLength = +response.headers.get('Content-Length') || 0;
164
+
165
+ // Process the stream
166
+ let receivedLength = 0;
167
+ let jsonData = '';
168
+
169
+ // Monitor the generation progress
170
+ const progressMonitor = setInterval(() => {
171
+ fetch('/generation-progress')
172
+ .then(res => res.json())
173
+ .then(progress => {
174
+ if (progress.status === 'in_progress') {
175
+ updateProgress(progress.current, progress.total, progress.message);
176
+ }
177
+ })
178
+ .catch(err => console.error('Error fetching progress:', err));
179
+ }, 1000);
180
+
181
+ while (true) {
182
+ const { done, value } = await reader.read();
183
+
184
+ if (done) {
185
+ clearInterval(progressMonitor);
186
+ break;
187
+ }
188
+
189
+ receivedLength += value.length;
190
+ const chunk = new TextDecoder().decode(value);
191
+ jsonData += chunk;
192
+
193
+ // Update progress if we have content length
194
+ if (contentLength) {
195
+ const percentComplete = Math.round((receivedLength / contentLength) * 100);
196
+ updateProgress(percentComplete, 100, 'Réception des données...');
197
+ }
198
+ }
199
+
200
+ // Parse the response
201
+ const data = JSON.parse(jsonData);
202
+
203
+ if (!response.ok) {
204
+ throw new Error(data.error || 'Échec de génération du podcast');
205
+ }
206
+
207
+ // Process successful response
208
+ currentAudioUrl = data.audioUrl;
209
+ currentAudioFilename = currentAudioUrl.split('/').pop();
210
+
211
+ // Setup audio player
212
+ audioPlayer.src = currentAudioUrl;
213
+ audioPlayer.load();
214
+
215
+ // Show audio card
216
+ audioCard.classList.remove('d-none');
217
+ audioCard.classList.add('fade-in');
218
+
219
+ // Auto-play the audio
220
+ try {
221
+ await audioPlayer.play();
222
+ } catch (playError) {
223
+ console.log('Lecture automatique empêchée par le navigateur.', playError);
224
+ }
225
+
226
+ } catch (error) {
227
+ showError(error.message || 'Une erreur inattendue est survenue. Veuillez réessayer.');
228
+ } finally {
229
+ setLoadingState(false);
230
+ }
231
+ });
232
+
233
+ // Download button handler
234
+ downloadBtn.addEventListener('click', function() {
235
+ if (currentAudioUrl) {
236
+ const downloadLink = document.createElement('a');
237
+ downloadLink.href = `/download/${currentAudioFilename}`;
238
+ downloadLink.download = 'gemini_podcast.wav';
239
+ document.body.appendChild(downloadLink);
240
+ downloadLink.click();
241
+ document.body.removeChild(downloadLink);
242
+ }
243
+ });
244
+
245
+ // New generation button handler
246
+ newGenerationBtn.addEventListener('click', function() {
247
+ // Reset UI for a new generation
248
+ audioCard.classList.add('d-none');
249
+ errorAlert.classList.add('d-none');
250
+
251
+ // Check which tab is currently active
252
+ const activeTabPane = document.querySelector('.tab-pane.active');
253
+ if (activeTabPane.id === 'simple-tts') {
254
+ textInput.focus();
255
+ } else {
256
+ scenarioInput.focus();
257
+ }
258
+ });
259
+
260
+ // Helper function to show error messages
261
+ function showError(message) {
262
+ errorMessage.textContent = message;
263
+ errorAlert.classList.remove('d-none');
264
+
265
+ // Auto-hide error after 8 seconds
266
+ setTimeout(() => {
267
+ errorAlert.classList.add('d-none');
268
+ }, 8000);
269
+ }
270
+
271
+ // Helper function to set loading state
272
+ function setLoadingState(isLoading, withProgress = false) {
273
+ if (isLoading) {
274
+ generateBtn.disabled = true;
275
+ generatePodcastBtn.disabled = true;
276
+ loadingIndicator.classList.remove('d-none');
277
+ errorAlert.classList.add('d-none');
278
+
279
+ if (withProgress) {
280
+ progressBar.classList.remove('d-none');
281
+ progressText.classList.remove('d-none');
282
+ updateProgress(0, 100, 'Préparation du podcast...');
283
+ } else {
284
+ progressBar.classList.add('d-none');
285
+ progressText.classList.add('d-none');
286
+ }
287
+ } else {
288
+ generateBtn.disabled = false;
289
+ generatePodcastBtn.disabled = false;
290
+ loadingIndicator.classList.add('d-none');
291
+ progressBar.classList.add('d-none');
292
+ progressText.classList.add('d-none');
293
+ }
294
+ }
295
+
296
+ // Helper function to update progress bar
297
+ function updateProgress(current, total, message) {
298
+ const percentage = Math.min(100, Math.max(0, Math.round((current / total) * 100)));
299
+ progressBarInner.style.width = `${percentage}%`;
300
+ progressBarInner.setAttribute('aria-valuenow', percentage);
301
+ progressText.textContent = message || `Progression: ${percentage}%`;
302
+ }
303
+
304
+ // Add event listener for when audio playback is complete
305
+ audioPlayer.addEventListener('ended', function() {
306
+ console.log('Audio playback completed');
307
+ });
308
+ });