wami / websocket_example.html
Bgk Injector SqLi
Add WebSocket demo page
d0b5038
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebSocket Pipeline - Exemple</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 min-h-screen p-8">
<div class="max-w-4xl mx-auto">
<h1 class="text-4xl font-bold text-white mb-2">🔄 WebSocket Pipeline</h1>
<p class="text-slate-300 mb-8">Pipeline temps réel : Audio → STT → Traduction</p>
<div class="grid gap-6">
<!-- Controls -->
<div class="bg-slate-800/50 backdrop-blur-sm rounded-xl p-6 border border-slate-700">
<h2 class="text-xl font-bold text-white mb-4">📡 Contrôles</h2>
<div class="space-y-4">
<div>
<label class="text-slate-300 mb-2 block">Langue cible :</label>
<select id="targetLang" class="w-full bg-slate-900 text-white rounded-lg px-4 py-2 border border-slate-600">
<option value="fr">Français</option>
<option value="dyu">Dioula</option>
</select>
</div>
<div class="flex items-center gap-2">
<input type="checkbox" id="includeTts" class="w-5 h-5">
<label for="includeTts" class="text-slate-300">Générer l'audio (TTS)</label>
</div>
<button id="recordBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-4 rounded-lg transition">
🎙️ Enregistrer et envoyer
</button>
<button id="connectBtn" class="w-full bg-green-600 hover:bg-green-700 text-white font-semibold py-2 rounded-lg transition">
🔌 Connecter WebSocket
</button>
</div>
</div>
<!-- Status -->
<div class="bg-slate-800/50 backdrop-blur-sm rounded-xl p-6 border border-slate-700">
<h2 class="text-xl font-bold text-white mb-4">📊 Statut</h2>
<div id="status" class="space-y-2">
<div class="text-slate-400">WebSocket: <span id="wsStatus" class="text-red-400">Déconnecté</span></div>
<div class="text-slate-400">Étape: <span id="currentStep" class="text-slate-300">-</span></div>
</div>
</div>
<!-- Progress Log -->
<div class="bg-slate-800/50 backdrop-blur-sm rounded-xl p-6 border border-slate-700">
<h2 class="text-xl font-bold text-white mb-4">📝 Journal</h2>
<div id="log" class="bg-slate-900/50 rounded-lg p-4 h-48 overflow-y-auto font-mono text-sm text-green-400"></div>
</div>
<!-- Results -->
<div class="bg-slate-800/50 backdrop-blur-sm rounded-xl p-6 border border-slate-700">
<h2 class="text-xl font-bold text-white mb-4">✨ Résultats</h2>
<div class="space-y-4">
<div id="transcriptionDiv" class="hidden">
<div class="text-sm text-slate-400 mb-1">Transcription (Dioula) :</div>
<div id="transcription" class="bg-slate-900/50 rounded-lg p-4 text-white"></div>
</div>
<div id="traductionDiv" class="hidden">
<div class="text-sm text-slate-400 mb-1">Traduction :</div>
<div id="traduction" class="bg-slate-900/50 rounded-lg p-4 text-white"></div>
</div>
<div id="audioDiv" class="hidden">
<div class="text-sm text-slate-400 mb-2">Audio généré :</div>
<audio id="audioResult" controls class="w-full"></audio>
</div>
</div>
</div>
</div>
</div>
<script>
let ws = null;
let mediaRecorder = null;
let audioChunks = [];
const log = document.getElementById('log');
const wsStatus = document.getElementById('wsStatus');
const currentStep = document.getElementById('currentStep');
const recordBtn = document.getElementById('recordBtn');
const connectBtn = document.getElementById('connectBtn');
function addLog(message, type = 'info') {
const time = new Date().toLocaleTimeString();
const color = type === 'error' ? 'text-red-400' : type === 'success' ? 'text-green-400' : 'text-blue-400';
log.innerHTML += `<div class="${color}">[${time}] ${message}</div>`;
log.scrollTop = log.scrollHeight;
}
// Connect WebSocket
connectBtn.addEventListener('click', () => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws/pipeline`;
ws = new WebSocket(wsUrl);
ws.onopen = () => {
wsStatus.textContent = 'Connecté';
wsStatus.className = 'text-green-400';
addLog('WebSocket connecté', 'success');
connectBtn.disabled = true;
connectBtn.textContent = '✅ Connecté';
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
handleMessage(data);
};
ws.onerror = (error) => {
addLog('Erreur WebSocket: ' + error, 'error');
};
ws.onclose = () => {
wsStatus.textContent = 'Déconnecté';
wsStatus.className = 'text-red-400';
addLog('WebSocket déconnecté', 'error');
connectBtn.disabled = false;
connectBtn.textContent = '🔌 Reconnecter';
};
});
function handleMessage(data) {
if (data.error) {
addLog('❌ Erreur: ' + data.error, 'error');
currentStep.textContent = 'Erreur';
return;
}
if (data.status === 'processing') {
currentStep.textContent = data.step;
if (data.message) {
addLog(data.message);
}
if (data.transcription) {
document.getElementById('transcription').textContent = data.transcription;
document.getElementById('transcriptionDiv').classList.remove('hidden');
addLog('✅ Transcription: ' + data.transcription, 'success');
}
if (data.traduction) {
document.getElementById('traduction').textContent = data.traduction;
document.getElementById('traductionDiv').classList.remove('hidden');
addLog('✅ Traduction: ' + data.traduction, 'success');
}
}
if (data.status === 'done') {
currentStep.textContent = 'Terminé ✅';
addLog('✨ Pipeline terminé', 'success');
if (data.audio_base64) {
const audioBlob = base64ToBlob(data.audio_base64, 'audio/wav');
const audioUrl = URL.createObjectURL(audioBlob);
document.getElementById('audioResult').src = audioUrl;
document.getElementById('audioDiv').classList.remove('hidden');
addLog('🔊 Audio généré', 'success');
}
}
}
// Record and send
recordBtn.addEventListener('click', async () => {
if (!ws || ws.readyState !== WebSocket.OPEN) {
alert('WebSocket non connecté. Cliquez sur "Connecter WebSocket" d\'abord.');
return;
}
if (!mediaRecorder || mediaRecorder.state === 'inactive') {
// Start recording
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
mediaRecorder = new MediaRecorder(stream);
audioChunks = [];
mediaRecorder.ondataavailable = (e) => {
if (e.data.size > 0) audioChunks.push(e.data);
};
mediaRecorder.onstop = async () => {
stream.getTracks().forEach(t => t.stop());
const blob = new Blob(audioChunks, { type: 'audio/webm' });
await sendAudioToWebSocket(blob);
};
mediaRecorder.start();
recordBtn.textContent = '⏹️ Arrêter et envoyer';
recordBtn.classList.remove('bg-blue-600', 'hover:bg-blue-700');
recordBtn.classList.add('bg-red-600', 'hover:bg-red-700', 'animate-pulse');
addLog('🎙️ Enregistrement en cours...');
} catch (err) {
addLog('❌ Erreur micro: ' + err.message, 'error');
}
} else {
// Stop recording
mediaRecorder.stop();
recordBtn.textContent = '🎙️ Enregistrer et envoyer';
recordBtn.classList.remove('bg-red-600', 'hover:bg-red-700', 'animate-pulse');
recordBtn.classList.add('bg-blue-600', 'hover:bg-blue-700');
}
});
async function sendAudioToWebSocket(blob) {
addLog('📤 Envoi de l\'audio...');
// Convert blob to base64
const base64 = await blobToBase64(blob);
const message = {
action: 'process',
audio: base64.split(',')[1], // Remove data:audio/webm;base64,
target_lang: document.getElementById('targetLang').value,
include_tts: document.getElementById('includeTts').checked
};
ws.send(JSON.stringify(message));
addLog('✅ Audio envoyé', 'success');
// Clear previous results
document.getElementById('transcriptionDiv').classList.add('hidden');
document.getElementById('traductionDiv').classList.add('hidden');
document.getElementById('audioDiv').classList.add('hidden');
currentStep.textContent = 'En traitement...';
}
function blobToBase64(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
function base64ToBlob(base64, mimeType) {
const byteCharacters = atob(base64);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
return new Blob([byteArray], { type: mimeType });
}
// Auto-connect on load
window.addEventListener('load', () => {
addLog('Page chargée. Cliquez sur "Connecter WebSocket" pour commencer.');
});
</script>
</body>
</html>