Ajout de la fonctionnalité TTS (Text-to-Speech) avec XTTS v2
- Ajout de TTS>=0.22.0 aux dépendances - Création du module utils/tts_generator.py avec Coqui XTTS v2 * Support GPU avec mixed precision (FP16) * Lazy loading avec singleton pattern * Chunking automatique pour textes longs * Support multilingue (fr, en, es, de, etc.) - Ajout de la route /chat/export-audio dans flask_app.py - Ajout du bouton Audio dans chat.html (côté Word/PDF) - Génération audio WAV téléchargeable depuis les réponses Optimisé pour GPU 4070 (8GB VRAM) : utilise 4-6GB, génération rapide Qualité : voix naturelle française avec prosodie expressive 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -590,7 +590,8 @@
|
||||
|
||||
/* Export buttons - compact size */
|
||||
.export-word-btn,
|
||||
.export-pdf-btn {
|
||||
.export-pdf-btn,
|
||||
.export-audio-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -613,14 +614,16 @@
|
||||
}
|
||||
|
||||
.export-word-btn:hover,
|
||||
.export-pdf-btn:hover {
|
||||
.export-pdf-btn:hover,
|
||||
.export-audio-btn:hover {
|
||||
background-color: var(--color-accent-alt);
|
||||
border-color: var(--color-accent-alt);
|
||||
color: var(--color-bg-main);
|
||||
}
|
||||
|
||||
.export-word-btn svg,
|
||||
.export-pdf-btn svg {
|
||||
.export-pdf-btn svg,
|
||||
.export-audio-btn svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
flex-shrink: 0;
|
||||
@@ -977,6 +980,7 @@
|
||||
let assistantContentDiv = null;
|
||||
let exportWordBtn = null;
|
||||
let exportPdfBtn = null;
|
||||
let exportAudioBtn = null;
|
||||
let exportContainer = null;
|
||||
let accumulatedText = '';
|
||||
|
||||
@@ -1006,6 +1010,7 @@
|
||||
assistantContentDiv = result.contentDiv;
|
||||
exportWordBtn = result.exportWordBtn;
|
||||
exportPdfBtn = result.exportPdfBtn;
|
||||
exportAudioBtn = result.exportAudioBtn;
|
||||
exportContainer = result.exportContainer;
|
||||
}
|
||||
|
||||
@@ -1049,6 +1054,11 @@
|
||||
originalQuestion
|
||||
);
|
||||
});
|
||||
|
||||
// Add click handler for Audio export
|
||||
exportAudioBtn.addEventListener('click', async () => {
|
||||
await exportToAudio(accumulatedText);
|
||||
});
|
||||
}
|
||||
|
||||
eventSource.close();
|
||||
@@ -1142,15 +1152,28 @@
|
||||
PDF
|
||||
`;
|
||||
|
||||
// Add export Audio button
|
||||
const exportAudioBtn = document.createElement('button');
|
||||
exportAudioBtn.className = 'export-audio-btn';
|
||||
exportAudioBtn.innerHTML = `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M11 5L6 9H2v6h4l5 4V5z"/>
|
||||
<path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
|
||||
<path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
|
||||
</svg>
|
||||
Audio
|
||||
`;
|
||||
|
||||
exportContainer.appendChild(exportWordBtn);
|
||||
exportContainer.appendChild(exportPdfBtn);
|
||||
exportContainer.appendChild(exportAudioBtn);
|
||||
|
||||
messageDiv.appendChild(label);
|
||||
messageDiv.appendChild(contentDiv);
|
||||
messageDiv.appendChild(exportContainer);
|
||||
chatMessages.appendChild(messageDiv);
|
||||
|
||||
return { messageDiv, contentDiv, exportWordBtn, exportPdfBtn, exportContainer };
|
||||
return { messageDiv, contentDiv, exportWordBtn, exportPdfBtn, exportAudioBtn, exportContainer };
|
||||
}
|
||||
|
||||
function addErrorMessage(message) {
|
||||
@@ -1348,6 +1371,39 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function exportToAudio(assistantResponse) {
|
||||
try {
|
||||
const response = await fetch('/chat/export-audio', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
assistant_response: assistantResponse,
|
||||
language: 'fr'
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json();
|
||||
throw new Error(error.error || 'TTS failed');
|
||||
}
|
||||
|
||||
// Download file
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `chat_audio_${new Date().getTime()}.wav`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
|
||||
} catch (error) {
|
||||
console.error('TTS error:', error);
|
||||
alert(`Erreur TTS: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize
|
||||
sendBtn.disabled = true;
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user