Initial Commit
This commit is contained in:
100
packages/media/audio/voice-elevenlabs/src/voices/voices.md
Normal file
100
packages/media/audio/voice-elevenlabs/src/voices/voices.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# `voices`
|
||||
|
||||
ElevenLabs implementation of `@suaveplan/voice`'s `VoiceRepository`
|
||||
interface. Lists voices via `GET /v1/voices` and looks them up via
|
||||
`GET /v1/voices/{voice_id}`. 404 on lookup is normalised to `null` per
|
||||
the contract.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
ElevenLabs returns voice records with a labels bag (`accent`,
|
||||
`description`, `age`, `gender`, `use_case`) and a settings bag
|
||||
(`stability`, `similarity_boost`, `style`, `use_speaker_boost`) that
|
||||
look nothing like the `Voice` shape. This adapter maps the
|
||||
upstream record onto the package schema, normalises gender / age
|
||||
strings, copies labels into a `metadata` bag for round-tripping, and
|
||||
honours the `VoiceRepository.get()` contract that "not found" must
|
||||
resolve to `null` rather than throw.
|
||||
|
||||
## Features
|
||||
|
||||
- `list(options?)` — paged listing with `category`, `languageCode`,
|
||||
`search`, `pageSize`, and `pageToken` filters.
|
||||
- `get(voiceId)` — returns the mapped voice or `null` on 404.
|
||||
- Maps unknown / non-canonical category strings to `"premade"`.
|
||||
- Tolerates missing `gender` / `age` fields and only emits values that
|
||||
match the canonical enums; unknown values are dropped silently.
|
||||
- Pulls language code from `fine_tuning.language` into a single-element
|
||||
`languageCodes` array.
|
||||
- Settings mapping converts each ElevenLabs snake_case field into the
|
||||
camelCase equivalent, omitting unspecified entries.
|
||||
- Description falls back to `labels.description` when the top-level
|
||||
field is absent.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { createElevenLabsAdapters } from "@suaveplan/third-party-elevenlabs";
|
||||
|
||||
const { voices } = createElevenLabsAdapters({ apiKey: KEY });
|
||||
|
||||
const page = await voices.list({ category: "premade" });
|
||||
for (const v of page.voices) console.log(v.id, v.name);
|
||||
|
||||
const adam = await voices.get("21m00Tcm4TlvDq8ikWAM");
|
||||
if (adam === null) throw new Error("voice missing");
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { createElevenLabsAdapters } from "@suaveplan/third-party-elevenlabs";
|
||||
|
||||
const { voices } = createElevenLabsAdapters({ apiKey: KEY });
|
||||
|
||||
let pageToken: string | undefined;
|
||||
const all: Voice[] = [];
|
||||
do {
|
||||
const page = await voices.list({
|
||||
category: "professional",
|
||||
languageCode: "en",
|
||||
search: "narrator",
|
||||
pageSize: 50,
|
||||
...(pageToken !== undefined ? { pageToken } : {}),
|
||||
});
|
||||
all.push(...page.voices);
|
||||
pageToken = page.hasMore ? page.nextPageToken : undefined;
|
||||
} while (pageToken !== undefined);
|
||||
|
||||
console.log(`${all.length} voices retrieved`);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `class ElevenLabsVoiceRepository implements VoiceRepository`
|
||||
- `providerName: "elevenlabs"`
|
||||
- `list(options?: ListVoicesOptions): Promise<VoiceList>` — returns
|
||||
`{ voices, hasMore, nextPageToken? }`. `hasMore` defaults to `false`
|
||||
when the upstream omits it.
|
||||
- `get(voiceId: string): Promise<Voice | null>` — `null` on 404, throws
|
||||
on any other error.
|
||||
|
||||
Throws: `ProviderNotConfiguredError`, `ProviderRateLimitedError`, and
|
||||
generic VoiceError-shaped failures.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- Age detection uses substring matching on the ElevenLabs label
|
||||
(`"young adult"` → `"young"`, `"middle aged"` → `"middle"`, `"old man"`
|
||||
→ `"old"`) because ElevenLabs has no canonical enum.
|
||||
- Gender is matched exactly against `male` / `female` / `neutral`; other
|
||||
values (e.g. ElevenLabs's occasional `"non-binary"`) are dropped — the
|
||||
raw label remains accessible via `metadata`.
|
||||
- `voice_id` is `encodeURIComponent`-escaped before being interpolated
|
||||
into the URL so that workspace IDs with spaces work correctly.
|
||||
- A 404 on `get` is the **only** non-2xx response that's swallowed; every
|
||||
other error (401 / 422 / 429 / 5xx) throws.
|
||||
Reference in New Issue
Block a user