first commit
This commit is contained in:
131
docs/media/audio/audio-codecs/src/detect/detect-format.md
Normal file
131
docs/media/audio/audio-codecs/src/detect/detect-format.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# `detect/detect-format`
|
||||
|
||||
Format detection and source-resolution helpers — the runtime-gated entry
|
||||
point that turns any `Source` (string path, URL, ArrayBuffer, Uint8Array,
|
||||
ReadableStream) into a header slice plus an optional detected format
|
||||
name.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
The package handles five concrete `Source` shapes; each needs a
|
||||
different platform-specific resolution strategy. Without a single
|
||||
helper, every public entry point (`decodeSource`, `encodeTo`,
|
||||
`progressiveDecode`) would re-implement the same `string | URL |
|
||||
ArrayBuffer | Uint8Array | ReadableStream` switch — duplicating wiring
|
||||
and risking divergent behaviour for the edge cases. This module
|
||||
centralises it.
|
||||
|
||||
The detection delegates magic-byte sniffing to
|
||||
`@suaveplan/audio-metadata`'s `detectAudioFormat` — the same function
|
||||
used by every other package that needs to identify audio bytes
|
||||
— so the registry's `detect` and the entry-point `detectSource` always
|
||||
agree.
|
||||
|
||||
## Features
|
||||
|
||||
- `Source` type union — `string | URL | ArrayBuffer | Uint8Array |
|
||||
ReadableStream<Uint8Array>`.
|
||||
- `DetectResult` shape — `{ format: string | undefined; bytes?:
|
||||
Uint8Array; path?: string; reader?: ReadableStreamDefaultReader }`.
|
||||
The optional fields tell the caller what kind of resolution happened
|
||||
so they can continue draining the source efficiently.
|
||||
- `detectSource(source)` — reads at most `HEADER_SIZE` (256 KiB) bytes
|
||||
from the source head, sniffs the format, and returns either the
|
||||
detected format name or `undefined` for unknown content. Never
|
||||
throws for unknown content — only throws `INVALID_SOURCE` for a
|
||||
source value that is not a recognised variant or a fetch that
|
||||
returned no body.
|
||||
- `resolveSource(source)` — drains the source completely into a single
|
||||
`Uint8Array`. Used by `decodeSource` to materialise the full payload
|
||||
before dispatch.
|
||||
- `HEADER_SIZE` — exported constant set to `256 * 1024`. Reading
|
||||
exactly this many bytes keeps detection cheap even for multi-GB
|
||||
files while being large enough to cover every realistic header
|
||||
layout (ID3v2 with embedded artwork, AIFF with large COMM-adjacent
|
||||
comment chunks, etc).
|
||||
|
||||
## Resolution rules
|
||||
|
||||
- `string` starting with `http://` / `https://` / `data:` / `blob:`,
|
||||
or any `URL` instance → resolved via `fetch()`. Header read via the
|
||||
response stream reader, which is returned to the caller for
|
||||
continued draining.
|
||||
- `string` starting with `file://` → resolved via dynamic
|
||||
`import('node:fs/promises')`. Returns the resolved file path so the
|
||||
caller can perform random-access reads.
|
||||
- Other bare `string` → on a server runtime (process.versions.node is
|
||||
a string) resolves via `fs/promises.readFile`; in a browser runtime
|
||||
resolves via `fetch()`.
|
||||
- `ArrayBuffer` / `Uint8Array` → header is the first 256 KiB slice;
|
||||
no platform call.
|
||||
- `ReadableStream<Uint8Array>` → reader drains until 256 KiB observed;
|
||||
the original reader is returned for continued draining.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { detectSource } from "@suaveplan/audio-codecs/detect/detect-format";
|
||||
|
||||
const result = await detectSource(bytes);
|
||||
if (result.format === "wav") {
|
||||
decodeWav(result.bytes!);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
Resolve a remote URL once and reuse the detection result + stream
|
||||
reader without re-fetching:
|
||||
|
||||
```ts
|
||||
import { detectSource, resolveSource } from "@suaveplan/audio-codecs/detect/detect-format";
|
||||
|
||||
const result = await detectSource("https://cdn.example/audio.flac");
|
||||
if (result.format === "flac") {
|
||||
// We have the first 256 KiB cached on result.bytes, and the rest
|
||||
// is available via result.reader.
|
||||
const rest = await drainReader(result.reader!);
|
||||
const full = concat(result.bytes!, rest);
|
||||
return decodeFlac(full);
|
||||
}
|
||||
|
||||
// resolveSource fully drains regardless of source shape.
|
||||
const buf = await resolveSource("file:///srv/audio/in.wav");
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `type Source = string | URL | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array>`
|
||||
- `interface DetectResult { format: string | undefined; bytes?: Uint8Array; path?: string; reader?: ReadableStreamDefaultReader<Uint8Array> }`
|
||||
- `const HEADER_SIZE: number` — `256 * 1024`
|
||||
- `async function detectSource(source: Source): Promise<DetectResult>` —
|
||||
throws `AUDIO_CODECS.INVALID_SOURCE` for an unrecognised source
|
||||
value or when `fetch()` returns a body-less response.
|
||||
- `async function resolveSource(source: Source): Promise<Uint8Array>` —
|
||||
throws `AUDIO_CODECS.INVALID_SOURCE` for an unrecognised source.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `isServerRuntime()` is a closure over `typeof process !== "undefined"
|
||||
&& typeof process.versions?.node === "string"`. The function exists
|
||||
so the runtime branch is not evaluated at module-load time — the
|
||||
package stays universal with no static `node:fs` import.
|
||||
- The `node:fs/promises` import is **always** inside a runtime-gated
|
||||
dynamic `import()` — never a top-level import. This keeps the package
|
||||
importable from browser bundles without bundler config tweaks.
|
||||
- The browser-only `fetch()` fallback paths (lines 154-156 in
|
||||
`detectSource`, 214-216 in `resolveSource`) are unreachable under
|
||||
Node tests; structural coverage of that branch requires a happy-dom
|
||||
or browser-target test harness (a future integration suite).
|
||||
- The `URL` instance branch and the `http://` / `https://` /
|
||||
`data:` / `blob:` string branch both route through
|
||||
`fetchHeaderViaFetch` — no logic is duplicated.
|
||||
- `readHeaderFromStream` keeps reading until at least `HEADER_SIZE`
|
||||
bytes are observed OR the stream ends. The resulting buffer may
|
||||
be larger than `HEADER_SIZE` because chunks arrive whole; that's
|
||||
intentional — over-read is cheap and avoids fragmenting the
|
||||
caller's understanding of what bytes were consumed.
|
||||
Reference in New Issue
Block a user