first commit
This commit is contained in:
83
docs/media/audio/audio-fingerprint/src/api/app.md
Normal file
83
docs/media/audio/audio-fingerprint/src/api/app.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# `api/app.py` — FastAPI service
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## 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
|
||||
|
||||
```bash
|
||||
# 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
|
||||
|
||||
```bash
|
||||
# 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"
|
||||
```
|
||||
|
||||
```python
|
||||
# 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`.
|
||||
Reference in New Issue
Block a user