5.6 KiB
Migrate suaveplan-audio-fingerprint (Python tier) into genesis
Package: @suaveplan/audio-fingerprint
Status: PROPOSED
Type: new-package (migration from upstream Python tier)
- Design: ./design.md
- Spec delta: ./specs/audio-fingerprint/spec.md
Why
@suaveplan/react-audio-fingerprint already ships a browser-tier TypeScript implementation of Shazam-style audio fingerprinting (peak detection on a magnitude spectrogram). For corpus-scale identification, recognised broadcast monitoring, and noise-robust matching of compressed or modified audio, the browser implementation is intentionally lightweight. Upstream /mnt/s/web/suaveplan/packages/python/audio-fingerprint/ carries the high-accuracy backend: a librosa + scipy chromagram pipeline wrapped in a FastAPI service, plus a typed HTTP client (PythonAudioService) that browser code calls.
The React sibling already reserves a python-backend subpath for this service. Without the Python tier the high-accuracy path is unreachable. This change migrates the Python service plus the TypeScript HTTP client to packages/media/audio/audio-fingerprint/ so the React package can stop deferring the path and downstream apps can run a real fingerprint backend behind their browser surface.
What changes
- ADDED:
@suaveplan/audio-fingerprintpackage atpackages/media/audio/audio-fingerprint/(Python tier + TS HTTP client bridge). - ADDED:
src/fingerprint.py—AudioFingerprinterclass (chromagram + 2D peak detection + constellation hashing + Jaccard matching + median time-offset estimation). - ADDED:
src/api/app.py— FastAPI application exposingGET /,POST /fingerprint,POST /matchwith CORS forlocalhost:3000+localhost:5173. - ADDED:
src/client/python-audio-service.ts— typedPythonAudioServiceHTTP client (healthCheck/fingerprint/match) withAbortController-based timeouts. - ADDED:
src/types/audio-fingerprint.types.ts—HealthCheckResponse,AudioFingerprint,FingerprintRequest/Response,MatchRequest/Response,ErrorResponse,AudioFingerprintConfig. - ADDED:
pyproject.toml+environment.ymlfor the Python runtime (librosa, scipy, numpy, fastapi, uvicorn, soundfile, python-multipart; dev: pytest, pytest-cov, pytest-asyncio, httpx, ruff, mypy). - ADDED:
pytest --cov --cov-fail-under=100Python test suite attests/test_fingerprint.py+tests/test_api.py. - ADDED: TS bridge vitest suite at
tests/python-audio-service.test.tsusing a realfetchmock against an in-memory ASGI surface emulator andAbortControllerfor timeout semantics.
Out of scope
- Production deployment manifests (Dockerfile, Helm chart) — handled per-app deploy, not in the library.
- Wiring the React sibling's
python-backendsubpath to consume this client — separate follow-up so the React change has its own spec delta. - ChromaPrint / AcoustID interoperability — different algorithm; would be a peer package, not a feature here.
- Native (non-FastAPI) RPC transport — the upstream uses HTTP; we preserve parity. A gRPC adapter is a future change.
Source mapping
This change derives from /mnt/s/web/suaveplan/packages/python/audio-fingerprint/. There is no enterprise/audio-fingerprint.md audit on main (the upstream is a Python-tier service rather than a TS library), so the source mapping references the upstream source tree directly.
| Requirement | Source lines | Notes |
|---|---|---|
| Chromagram fingerprinting | src/fingerprint.py L67-L90 |
verbatim algorithm, repackaged |
| 2D peak detection | src/fingerprint.py L92-L132 |
verbatim (scipy.ndimage.maximum_filter (3,5)) |
| Constellation hashing | src/fingerprint.py L134-L171 |
verbatim fan-pairing |
| Full-file pipeline | src/fingerprint.py L173-L213 |
verbatim |
| Jaccard matching + time offset | src/fingerprint.py L215-L267 |
verbatim |
FastAPI / + /fingerprint + /match |
src/api/app.py L31-L171 |
verbatim endpoints, CORS preserved |
| TS HTTP client | src/PythonAudioService.ts L45-L175 |
renamed file to kebab-case for genesis convention; surface preserved |
| Response / request types | types/index.ts L7-L111 |
preserved field-for-field |
Requirements with no source line
| Requirement | Justification |
|---|---|
client.timeout propagates AbortController reasons |
Upstream throws on timeout but the message tells callers nothing actionable; genesis surfaces "Request timeout after Nms: <endpoint>" already-present in the upstream client (kept verbatim, listed here because it appears as its own spec scenario). |
Verification approach
- Python:
pytest --cov=src --cov-fail-under=100from the package directory; coversfingerprint.pyandapi/app.pyend-to-end including FastAPITestClientandhttpx.ASGITransportasync paths. - TS: vitest suite at
tests/python-audio-service.test.tsusing runner-neutral imports from@suaveplan/testing/runner, realfetchstubbed via a script-injected handler (novi.mock), pinning every public method, the timeout path, the error-response parsing path, and the unknown-error fallback path. - Both gates exit with 100% line + branch coverage on the touched modules.
Impact
Affected dependents
Run bunx turbo ls --filter=...@suaveplan/audio-fingerprint to enumerate. The package is new; the only logical dependent is @suaveplan/react-audio-fingerprint, whose python-backend subpath is intentionally not wired in this change.
@suaveplan/react-audio-fingerprint— [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: thepython-backendsubpath wiring is deferred to its own change; this migration is additive and does not modify the React surface)