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

5.4 KiB

client/python-audio-service.ts — PythonAudioService

(no submodules)

Purpose

PythonAudioService is the typed HTTP client TypeScript and browser callers use to talk to the Python-tier audio fingerprinting service. It wraps fetch, handles multipart FormData uploads, propagates AbortController-based timeouts, and translates the FastAPI error envelope ({detail: string}) into well-typed JavaScript Error instances with informative messages. The class is the only runtime export of the @suaveplan/audio-fingerprint/client subpath, and it is also re-exported from the package root.

The client deliberately depends on nothing beyond the platform fetch, FormData, and AbortController APIs. Browsers (Chrome, Firefox, Safari 14+, Edge) and modern server runtimes (Node 18+, Bun, Deno) all expose these, so the same code runs unchanged in every consumer tier. There is no axios, no isomorphic-fetch polyfill, and no transitive dependency closure to audit.

Features

  • Configurable baseUrl (default http://localhost:8000) and timeout in ms (default 30000).
  • healthCheck() — GET /, returns the typed HealthCheckResponse.
  • fingerprint(req) — POST /fingerprint with file plus optional peak_threshold / fan_value.
  • match(req) — POST /match with query_file, reference_file, plus optional peak_threshold / fan_value / min_matches.
  • AbortController-based timeouts; on expiry the client rejects with Error("Request timeout after Nms: <endpoint>").
  • Error parsing: HTTP-non-OK responses are parsed as JSON; if the body is {detail: "..."}, that string becomes the Error.message. Otherwise the client falls back to HTTP <status>: <statusText>.
  • Non-Error thrown values from fetch are normalised to Error("Unknown error occurred: <string>").

Usage: Basic

import { PythonAudioService } from "@suaveplan/audio-fingerprint/client";

const client = new PythonAudioService();
const health = await client.healthCheck();
// { status: "ok", service: "audio-fingerprinting" }

Usage: Advanced

import { PythonAudioService } from "@suaveplan/audio-fingerprint";
import type {
    FingerprintResponse,
    MatchResponse,
} from "@suaveplan/audio-fingerprint/types";

const client = new PythonAudioService({
    baseUrl: "https://audio-fp.internal.example.com",
    timeout: 60_000,
});

async function identify(query: File, reference: File): Promise<MatchResponse> {
    try {
        const queryFingerprint: FingerprintResponse = await client.fingerprint({
            file: query,
            peak_threshold: 0.55,
            fan_value: 18,
        });
        // ... persist queryFingerprint to corpus if needed
        return await client.match({
            query_file: query,
            reference_file: reference,
            peak_threshold: 0.55,
            fan_value: 18,
            min_matches: 25,
        });
    } catch (error) {
        // The thrown message is either the FastAPI `detail`, a status-text
        // fallback, or the timeout / unknown-error envelope.
        console.error("identification failed:", (error as Error).message);
        throw error;
    }
}

API Reference

Constructor

constructor(config?: AudioFingerprintConfig)
Field Type Default Description
config.baseUrl string "http://localhost:8000" Base URL of the FastAPI service.
config.timeout number 30000 Per-request timeout in milliseconds.

Methods

Method Signature Throws
healthCheck () => Promise<HealthCheckResponse> Error on HTTP error, timeout, or network failure.
fingerprint (req: FingerprintRequest) => Promise<FingerprintResponse> Error on HTTP error, timeout, or network failure.
match (req: MatchRequest) => Promise<MatchResponse> Error on HTTP error, timeout, or network failure.

Error envelope

Cause Error.message
Timeout Request timeout after <Nms>: <endpoint>
HTTP non-OK with parseable {detail: string} body <detail>
HTTP non-OK with non-JSON or detail-missing body HTTP <status>: <statusText>
fetch throws a non-Error value Unknown error occurred: <stringified value>
fetch throws a non-Abort Error the original error is re-raised verbatim

Implementation Notes

  • The timeout is implemented with a setTimeout that calls controller.abort(); the timer is cleared both on success and on failure (try/catch + redundant clearTimeout after the await). This avoids leaking timers in long-lived browsers tabs.
  • AbortError is detected by error.name === "AbortError" rather than by instanceof DOMException to remain runtime-agnostic (DOMException is browser-only).
  • Optional form fields are only appended when the caller supplies them; the FastAPI defaults remain authoritative.
  • The error-body JSON parse is wrapped in a try/catch with no rethrow inside the catch — a parse failure should NOT mask the original HTTP error, only fall through to the status-text fallback.
  • Type-only imports from ../types/audio-fingerprint.types.js are used so the client carries zero runtime cost from the type definitions.
  • The client has no peer dependency on any framework; consumers wrap it in their own React/Vue/Svelte hooks as appropriate. The sibling @suaveplan/react-audio-fingerprint package's python-backend subpath is the canonical React adapter.