Files
suaveplan/openspec/archive/2026-05-23-enterprise-audio-fingerprint-python/proposal.md
2026-05-27 23:29:53 +01:00

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-fingerprint package at packages/media/audio/audio-fingerprint/ (Python tier + TS HTTP client bridge).
  • ADDED: src/fingerprint.py — AudioFingerprinter class (chromagram + 2D peak detection + constellation hashing + Jaccard matching + median time-offset estimation).
  • ADDED: src/api/app.py — FastAPI application exposing GET /, POST /fingerprint, POST /match with CORS for localhost:3000 + localhost:5173.
  • ADDED: src/client/python-audio-service.ts — typed PythonAudioService HTTP client (healthCheck / fingerprint / match) with AbortController-based timeouts.
  • ADDED: src/types/audio-fingerprint.types.ts — HealthCheckResponse, AudioFingerprint, FingerprintRequest / Response, MatchRequest / Response, ErrorResponse, AudioFingerprintConfig.
  • ADDED: pyproject.toml + environment.yml for 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=100 Python test suite at tests/test_fingerprint.py + tests/test_api.py.
  • ADDED: TS bridge vitest suite at tests/python-audio-service.test.ts using a real fetch mock against an in-memory ASGI surface emulator and AbortController for 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-backend subpath 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=100 from the package directory; covers fingerprint.py and api/app.py end-to-end including FastAPI TestClient and httpx.ASGITransport async paths.
  • TS: vitest suite at tests/python-audio-service.test.ts using runner-neutral imports from @suaveplan/testing/runner, real fetch stubbed via a script-injected handler (no vi.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: the python-backend subpath wiring is deferred to its own change; this migration is additive and does not modify the React surface)