Skip to content

Repository files navigation

audio-transcriber

Self-hosted audio transcription API. Upload an audio file, get back a timestamped transcript plus the non-speech sounds that occurred alongside it.

Speech comes from faster-whisper; sound events (applause, laughter, music, vehicles) come from PANNs. Jobs run through a Redis-backed queue, and clients follow progress over Server-Sent Events rather than polling.

Runs on your own hardware. No third-party transcription service, no per-minute billing, no audio leaving your machine.

Why this exists

Hosted transcription APIs charge per minute and require shipping your audio to someone else. For recordings that are private, long, or simply numerous, that is the wrong trade. This packages the two best open models for the job behind one HTTP API you can run on a spare box.

The sound-event detection is the part that hosted APIs usually don't give you. A transcript that says [00:04:12] (applause) between two paragraphs is more useful than one that silently drops it.

Features

  • Timestamped transcripts via faster-whisper, with model size configurable per deployment
  • Sound event detection via PANNs, interleaved into the transcript timeline
  • Redis job queue with a bounded depth, so a burst of uploads degrades predictably instead of exhausting memory
  • Live progress over SSE including queue position while a job is still waiting
  • Cancellation of queued or running jobs
  • API-key auth and rate limiting as middleware on every route
  • GPU auto-detection at setup and build time, with a CPU fallback

Quick start

git clone https://github.com/jhgit0219/audio-transcriber.git
cd audio-transcriber
cp .env.example .env      # set API_KEY at minimum
docker compose up -d --build

The API listens on http://localhost:8000.

Without Docker:

./setup.sh                # creates .venv, detects GPU, installs deps
source .venv/bin/activate
uvicorn app.main:app --host 0.0.0.0 --port 8000

setup.sh picks a CUDA or CPU PyTorch build depending on what it finds on the host.

Configuration

Every value is read from the environment. See .env.example.

Variable Default Purpose
API_KEY (required) Value clients must send in the x-api-key header
ALLOWED_ORIGIN — Single origin permitted by CORS
WHISPER_MODEL medium faster-whisper model size (tiny … large-v3)
RATE_LIMIT 5 Requests per window, per client
REDIS_URL redis://localhost:6379/0 Queue backend
JOB_TTL 600 Seconds a finished job's result is retained
MAX_QUEUE 20 Queue depth before uploads are rejected

API

All routes require an x-api-key header matching API_KEY.

POST /api/transcribe

Accepts a multipart/form-data upload under the field file. The upload is streamed to disk in chunks, so file size is bounded by disk rather than memory. Returns immediately with a job handle.

curl -X POST http://localhost:8000/api/transcribe \
  -H "x-api-key: $API_KEY" \
  -F "file=@meeting.mp3"
{
  "job_id": "3f9c1a72-...",
  "status": "queued",
  "stream_url": "/api/jobs/3f9c1a72-.../stream"
}

GET /api/jobs/{job_id}/stream

Server-Sent Events for the life of the job. Emits queue position while waiting, progress while transcribing, and one terminal event carrying either the transcript or an error.

curl -N http://localhost:8000/api/jobs/$JOB_ID/stream -H "x-api-key: $API_KEY"
const es = new EventSource(`/api/jobs/${jobId}/stream`);
es.onmessage = (e) => {
  const { status, progress, result } = JSON.parse(e.data);
  if (status === "completed") { render(result); es.close(); }
};

GET /api/jobs/{job_id}

One-shot status for clients that would rather poll, or that reconnect after losing the stream. Returns status, progress, timestamps, and the result once available.

DELETE /api/jobs/{job_id}

Cancels a queued or running job and releases its queue slot.

Architecture

POST /api/transcribe
        │
        ├─ stream upload to disk (chunked)
        ├─ enqueue job id                    ──►  Redis  ◄──  worker loop
        └─ return { job_id, stream_url }                        │
                                                    faster-whisper (speech)
GET /api/jobs/{id}/stream                           PANNs (sound events)
        │                                                       │
        └─ SSE: queue position → progress → result  ◄───────────┘

Redis holds both the queue and job state, which keeps the API process stateless: it can be restarted or scaled horizontally without losing in-flight work.

app/
  main.py              FastAPI app, middleware chain, router wiring
  core.py              Settings loaded from environment
  middleware/          API-key auth, rate limiting
  routes/              transcribe.py (upload), jobs.py (status, SSE, cancel)
  services/            job_queue.py, redis_client.py, worker.py,
                       transcriber.py (faster-whisper), sound_detector.py (PANNs)
  schemas/             Pydantic request and response models

Deployment

The included docker-compose.yml runs Redis alongside the API and persists the model cache in a named volume, so the Whisper weights survive container rebuilds. Redis is configured with maxmemory-policy noeviction on purpose: silently dropping queued jobs under memory pressure would be worse than refusing new ones.

For GPU hosts, build with --build-arg COMPUTE=cuda.

License

MIT. See LICENSE.

About

A self-hosted audio transcription API powered by faster-whisper and PANNs sound detection. Upload audio files and get timestamped transcripts with detected sound events. Features a Redis-backed job queue with real-time SSE progress streaming, queue positioning, and cancellation support. Dockerized for deployment.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages