Servicio de transcripcion de audio a texto ejecutado localmente con FastAPI, faster-whisper y ffmpeg.
- Endpoint de transcripcion:
POST /transcribe - UI de prueba en navegador:
GET /test(raiz/redirige a/test) - Carga de modelo en background al iniciar
- Normalizacion de audio a WAV 16 kHz mono con
ffmpeg - Limite de concurrencia configurable para transcripciones
- Restriccion de seguridad configurable para
POSTpor host de cliente
main.py: API, validaciones, conversion de audio y transcripciontemplates/test.html: interfaz para subir/grabar audio y probar la APIstart.sh: levantauvicornen background y guarda PID en.uvicorn.pidstop.sh: detiene el servicio por PID o por puertosetup_rocky10.sh: instala dependencias de sistema para Rocky Linux 10.xMODEL_SETUP.md: guia para preparar un modelo local dectranslate2Dockerfile: imagen basada en Rocky Linux 10
- Python 3.10+
ffmpeginstalado en el sistema- Dependencias de
requirements.txt
./setup_rocky10.sh
python3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txtpython3 -m venv .venv
source .venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt./start.shDetener:
./stop.sh./.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000GET /: redirige a/testGET /test: UI para pruebas manuales (subida de archivo o grabacion)POST /transcribe: recibemultipart/form-datacon campofileGET /transcribe/logs?limit=10: devuelve logs recientes de transcripcion
Formatos permitidos por extension: .wav, .mp3, .m4a, .flac, .ogg, .aac, .webm
Ejemplo con curl:
curl -X POST "http://127.0.0.1:8000/transcribe" \
-F "file=@./audio-test.mp3" \
-H "Accept: application/json"Respuesta esperada:
{
"text": "Texto transcrito...",
"language": "es",
"segments": [
{ "start": 0.0, "end": 1.2, "text": "Hola" }
]
}MODEL_NAME(default:small)MODEL_DEVICE(default:cpu)BEAM_SIZE(default:5)MAX_UPLOAD_BYTES(default:26214400, 25 MB)MAX_CONCURRENT_TRANSCRIBES(default:1)FFMPEG_TIMEOUT_SECONDS(default:45)FFPROBE_TIMEOUT_SECONDS(default:15)MAX_AUDIO_SECONDS(default:600)ALLOW_OCTET_STREAM(default:false)ENABLE_SQLITE_LOGS(default:true)TRANSCRIBE_LOG_DB_PATH(default:transcribe_logs.db)MAX_LOG_PAYLOAD_CHARS(default:20000)ALLOWED_POST_HOSTS(default:127.0.0.1,::1; usa*para permitir todos)HOST(solo parastart.sh, default:127.0.0.1)PORT(parastart.shystop.sh, default:8000)
Ejemplo:
MODEL_NAME=small MODEL_DEVICE=cpu BEAM_SIZE=5 ./start.shEn servidor (red interna o reverse proxy), puedes abrir el endpoint:
ALLOWED_POST_HOSTS="*" HOST=0.0.0.0 PORT=8000 ./start.shCada request a POST /transcribe (exito o error) se guarda en SQLite en:
transcribe_logs.db(por defecto)- Puedes desactivar esta funcionalidad con
ENABLE_SQLITE_LOGS=false
Campos registrados: timestamp UTC, host cliente, user-agent, nombre y tipo de archivo, tamano recibido, status HTTP, latencia, payload de respuesta (JSON) y detalle de error.
Consultar ultimos registros:
sqlite3 transcribe_logs.db "SELECT id, created_at, client_host, filename, status_code, latency_ms FROM transcribe_logs ORDER BY id DESC LIMIT 20;"Ver errores recientes:
sqlite3 transcribe_logs.db "SELECT id, created_at, status_code, error_detail FROM transcribe_logs WHERE ok = 0 ORDER BY id DESC LIMIT 20;"Construir:
docker build -t local-whisper-stt:latest .Ejecutar:
docker run --rm -p 8000:8000 --name stt local-whisper-stt:latestSi deseas usar un modelo local ya convertido:
docker run --rm -p 8000:8000 \
-v /opt/models/whisper-small:/models/whisper-small \
-e MODEL_NAME=/models/whisper-small \
local-whisper-stt:latestTambien puedes montar cache de Hugging Face:
docker run --rm -p 8000:8000 \
-v "$HOME/.cache/huggingface:/root/.cache/huggingface" \
local-whisper-stt:latest- El primer arranque puede descargar el modelo si no existe en cache.
- Para un entorno sin internet, prepara/monta un modelo local.
- Consulta
MODEL_SETUP.mdpara conversion deopenai/whisper-smalla formatoctranslate2.
503 Modelo no cargado: verificaMODEL_NAMEy que el modelo exista.422 No se pudo procesar el audio: revisa formato de entrada yffmpeg.413 Archivo demasiado grande: incrementaMAX_UPLOAD_BYTES.413 Audio demasiado largo: incrementaMAX_AUDIO_SECONDSsi necesitas archivos mas largos.429 Servicio ocupado: incrementaMAX_CONCURRENT_TRANSCRIBESo reintenta.
- Validacion previa con
ffprobepara confirmar que el archivo contiene stream de audio. - Limite de duracion de audio configurable (
MAX_AUDIO_SECONDS) para reducir riesgo de DoS. Content-Typeestricto por defecto (no aceptaapplication/octet-streamsalvo que habilitesALLOW_OCTET_STREAM=true).- Conversion con
ffmpegusando protocolos locales permitidos (file,pipe) para reducir superficie de ataque.