@breeze.blue/sdk 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/dist/_transform.d.ts +2 -0
- package/dist/_transform.js +38 -0
- package/dist/audio.d.ts +49 -0
- package/dist/audio.js +82 -0
- package/dist/client.d.ts +87 -0
- package/dist/client.js +331 -0
- package/dist/errors.d.ts +50 -0
- package/dist/errors.js +151 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/node.d.ts +5 -0
- package/dist/node.js +74 -0
- package/dist/types.d.ts +275 -0
- package/dist/types.js +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/package.json +52 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
- Added async text-to-speech helpers: `client.textToSpeech.createJob(...)`,
|
|
6
|
+
`client.generationJobs.get(...)`, and
|
|
7
|
+
`client.generationJobs.downloadAudio(...)` for long text, reference-heavy
|
|
8
|
+
voices, and batch production workflows.
|
|
9
|
+
- Added `BreezeBlueGenerationNotReadyError` for active async job audio
|
|
10
|
+
downloads, with `retryAfter` parsed from the `Retry-After` response header.
|
|
11
|
+
|
|
12
|
+
## 0.2.1
|
|
13
|
+
|
|
14
|
+
- Fixed README text-to-speech examples to omit the optional model selector unless using a value returned by `models.list()`.
|
|
15
|
+
- Fixed the README clone preview example to load `sample.wav` directly instead of referencing an undefined `audioBytes` variable.
|
|
16
|
+
|
|
17
|
+
## 0.2.0
|
|
18
|
+
|
|
19
|
+
- Updated voice preview helpers to use the clearer `/v1/voice-previews/...` API paths by default while keeping the same `client.voices.*` method names.
|
|
20
|
+
- Updated voice edit and settings helpers to use REST-style `PATCH /v1/voices/{voiceId}` and `PATCH /v1/voices/{voiceId}/settings` paths by default.
|
|
21
|
+
|
|
22
|
+
## 0.1.3
|
|
23
|
+
|
|
24
|
+
- Breaking: collapsed `client.voices.ivc.*` and `client.textToVoice.*` into `client.voices.*` and renamed methods around Breeze's actual two-step preview → save flow. Replacements:
|
|
25
|
+
- `voices.ivc.createPreview` → `voices.createClonePreview`
|
|
26
|
+
- `voices.ivc.edit` → `voices.edit`
|
|
27
|
+
- `voices.ivc.settings` → `voices.getSettings`
|
|
28
|
+
- `voices.ivc.delete` → `voices.delete`
|
|
29
|
+
- `textToVoice.design` → `voices.createDesignPreview`
|
|
30
|
+
- `textToVoice.create` → `voices.savePreview`
|
|
31
|
+
- `textToVoice.streamPreview` → `voices.streamPreview`
|
|
32
|
+
- Removed the `client.textToVoice` namespace and the internal `voices.ivc` proxy. `voices.editSettings` is unchanged.
|
|
33
|
+
|
|
34
|
+
## 0.1.2
|
|
35
|
+
|
|
36
|
+
- Breaking: all request and response field names are now camelCase (e.g. `modelId`, `voiceId`, `voiceSettings`, `historyItemId`). The SDK transparently translates to the snake_case HTTP wire format on send and from snake_case on receive. `labels: Record<string, string>` is preserved verbatim.
|
|
37
|
+
- Removed remaining ElevenLabs-positioning copy from the README and package keywords. Quickstart and reference examples use the published packages only.
|
|
38
|
+
|
|
39
|
+
## 0.1.1
|
|
40
|
+
|
|
41
|
+
- Rewrote the npm package README for external developers.
|
|
42
|
+
- Removed internal monorepo release workflow notes from the packaged README.
|
|
43
|
+
- Clarified setup, TTS, voice, history, account, Node audio helper, and error handling examples.
|
|
44
|
+
- Removed JavaScript and declaration source maps from the published npm package.
|
|
45
|
+
|
|
46
|
+
## 0.1.0
|
|
47
|
+
|
|
48
|
+
Initial release.
|
|
49
|
+
|
|
50
|
+
- ElevenLabs-compatible client surface for text-to-speech, voices, text-to-voice, history, models, and account APIs.
|
|
51
|
+
- Typed errors for authentication, rate limiting, validation, billing, upstream, and service-unavailable failures.
|
|
52
|
+
- Node audio playback helpers through the `@breeze.blue/sdk/node` subpath.
|
|
53
|
+
- `xi-api-key` auth header and `x-breeze-sdk` telemetry header.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Breeze Blue
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# @breeze.blue/sdk
|
|
2
|
+
|
|
3
|
+
ESM-first TypeScript SDK for the Breeze Blue Developer API. Covers
|
|
4
|
+
text-to-speech, voice management, voice preview generation, history audio,
|
|
5
|
+
models, and account usage.
|
|
6
|
+
|
|
7
|
+
The root entrypoint is safe to import in Node, browser, and edge runtimes. Local
|
|
8
|
+
audio playback helpers live in the Node-only `@breeze.blue/sdk/node` subpath.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
pnpm add @breeze.blue/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
or:
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @breeze.blue/sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## API Key
|
|
23
|
+
|
|
24
|
+
Create an API key in the Breeze Blue Developer Console, then export it:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
export BREEZE_API_KEY=brz_...
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The SDK sends the key with the `xi-api-key` header. It also sends
|
|
31
|
+
`x-breeze-sdk` for server-side observability.
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { BreezeBlueClient } from "@breeze.blue/sdk";
|
|
37
|
+
import { save } from "@breeze.blue/sdk/node";
|
|
38
|
+
|
|
39
|
+
const client = new BreezeBlueClient();
|
|
40
|
+
|
|
41
|
+
const audio = await client.textToSpeech.convert(
|
|
42
|
+
"voc_...",
|
|
43
|
+
{ text: "Hello from Breeze Blue." },
|
|
44
|
+
{ outputFormat: "mp3" },
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
await save(audio, "hello.mp3");
|
|
48
|
+
console.log(audio.contentType);
|
|
49
|
+
console.log(audio.historyItemId);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`new BreezeBlueClient()` reads `BREEZE_API_KEY` by default and sends requests to
|
|
53
|
+
`https://api.breeze.blue`. To point at another environment, pass `baseUrl` or
|
|
54
|
+
set `BREEZE_BASE_URL`.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const client = new BreezeBlueClient({
|
|
58
|
+
apiKey: "brz_...",
|
|
59
|
+
baseUrl: "https://api.breeze.blue",
|
|
60
|
+
timeout: 120_000,
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Per-request options accept `timeout`, `signal`, and extra `headers`.
|
|
65
|
+
|
|
66
|
+
## Text to Speech
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
const audio = await client.textToSpeech.convert("voc_...", {
|
|
70
|
+
text: "Render this line.",
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const audioStream = await client.textToSpeech.stream("voc_...", {
|
|
74
|
+
text: "Stream this line.",
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Use async text-to-speech for long text, reference-heavy voices, or batch
|
|
79
|
+
production where the caller should not hold an HTTP connection open:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const job = await client.textToSpeech.createJob(
|
|
83
|
+
"voc_...",
|
|
84
|
+
{ text: "Render this longer script." },
|
|
85
|
+
{ outputFormat: "mp3" },
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const status = await client.generationJobs.get(job.generationJobId);
|
|
89
|
+
if (status.status === "ready") {
|
|
90
|
+
const audio = await client.generationJobs.downloadAudio(job.generationJobId);
|
|
91
|
+
await save(audio, "async.mp3");
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If the job is still active, `downloadAudio(...)` rejects with
|
|
96
|
+
`BreezeBlueGenerationNotReadyError`; read `error.retryAfter` before retrying.
|
|
97
|
+
|
|
98
|
+
The API uses the default text-to-speech model when `modelId` is omitted. If
|
|
99
|
+
you need to select a model explicitly, call `client.models.list()` and pass one
|
|
100
|
+
of the returned `modelId` values.
|
|
101
|
+
|
|
102
|
+
All field names follow TypeScript conventions (`modelId`, `voiceSettings`,
|
|
103
|
+
`outputFormat`, `historyItemId`, ...). The SDK translates them to the
|
|
104
|
+
snake_case HTTP wire format on send and translates JSON responses back to
|
|
105
|
+
camelCase on receive.
|
|
106
|
+
|
|
107
|
+
Audio responses expose convenient helpers:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
await audio.arrayBuffer();
|
|
111
|
+
await audio.bytes();
|
|
112
|
+
await audio.blob();
|
|
113
|
+
|
|
114
|
+
audio.contentType;
|
|
115
|
+
audio.historyItemId;
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Node playback helpers:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { play, save, stream } from "@breeze.blue/sdk/node";
|
|
122
|
+
|
|
123
|
+
await play(audio); // ffplay, with macOS afplay fallback
|
|
124
|
+
await save(audio, "x.mp3");
|
|
125
|
+
await stream(audioStream); // mpv
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Voices
|
|
129
|
+
|
|
130
|
+
Browse existing voices and inspect a single voice:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const voices = await client.voices.search({ search: "narrator" });
|
|
134
|
+
const firstVoiceId = voices.voices[0].voiceId;
|
|
135
|
+
|
|
136
|
+
const voice = await client.voices.get(firstVoiceId);
|
|
137
|
+
const settings = await client.voices.getSettings(firstVoiceId);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Breeze voice creation is always two steps: produce a **preview**, let the
|
|
141
|
+
user accept it, then **save the preview** as a real voice. Two ways to
|
|
142
|
+
produce a preview:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { readFile } from "node:fs/promises";
|
|
146
|
+
|
|
147
|
+
// Option A — clone preview from an audio sample
|
|
148
|
+
const clonePreview = await client.voices.createClonePreview({
|
|
149
|
+
name: "Demo voice",
|
|
150
|
+
file: {
|
|
151
|
+
data: await readFile("sample.wav"),
|
|
152
|
+
filename: "sample.wav",
|
|
153
|
+
contentType: "audio/wav",
|
|
154
|
+
},
|
|
155
|
+
text: "This is a short preview script.",
|
|
156
|
+
});
|
|
157
|
+
let generatedVoiceId = clonePreview.generatedVoiceId;
|
|
158
|
+
|
|
159
|
+
// Option B — design preview from a text description (no audio)
|
|
160
|
+
const design = await client.voices.createDesignPreview({
|
|
161
|
+
voiceDescription: "Warm documentary narrator with clear articulation.",
|
|
162
|
+
});
|
|
163
|
+
generatedVoiceId = design.previews[0].generatedVoiceId;
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Stream the preview so the user can audition it, then save the one they
|
|
167
|
+
pick:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const audio = await client.voices.streamPreview(generatedVoiceId);
|
|
171
|
+
await save(audio, "preview.mp3");
|
|
172
|
+
|
|
173
|
+
const saved = await client.voices.savePreview({
|
|
174
|
+
generatedVoiceId,
|
|
175
|
+
voiceName: "Documentary narrator",
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Edit, tune settings, or delete a saved voice:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
await client.voices.edit(saved.voiceId, { name: "Renamed narrator" });
|
|
183
|
+
await client.voices.editSettings(saved.voiceId, { guidanceScale: 1.2 });
|
|
184
|
+
await client.voices.delete(saved.voiceId);
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## History, Models, and Account
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
const models = await client.models.list();
|
|
191
|
+
const balance = await client.account.balance();
|
|
192
|
+
const usage = await client.account.usage({ days: 7 });
|
|
193
|
+
|
|
194
|
+
const history = await client.history.list({ pageSize: 10 });
|
|
195
|
+
const item = await client.history.get(history.history[0].historyItemId);
|
|
196
|
+
const audio = await client.history.downloadAudio(item.historyItemId);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Errors
|
|
200
|
+
|
|
201
|
+
API failures throw `BreezeBlueAPIError` subclasses:
|
|
202
|
+
|
|
203
|
+
- `BreezeBlueAuthenticationError`
|
|
204
|
+
- `BreezeBlueBadRequestError`
|
|
205
|
+
- `BreezeBlueConflictError`
|
|
206
|
+
- `BreezeBlueForbiddenError`
|
|
207
|
+
- `BreezeBlueNotFoundError`
|
|
208
|
+
- `BreezeBlueValidationError`
|
|
209
|
+
- `BreezeBlueRateLimitError`
|
|
210
|
+
- `BreezeBlueInsufficientCreditsError`
|
|
211
|
+
- `BreezeBlueUpstreamError` for upstream generation/storage/model failures,
|
|
212
|
+
commonly 502/504
|
|
213
|
+
- `BreezeBlueServiceUnavailableError` for capacity or service-configuration
|
|
214
|
+
failures, commonly 503
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { BreezeBlueClient, BreezeBlueRateLimitError } from "@breeze.blue/sdk";
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
await new BreezeBlueClient().models.list();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (error instanceof BreezeBlueRateLimitError) {
|
|
223
|
+
console.log(error.headers.get("retry-after"));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Each API error exposes `status`, `code`, `detail`, `meta`, and `headers`.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Keys whose values are user-defined dictionaries; we transform the key itself
|
|
2
|
+
// but never recurse into the value.
|
|
3
|
+
const OPAQUE_VALUE_KEYS = new Set(["labels"]);
|
|
4
|
+
function snakeToCamel(key) {
|
|
5
|
+
return key.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
6
|
+
}
|
|
7
|
+
function camelToSnake(key) {
|
|
8
|
+
return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
function isPlainObject(value) {
|
|
11
|
+
if (value === null || typeof value !== "object")
|
|
12
|
+
return false;
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return false;
|
|
15
|
+
const proto = Object.getPrototypeOf(value);
|
|
16
|
+
return proto === Object.prototype || proto === null;
|
|
17
|
+
}
|
|
18
|
+
function transform(value, keyFn, opaqueKey) {
|
|
19
|
+
if (Array.isArray(value)) {
|
|
20
|
+
return value.map((item) => transform(item, keyFn, false));
|
|
21
|
+
}
|
|
22
|
+
if (isPlainObject(value)) {
|
|
23
|
+
if (opaqueKey)
|
|
24
|
+
return value;
|
|
25
|
+
const out = {};
|
|
26
|
+
for (const [k, v] of Object.entries(value)) {
|
|
27
|
+
out[keyFn(k)] = transform(v, keyFn, OPAQUE_VALUE_KEYS.has(k));
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
export function camelizeKeys(value) {
|
|
34
|
+
return transform(value, snakeToCamel, false);
|
|
35
|
+
}
|
|
36
|
+
export function snakeizeKeys(value) {
|
|
37
|
+
return transform(value, camelToSnake, false);
|
|
38
|
+
}
|
package/dist/audio.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface CommandAvailability {
|
|
2
|
+
has(command: string): boolean;
|
|
3
|
+
}
|
|
4
|
+
export interface PlaybackCommand {
|
|
5
|
+
command: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
mode: "stdin" | "file";
|
|
8
|
+
}
|
|
9
|
+
export interface StreamPlaybackCommand {
|
|
10
|
+
command: string;
|
|
11
|
+
args: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface AudioResponseOptions {
|
|
14
|
+
filename?: string | null;
|
|
15
|
+
}
|
|
16
|
+
export interface PlayOptions {
|
|
17
|
+
command?: string;
|
|
18
|
+
args?: string[];
|
|
19
|
+
availability?: CommandAvailability;
|
|
20
|
+
platform?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface StreamOptions {
|
|
23
|
+
command?: string;
|
|
24
|
+
args?: string[];
|
|
25
|
+
}
|
|
26
|
+
export declare class AudioResponse {
|
|
27
|
+
#private;
|
|
28
|
+
readonly response: Response;
|
|
29
|
+
readonly contentType: string;
|
|
30
|
+
readonly historyItemId: string | null;
|
|
31
|
+
readonly filename: string | null;
|
|
32
|
+
constructor(response: Response, options?: AudioResponseOptions);
|
|
33
|
+
get headers(): Headers;
|
|
34
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
35
|
+
bytes(): Promise<Uint8Array>;
|
|
36
|
+
blob(): Promise<Blob>;
|
|
37
|
+
saveToFile(path: string): Promise<void>;
|
|
38
|
+
play(options?: PlayOptions): Promise<void>;
|
|
39
|
+
stream(options?: StreamOptions): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export declare function save(audio: AudioResponse, path: string): Promise<void>;
|
|
42
|
+
export declare function play(audio: AudioResponse, options?: PlayOptions): Promise<void>;
|
|
43
|
+
export declare function stream(audio: AudioResponse, options?: StreamOptions): Promise<void>;
|
|
44
|
+
export declare function selectAudioPlaybackCommand(params: {
|
|
45
|
+
availability: CommandAvailability;
|
|
46
|
+
platform: string;
|
|
47
|
+
}): PlaybackCommand;
|
|
48
|
+
export declare function selectStreamPlaybackCommand(): StreamPlaybackCommand;
|
|
49
|
+
export declare function extensionForContentType(contentType: string): string;
|
package/dist/audio.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export class AudioResponse {
|
|
2
|
+
response;
|
|
3
|
+
contentType;
|
|
4
|
+
historyItemId;
|
|
5
|
+
filename;
|
|
6
|
+
#buffer = null;
|
|
7
|
+
constructor(response, options = {}) {
|
|
8
|
+
this.response = response;
|
|
9
|
+
this.contentType = response.headers.get("content-type")?.split(";", 1)[0].trim() || "application/octet-stream";
|
|
10
|
+
this.historyItemId = response.headers.get("history-item-id");
|
|
11
|
+
this.filename = options.filename ?? filenameFromContentDisposition(response.headers.get("content-disposition"));
|
|
12
|
+
}
|
|
13
|
+
get headers() {
|
|
14
|
+
return this.response.headers;
|
|
15
|
+
}
|
|
16
|
+
async arrayBuffer() {
|
|
17
|
+
if (this.#buffer === null) {
|
|
18
|
+
this.#buffer = await this.response.arrayBuffer();
|
|
19
|
+
}
|
|
20
|
+
return this.#buffer.slice(0);
|
|
21
|
+
}
|
|
22
|
+
async bytes() {
|
|
23
|
+
return new Uint8Array(await this.arrayBuffer());
|
|
24
|
+
}
|
|
25
|
+
async blob() {
|
|
26
|
+
return new Blob([await this.arrayBuffer()], { type: this.contentType });
|
|
27
|
+
}
|
|
28
|
+
async saveToFile(path) {
|
|
29
|
+
throw nodeHelperError(`save audio to ${path}`);
|
|
30
|
+
}
|
|
31
|
+
async play(options = {}) {
|
|
32
|
+
void options;
|
|
33
|
+
throw nodeHelperError("play audio");
|
|
34
|
+
}
|
|
35
|
+
async stream(options = {}) {
|
|
36
|
+
void options;
|
|
37
|
+
throw nodeHelperError("stream audio");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function save(audio, path) {
|
|
41
|
+
return audio.saveToFile(path);
|
|
42
|
+
}
|
|
43
|
+
export function play(audio, options = {}) {
|
|
44
|
+
return audio.play(options);
|
|
45
|
+
}
|
|
46
|
+
export function stream(audio, options = {}) {
|
|
47
|
+
return audio.stream(options);
|
|
48
|
+
}
|
|
49
|
+
export function selectAudioPlaybackCommand(params) {
|
|
50
|
+
if (params.availability.has("ffplay")) {
|
|
51
|
+
return { command: "ffplay", args: ["-autoexit", "-nodisp", "-"], mode: "stdin" };
|
|
52
|
+
}
|
|
53
|
+
if (params.platform === "darwin" && params.availability.has("afplay")) {
|
|
54
|
+
return { command: "afplay", args: [], mode: "file" };
|
|
55
|
+
}
|
|
56
|
+
throw new Error("No supported audio player found. Install ffplay, or use afplay on macOS.");
|
|
57
|
+
}
|
|
58
|
+
export function selectStreamPlaybackCommand() {
|
|
59
|
+
return { command: "mpv", args: ["--no-terminal", "--really-quiet", "-"] };
|
|
60
|
+
}
|
|
61
|
+
function nodeHelperError(action) {
|
|
62
|
+
return new Error(`Use @breeze.blue/sdk/node to ${action} in Node.js runtimes.`);
|
|
63
|
+
}
|
|
64
|
+
function filenameFromContentDisposition(value) {
|
|
65
|
+
if (!value)
|
|
66
|
+
return null;
|
|
67
|
+
const match = /filename="?([^";]+)"?/i.exec(value);
|
|
68
|
+
return match?.[1] ?? null;
|
|
69
|
+
}
|
|
70
|
+
export function extensionForContentType(contentType) {
|
|
71
|
+
if (contentType.includes("mpeg"))
|
|
72
|
+
return ".mp3";
|
|
73
|
+
if (contentType.includes("wav"))
|
|
74
|
+
return ".wav";
|
|
75
|
+
if (contentType.includes("flac"))
|
|
76
|
+
return ".flac";
|
|
77
|
+
if (contentType.includes("aac"))
|
|
78
|
+
return ".aac";
|
|
79
|
+
if (contentType.includes("opus"))
|
|
80
|
+
return ".opus";
|
|
81
|
+
return ".bin";
|
|
82
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { AudioResponse } from "./audio.js";
|
|
2
|
+
import type { AudioRequestOptions, AsyncTextToSpeechJob, Balance, BreezeBlueClientOptions, GenericStatus, GenerationJob, HistoryItem, HistoryList, HistoryListParams, Model, RequestOptions, SaveVoiceRequest, StreamTextToSpeechOptions, TextToSpeechRequest, Usage, UsageParams, Voice, VoiceClonePreview, VoiceClonePreviewRequest, VoiceDesignRequest, VoiceDesignResponse, VoiceEditRequest, VoiceList, VoiceSearchParams, VoiceSettings } from "./types.js";
|
|
3
|
+
export declare class BreezeBlueClient {
|
|
4
|
+
readonly apiKey: string;
|
|
5
|
+
readonly baseUrl: string;
|
|
6
|
+
readonly fetch: typeof fetch;
|
|
7
|
+
readonly headers: HeadersInit | undefined;
|
|
8
|
+
readonly timeout: number | undefined;
|
|
9
|
+
readonly textToSpeech: TextToSpeechResource;
|
|
10
|
+
readonly generationJobs: GenerationJobsResource;
|
|
11
|
+
readonly voices: VoicesResource;
|
|
12
|
+
readonly history: HistoryResource;
|
|
13
|
+
readonly models: ModelsResource;
|
|
14
|
+
readonly account: AccountResource;
|
|
15
|
+
constructor(options?: BreezeBlueClientOptions);
|
|
16
|
+
requestJson<T>(method: string, path: string, params?: object, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
17
|
+
requestAudio(method: string, path: string, params?: object, body?: unknown, options?: RequestOptions): Promise<AudioResponse>;
|
|
18
|
+
request(method: string, path: string, params?: object, body?: unknown, options?: RequestOptions): Promise<Response>;
|
|
19
|
+
private buildHeaders;
|
|
20
|
+
}
|
|
21
|
+
declare class TextToSpeechResource {
|
|
22
|
+
private readonly client;
|
|
23
|
+
constructor(client: BreezeBlueClient);
|
|
24
|
+
convert(voiceId: string, request: TextToSpeechRequest, options?: AudioRequestOptions): Promise<AudioResponse>;
|
|
25
|
+
createJob(voiceId: string, request: TextToSpeechRequest, options?: AudioRequestOptions): Promise<AsyncTextToSpeechJob>;
|
|
26
|
+
stream(voiceId: string, request: TextToSpeechRequest, options?: StreamTextToSpeechOptions): Promise<AudioResponse>;
|
|
27
|
+
}
|
|
28
|
+
declare class GenerationJobsResource {
|
|
29
|
+
private readonly client;
|
|
30
|
+
constructor(client: BreezeBlueClient);
|
|
31
|
+
get(generationJobId: string, options?: RequestOptions): Promise<GenerationJob>;
|
|
32
|
+
downloadAudio(generationJobId: string, options?: RequestOptions): Promise<AudioResponse>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Voice CRUD plus the two-step preview → save flow for Breeze voices.
|
|
36
|
+
*
|
|
37
|
+
* Breeze's voice creation is always two steps: produce a preview
|
|
38
|
+
* (`createClonePreview` from an audio sample, or `createDesignPreview` from
|
|
39
|
+
* a text description), then persist it with `savePreview`. Preview generation
|
|
40
|
+
* never creates a saved voice until `savePreview` is called.
|
|
41
|
+
*/
|
|
42
|
+
declare class VoicesResource {
|
|
43
|
+
private readonly client;
|
|
44
|
+
constructor(client: BreezeBlueClient);
|
|
45
|
+
search(params?: VoiceSearchParams, options?: RequestOptions): Promise<VoiceList>;
|
|
46
|
+
get(voiceId: string, options?: RequestOptions): Promise<Voice>;
|
|
47
|
+
edit(voiceId: string, request: VoiceEditRequest, options?: RequestOptions): Promise<GenericStatus>;
|
|
48
|
+
delete(voiceId: string, options?: RequestOptions): Promise<GenericStatus>;
|
|
49
|
+
getSettings(voiceId: string, options?: RequestOptions): Promise<Required<VoiceSettings>>;
|
|
50
|
+
editSettings(voiceId: string, settings: VoiceSettings, options?: RequestOptions): Promise<Required<VoiceSettings>>;
|
|
51
|
+
/**
|
|
52
|
+
* Generate a clone preview from a single audio sample. Returns a
|
|
53
|
+
* `generatedVoiceId`; call {@link savePreview} after the user accepts
|
|
54
|
+
* the preview to persist it as a real voice.
|
|
55
|
+
*/
|
|
56
|
+
createClonePreview(request: VoiceClonePreviewRequest, options?: RequestOptions): Promise<VoiceClonePreview>;
|
|
57
|
+
/**
|
|
58
|
+
* Design preview voices from a text prompt (no audio sample). Returns one
|
|
59
|
+
* or more previews; pass the chosen `generatedVoiceId` to
|
|
60
|
+
* {@link savePreview}.
|
|
61
|
+
*/
|
|
62
|
+
createDesignPreview(request: VoiceDesignRequest, options?: AudioRequestOptions): Promise<VoiceDesignResponse>;
|
|
63
|
+
/** Stream the audio of a clone or design preview before saving. */
|
|
64
|
+
streamPreview(generatedVoiceId: string, options?: AudioRequestOptions): Promise<AudioResponse>;
|
|
65
|
+
/** Persist a clone or design preview as a real voice asset. */
|
|
66
|
+
savePreview(request: SaveVoiceRequest, options?: RequestOptions): Promise<Voice>;
|
|
67
|
+
}
|
|
68
|
+
declare class HistoryResource {
|
|
69
|
+
private readonly client;
|
|
70
|
+
constructor(client: BreezeBlueClient);
|
|
71
|
+
list(params?: HistoryListParams, options?: RequestOptions): Promise<HistoryList>;
|
|
72
|
+
get(historyItemId: string, options?: RequestOptions): Promise<HistoryItem>;
|
|
73
|
+
downloadAudio(historyItemId: string, options?: RequestOptions): Promise<AudioResponse>;
|
|
74
|
+
delete(historyItemId: string, options?: RequestOptions): Promise<GenericStatus>;
|
|
75
|
+
}
|
|
76
|
+
declare class ModelsResource {
|
|
77
|
+
private readonly client;
|
|
78
|
+
constructor(client: BreezeBlueClient);
|
|
79
|
+
list(options?: RequestOptions): Promise<Model[]>;
|
|
80
|
+
}
|
|
81
|
+
declare class AccountResource {
|
|
82
|
+
private readonly client;
|
|
83
|
+
constructor(client: BreezeBlueClient);
|
|
84
|
+
balance(options?: RequestOptions): Promise<Balance>;
|
|
85
|
+
usage(params?: UsageParams, options?: RequestOptions): Promise<Usage>;
|
|
86
|
+
}
|
|
87
|
+
export {};
|