Files
skills/docs/media/audio/audio-fingerprint/src/api/app.md
2026-05-29 08:20:56 +01:00

4.9 KiB

api/app.py — FastAPI service

(no submodules)

Purpose

api/app.py wraps the AudioFingerprinter core into a FastAPI HTTP service. It is the network surface the TypeScript PythonAudioService client talks to from browsers (via the React-tier sibling) and from server-side JavaScript runtimes. The application exposes a health-check endpoint and two operational endpoints (POST /fingerprint, POST /match) that accept multipart file uploads and return JSON. It is intentionally a thin REST shell: every business decision (chromagram parameters, peak threshold, fan value, min matches) is driven by client-supplied form fields.

The service is deployment-tier code — it runs under uvicorn and is intended to live behind a reverse proxy in production. CORS is pre-configured for the two local development origins (http://localhost:3000 and http://localhost:5173); add production origins via your deployment-time CORSMiddleware override.

Features

  • GET / health-check returning {"status": "ok", "service": "audio-fingerprinting"}.
  • POST /fingerprint accepting an audio file, optional peak_threshold and fan_value, returning fingerprint hashes and statistics.
  • POST /match accepting two audio files, optional peak_threshold, fan_value, min_matches, returning the match decision, count, confidence, and time offset.
  • Temporary-file lifecycle: each upload is persisted to NamedTemporaryFile, processed, and unlinked in a finally block — including on the failure path.
  • Uniform HTTP-500 error envelope: pipeline exceptions are caught and re-raised as HTTPException(status_code=500, detail="Failed to ... : <message>").
  • CORS pre-configured for localhost:3000 and localhost:5173.

Usage: Basic

# Start the service
micromamba run -n suaveplan-audio-fingerprint \
  uvicorn src.api.app:app --host 0.0.0.0 --port 8000

# Health check
curl http://localhost:8000/

# Fingerprint a single file
curl -X POST http://localhost:8000/fingerprint -F "file=@track.mp3"

Usage: Advanced

# Tune the algorithm parameters at request time
curl -X POST http://localhost:8000/fingerprint \
  -F "file=@track.flac" \
  -F "peak_threshold=0.6" \
  -F "fan_value=20"

# Match two files with a stricter min_matches threshold
curl -X POST http://localhost:8000/match \
  -F "query_file=@query.wav" \
  -F "reference_file=@reference.wav" \
  -F "peak_threshold=0.5" \
  -F "fan_value=15" \
  -F "min_matches=25"
# Programmatic in-process testing via httpx ASGITransport
from httpx import ASGITransport, AsyncClient
from src.api.app import app

async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac:
    response = await ac.get("/")
    assert response.json() == {"status": "ok", "service": "audio-fingerprinting"}

API Reference

Method Path Form fields Response
GET / — {"status": "ok", "service": "audio-fingerprinting"}
POST /fingerprint file (UploadFile, required), peak_threshold (float, default 0.5), fan_value (int, default 15) {filename, duration, peaks_count, fingerprints_count, fingerprints[{hash,time,delta}]}
POST /match query_file (UploadFile, required), reference_file (UploadFile, required), peak_threshold (float, default 0.5), fan_value (int, default 15), min_matches (int, default 10) {query_file, reference_file, query_duration, reference_duration, matched, match_count, confidence, time_offset}

Failure paths

  • Missing required upload(s) -> HTTP 422 (FastAPI validation error envelope).
  • Pipeline failure (unreadable audio, decode error) -> HTTP 500 with detail starting with Failed to process audio file: (for /fingerprint) or Failed to match audio files: (for /match).

Implementation Notes

  • A single module-level AudioFingerprinter() instance is reused across requests. It has no mutable per-request state; only its analysis parameters are shared, and those are configuration-stable.
  • tempfile.NamedTemporaryFile(delete=False) is used so the descriptor can be closed while the file is still on disk for librosa to open by path. The unlink lives in a finally clause and uses missing_ok=True so the cleanup is idempotent even if librosa already moved or removed the file.
  • The match endpoint persists both uploads inside a single nested with block; this guarantees both temp paths are bound before either pipeline call runs, simplifying the cleanup in the finally clause.
  • CORSMiddleware is wired with allow_credentials=True, allow_methods=["*"], allow_headers=["*"]. The TestClient suite exercises the preflight (OPTIONS) path for http://localhost:3000 to ensure the configuration is live.
  • The version="0.1.0" field on the FastAPI(...) constructor matches the pyproject.toml and package.json versions; bump all three in lockstep at graduation to 1.0.0.