3b- Mostrar la transcripción parcial

 Mostrar la transcripción parcial (⏳ ...) en vivo en la interfaz web,

como se ve en la terminal cuando dictas — pero en el navegador.


✅ Solución óptima y eficiente

Vamos a:

  • Emitir las transcripciones parciales desde el servidor con Flask-SocketIO

  • Mostrarlas en tiempo real en la interfaz web con JavaScript

  • No sobrecargar el sistema porque:

    • El texto parcial se actualiza cada vez que cambia

    • Solo se envía texto, no audio


🧠 ¿Cómo lo integramos?

🔧 Cambios en:

  • transcriptor.py: para emitir parcial por WebSocket (sin bloquear)

  • app.py: ahora usará SocketIO

  • index.html y script.js: reciben parcial y final


🔁 Paso a paso


✅ 1. transcriptor.py modificado para usar socketio.emit

python
import queue import sounddevice as sd from vosk import Model, KaldiRecognizer import json import threading import os from flask_socketio import SocketIO class Transcriptor: def __init__(self, socketio: SocketIO, model_path="models/vosk-model-small-es-0.42", sample_rate=16000): self.sample_rate = sample_rate self.q = queue.Queue() self.model = Model(model_path) self.recognizer = KaldiRecognizer(self.model, self.sample_rate) self.thread = None self.running = False self.transcripcion = "" self.socketio = socketio def _callback(self, indata, frames, time, status): if status: print("⚠️", status) self.q.put(bytes(indata)) def _procesar_audio(self): with sd.RawInputStream(samplerate=self.sample_rate, blocksize=4000, dtype='int16', channels=1, callback=self._callback): print("🎤 Transcripción iniciada...") last_partial = "" while self.running: try: data = self.q.get(timeout=0.5) if self.recognizer.AcceptWaveform(data): result = json.loads(self.recognizer.Result()) texto = result.get("text", "") if texto: self.transcripcion += texto + " " self.socketio.emit("texto_final", texto) else: parcial = json.loads(self.recognizer.PartialResult()).get("partial", "") if parcial != last_partial: self.socketio.emit("texto_parcial", parcial) last_partial = parcial except queue.Empty: continue def iniciar(self): if self.running: return self.running = True self.transcripcion = "" self.thread = threading.Thread(target=self._procesar_audio, daemon=True) self.thread.start() def detener(self): self.running = False if self.thread: self.thread.join() def obtener_transcripcion(self): return self.transcripcion.strip()

✅ 2. app.py usando Flask-SocketIO

python
from flask import Flask, render_template from flask_socketio import SocketIO from transcriptor import Transcriptor app = Flask(__name__) socketio = SocketIO(app, cors_allowed_origins="*") # Pasa socketio al transcriptor transcriptor = Transcriptor(socketio) @app.route('/') def index(): return render_template('index.html') @socketio.on('iniciar') def handle_iniciar(): transcriptor.iniciar() @socketio.on('detener') def handle_detener(): transcriptor.detener() socketio.emit("texto_final", transcriptor.obtener_transcripcion()) if __name__ == '__main__': socketio.run(app, debug=True)

✅ 3. templates/index.html + script.js con texto parcial

index.html

html
<!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <title>🎙️ Transcriptor Web con Vosk</title> <style> body { font-family: sans-serif; padding: 2rem; background: #eef; } button { padding: 1rem; font-size: 1rem; margin-right: 1rem; } textarea { width: 100%; height: 200px; margin-top: 1rem; font-size: 1.1rem; } #parcial { font-style: italic; color: #666; margin-top: 0.5rem; } </style> </head> <body> <h1>🎤 Transcriptor con Control Web</h1> <button onclick="iniciar()">Iniciar</button> <button onclick="detener()">Detener</button> <div id="estado">Estado: Inactivo</div> <div id="parcial">⏳ ...</div> <textarea id="texto" readonly></textarea> <script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script> <script src="/static/script.js"></script> </body> </html>

script.js

javascript
const socket = io(); const texto = document.getElementById("texto"); const parcial = document.getElementById("parcial"); const estado = document.getElementById("estado"); function iniciar() { socket.emit("iniciar"); estado.textContent = "Estado: Escuchando..."; parcial.textContent = "⏳ ..."; texto.value = ""; } function detener() { socket.emit("detener"); estado.textContent = "Estado: Detenido"; } socket.on("texto_parcial", data => { parcial.textContent = "⏳ " + data; }); socket.on("texto_final", data => { texto.value += data + " "; parcial.textContent = "⏳ ..."; });

✅ Resultado

  • 🎤 Cuando hablas, ves los ⏳ Parcial: ... en la interfaz

  • 📝 Cuando termina una frase, aparece en el área de texto

  • ✅ Todo fluido, sin reiniciar nada, 100% en tiempo real


¿Quieres que el botón "detener" guarde la transcripción a un archivo .txt, o que permita copiar el texto al portapapeles?

Comentarios

Entradas más populares de este blog

b-Web Speech API

captura video con audio del sistema (como música o sonidos del navegador) pero sin usar el micrófono

EL audio lo envia el navegador-Transcripción de Voz con Whisper