@vocalstack/js-sdk 1.1.0 → 2.0.0-alpha.1
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/LICENSE +21 -21
- package/README.md +112 -65
- package/SKILL.md +249 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +56 -0
- package/dist/file.d.ts +2 -0
- package/dist/file.js +38 -0
- package/dist/http.d.ts +43 -0
- package/dist/http.js +124 -0
- package/dist/index.d.ts +42 -2
- package/dist/index.js +51 -17
- package/dist/mic.d.ts +27 -0
- package/dist/mic.js +236 -0
- package/dist/realtime.d.ts +77 -0
- package/dist/realtime.js +490 -0
- package/dist/resources.d.ts +374 -0
- package/dist/resources.js +712 -0
- package/dist/subscription.d.ts +38 -0
- package/dist/subscription.js +153 -0
- package/dist/ws.d.ts +9 -0
- package/dist/ws.js +12 -0
- package/package.json +32 -36
- package/skills/vocalstack/SKILL.md +249 -0
- package/dist/ApiVersion.d.ts +0 -2
- package/dist/ApiVersion.js +0 -32
- package/dist/RestApi.d.ts +0 -21
- package/dist/RestApi.js +0 -95
- package/dist/VocalStackApi.d.ts +0 -19
- package/dist/VocalStackApi.js +0 -60
- package/dist/WebsocketApi.d.ts +0 -38
- package/dist/WebsocketApi.js +0 -150
- package/dist/apis/Billing.d.ts +0 -59
- package/dist/apis/Billing.js +0 -21
- package/dist/apis/LiveTranscription.d.ts +0 -58
- package/dist/apis/LiveTranscription.js +0 -55
- package/dist/apis/Polyglot.d.ts +0 -113
- package/dist/apis/Polyglot.js +0 -68
- package/dist/apis/Security.d.ts +0 -25
- package/dist/apis/Security.js +0 -35
- package/dist/apis/Transcriptions.d.ts +0 -60
- package/dist/apis/Transcriptions.js +0 -38
- package/dist/apis/UrlTranscription.d.ts +0 -59
- package/dist/apis/UrlTranscription.js +0 -53
- package/dist/apis/index.d.ts +0 -6
- package/dist/apis/index.js +0 -22
- package/dist/esm/ApiVersion.d.ts +0 -2
- package/dist/esm/ApiVersion.js +0 -28
- package/dist/esm/RestApi.d.ts +0 -21
- package/dist/esm/RestApi.js +0 -91
- package/dist/esm/VocalStackApi.d.ts +0 -19
- package/dist/esm/VocalStackApi.js +0 -56
- package/dist/esm/WebsocketApi.d.ts +0 -38
- package/dist/esm/WebsocketApi.js +0 -146
- package/dist/esm/apis/Billing.d.ts +0 -59
- package/dist/esm/apis/Billing.js +0 -17
- package/dist/esm/apis/LiveTranscription.d.ts +0 -58
- package/dist/esm/apis/LiveTranscription.js +0 -51
- package/dist/esm/apis/Polyglot.d.ts +0 -113
- package/dist/esm/apis/Polyglot.js +0 -64
- package/dist/esm/apis/Security.d.ts +0 -25
- package/dist/esm/apis/Security.js +0 -31
- package/dist/esm/apis/Transcriptions.d.ts +0 -60
- package/dist/esm/apis/Transcriptions.js +0 -34
- package/dist/esm/apis/UrlTranscription.d.ts +0 -59
- package/dist/esm/apis/UrlTranscription.js +0 -49
- package/dist/esm/apis/index.d.ts +0 -6
- package/dist/esm/apis/index.js +0 -6
- package/dist/esm/generics.zod.d.ts +0 -83
- package/dist/esm/generics.zod.js +0 -47
- package/dist/esm/index.d.ts +0 -2
- package/dist/esm/index.js +0 -2
- package/dist/generics.zod.d.ts +0 -83
- package/dist/generics.zod.js +0 -50
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
import { VocalStackError } from './errors.js';
|
|
2
|
+
import { listenerQuery, seg } from './http.js';
|
|
3
|
+
import { RealtimeSession } from './realtime.js';
|
|
4
|
+
import { Subscription } from './subscription.js';
|
|
5
|
+
import { openWebSocket } from './ws.js';
|
|
6
|
+
async function toBlob(file) {
|
|
7
|
+
if (typeof file === 'string') {
|
|
8
|
+
const fsSpecifier = 'node:' + 'fs';
|
|
9
|
+
const { openAsBlob } = (await import(/* @vite-ignore */ fsSpecifier));
|
|
10
|
+
return { blob: await openAsBlob(file), name: file.split(/[\\/]/).pop() };
|
|
11
|
+
}
|
|
12
|
+
if (typeof Blob !== 'undefined' && file instanceof Blob) {
|
|
13
|
+
return { blob: file, name: file.name || undefined };
|
|
14
|
+
}
|
|
15
|
+
return { blob: new Blob([file]) };
|
|
16
|
+
}
|
|
17
|
+
async function allPages(page) {
|
|
18
|
+
const out = [];
|
|
19
|
+
let cursor;
|
|
20
|
+
for (;;) {
|
|
21
|
+
const r = await page(cursor);
|
|
22
|
+
out.push(...(r.items ?? []));
|
|
23
|
+
if (!r.nextCursor)
|
|
24
|
+
return out;
|
|
25
|
+
cursor = r.nextCursor;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
class ParentResource {
|
|
29
|
+
t;
|
|
30
|
+
constructor(t) {
|
|
31
|
+
this.t = t;
|
|
32
|
+
}
|
|
33
|
+
p(id, rest = '') {
|
|
34
|
+
return `${this.base}/${seg(id)}${rest}`;
|
|
35
|
+
}
|
|
36
|
+
list(lifecycle = 'active') {
|
|
37
|
+
return allPages(async (cursor) => {
|
|
38
|
+
const r = await this.t.json('GET', this.base, { query: { lifecycle, cursor } });
|
|
39
|
+
return { items: r[this.key], nextCursor: r.nextCursor };
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
get(id) {
|
|
43
|
+
return this.t.json('GET', this.p(id));
|
|
44
|
+
}
|
|
45
|
+
update(id, req) {
|
|
46
|
+
return this.t.json('PATCH', this.p(id), { body: req });
|
|
47
|
+
}
|
|
48
|
+
archive(id) {
|
|
49
|
+
return this.t.json('POST', this.p(id, '/archive'));
|
|
50
|
+
}
|
|
51
|
+
restore(id) {
|
|
52
|
+
return this.t.json('POST', this.p(id, '/restore'));
|
|
53
|
+
}
|
|
54
|
+
async purge(id) {
|
|
55
|
+
await this.t.json('POST', this.p(id, '/purge'));
|
|
56
|
+
}
|
|
57
|
+
archiveMany(ids) {
|
|
58
|
+
return this.t.json('POST', `${this.base}/archive`, { body: { ids } });
|
|
59
|
+
}
|
|
60
|
+
restoreMany(ids) {
|
|
61
|
+
return this.t.json('POST', `${this.base}/restore`, { body: { ids } });
|
|
62
|
+
}
|
|
63
|
+
purgeMany(ids) {
|
|
64
|
+
return this.t.json('POST', `${this.base}/purge`, { body: { ids } });
|
|
65
|
+
}
|
|
66
|
+
listRecordings(id, lifecycle = 'active') {
|
|
67
|
+
return allPages(async (cursor) => {
|
|
68
|
+
const r = await this.listRecordingsPage(id, { lifecycle, cursor });
|
|
69
|
+
return { items: r.recordings, nextCursor: r.nextCursor };
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
listRecordingsPage(id, opts = {}) {
|
|
73
|
+
return this.t.json('GET', this.p(id, '/recordings'), { query: { lifecycle: opts.lifecycle, cursor: opts.cursor, limit: opts.limit } });
|
|
74
|
+
}
|
|
75
|
+
startRecording(id, req = {}, call = {}) {
|
|
76
|
+
return this.t.json('POST', this.p(id, '/recordings'), { body: req, ...call });
|
|
77
|
+
}
|
|
78
|
+
async listShareLinks(id) {
|
|
79
|
+
const r = await this.t.json('GET', this.p(id, '/share-links'));
|
|
80
|
+
return r.links ?? [];
|
|
81
|
+
}
|
|
82
|
+
createShareLink(id, req) {
|
|
83
|
+
return this.t.json('POST', this.p(id, '/share-links'), { body: req });
|
|
84
|
+
}
|
|
85
|
+
async events(id, as = {}) {
|
|
86
|
+
const token = as.asOwner === false ? undefined : await this.t.bearer();
|
|
87
|
+
return openWebSocket(this.t.wsUrl(this.p(id, '/events'), listenerQuery(as)), token);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export class Broadcasts extends ParentResource {
|
|
91
|
+
base = '/v1/broadcasts';
|
|
92
|
+
key = 'broadcasts';
|
|
93
|
+
create(req, call = {}) {
|
|
94
|
+
return this.t.json('POST', this.base, { body: req, ...call });
|
|
95
|
+
}
|
|
96
|
+
addAudioSource(id, req) {
|
|
97
|
+
return this.t.json('POST', this.p(id, '/audio-sources'), { body: req });
|
|
98
|
+
}
|
|
99
|
+
removeAudioSource(id, sourceId) {
|
|
100
|
+
return this.t.json('DELETE', this.p(id, `/audio-sources/${seg(sourceId)}`));
|
|
101
|
+
}
|
|
102
|
+
checkAudioSource(id, sourceId, opts = {}) {
|
|
103
|
+
return this.t.json('GET', this.p(id, `/audio-sources/${seg(sourceId)}/check`), { query: { fresh: opts.fresh || undefined } });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
export class Transcriptions extends ParentResource {
|
|
107
|
+
base = '/v1/transcriptions';
|
|
108
|
+
key = 'transcriptions';
|
|
109
|
+
listPage(opts = {}) {
|
|
110
|
+
return this.t.json('GET', this.base, { query: { lifecycle: opts.lifecycle, cursor: opts.cursor, limit: opts.limit } });
|
|
111
|
+
}
|
|
112
|
+
uploadRecording(id, req = {}, call = {}) {
|
|
113
|
+
return this.t.json('POST', this.p(id, '/recordings/upload'), { body: req, ...call });
|
|
114
|
+
}
|
|
115
|
+
async create(req, call = {}) {
|
|
116
|
+
const { file, fileName, ...options } = req;
|
|
117
|
+
if (file === undefined)
|
|
118
|
+
return this.t.json('POST', this.base, { body: options, ...call });
|
|
119
|
+
const { blob, name } = await toBlob(file);
|
|
120
|
+
const fd = new FormData();
|
|
121
|
+
fd.append('options', JSON.stringify(options));
|
|
122
|
+
fd.append('file', blob, fileName ?? name ?? 'upload');
|
|
123
|
+
return this.t.json('POST', this.base, { body: fd, ...call });
|
|
124
|
+
}
|
|
125
|
+
createFromUrl(url, opts = {}, call = {}) {
|
|
126
|
+
return this.create({ ...opts, url }, call);
|
|
127
|
+
}
|
|
128
|
+
createLive(req, call = {}) {
|
|
129
|
+
return this.t.json('POST', `${this.base}/live`, { body: req, ...call });
|
|
130
|
+
}
|
|
131
|
+
probe(url) {
|
|
132
|
+
return this.t.json('GET', `${this.base}/probe`, { query: { url } });
|
|
133
|
+
}
|
|
134
|
+
attachBroadcast(id, broadcastId) {
|
|
135
|
+
return this.t.json('PUT', this.p(id, `/broadcasts/${seg(broadcastId)}`));
|
|
136
|
+
}
|
|
137
|
+
detachBroadcast(id, broadcastId) {
|
|
138
|
+
return this.t.json('DELETE', this.p(id, `/broadcasts/${seg(broadcastId)}`));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export class Batches {
|
|
142
|
+
#t;
|
|
143
|
+
constructor(t) {
|
|
144
|
+
this.#t = t;
|
|
145
|
+
}
|
|
146
|
+
#p(id, rest = '') {
|
|
147
|
+
return `/v1/batches/${seg(id)}${rest}`;
|
|
148
|
+
}
|
|
149
|
+
create(req = {}, call = {}) {
|
|
150
|
+
return this.#t.json('POST', '/v1/batches', { body: req, ...call });
|
|
151
|
+
}
|
|
152
|
+
list() {
|
|
153
|
+
return allPages(async (cursor) => {
|
|
154
|
+
const r = await this.listPage({ cursor });
|
|
155
|
+
return { items: r.batches, nextCursor: r.nextCursor };
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
listPage(opts = {}) {
|
|
159
|
+
return this.#t.json('GET', '/v1/batches', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
160
|
+
}
|
|
161
|
+
get(id) {
|
|
162
|
+
return this.#t.json('GET', this.#p(id));
|
|
163
|
+
}
|
|
164
|
+
async delete(id) {
|
|
165
|
+
await this.#t.json('DELETE', this.#p(id));
|
|
166
|
+
}
|
|
167
|
+
async addFile(id, req, call = {}) {
|
|
168
|
+
const { file, fileName, ...options } = req;
|
|
169
|
+
if (file === undefined)
|
|
170
|
+
return this.#t.json('POST', this.#p(id, '/files'), { body: options, ...call });
|
|
171
|
+
const { blob, name } = await toBlob(file);
|
|
172
|
+
const fd = new FormData();
|
|
173
|
+
fd.append('options', JSON.stringify(options));
|
|
174
|
+
fd.append('file', blob, fileName ?? name ?? 'upload');
|
|
175
|
+
return this.#t.json('POST', this.#p(id, '/files'), { body: fd, ...call });
|
|
176
|
+
}
|
|
177
|
+
addUrl(id, url, opts = {}, call = {}) {
|
|
178
|
+
return this.addFile(id, { ...opts, url }, call);
|
|
179
|
+
}
|
|
180
|
+
start(id, call = {}) {
|
|
181
|
+
return this.#t.json('POST', this.#p(id, '/start'), call);
|
|
182
|
+
}
|
|
183
|
+
export(id, format) {
|
|
184
|
+
return this.#t.raw('GET', this.#p(id, '/export'), { headers: { accept: '*/*' }, query: { format } });
|
|
185
|
+
}
|
|
186
|
+
stop(id) {
|
|
187
|
+
return this.#t.json('POST', this.#p(id, '/stop'));
|
|
188
|
+
}
|
|
189
|
+
async wait(id, opts = {}) {
|
|
190
|
+
const interval = opts.intervalMs ?? 5000;
|
|
191
|
+
const deadline = opts.timeoutMs ? Date.now() + opts.timeoutMs : Infinity;
|
|
192
|
+
for (;;) {
|
|
193
|
+
const batch = await this.get(id);
|
|
194
|
+
opts.onProgress?.(batch);
|
|
195
|
+
if ((batch.state === 'done' || batch.state === 'stopped') && batch.finishedAt)
|
|
196
|
+
return batch;
|
|
197
|
+
if (batch.state === 'open')
|
|
198
|
+
throw new VocalStackError('invalid_state', 'the batch is open: start it before waiting for it');
|
|
199
|
+
if (Date.now() + interval > deadline)
|
|
200
|
+
throw new VocalStackError('timeout', `batch still ${batch.state} after ${opts.timeoutMs} ms`, { retryable: true });
|
|
201
|
+
if (opts.signal?.aborted)
|
|
202
|
+
throw opts.signal.reason ?? new Error('aborted');
|
|
203
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export class Recordings {
|
|
208
|
+
#t;
|
|
209
|
+
constructor(t) {
|
|
210
|
+
this.#t = t;
|
|
211
|
+
}
|
|
212
|
+
#p(id, rest = '') {
|
|
213
|
+
return `/v1/recordings/${seg(id)}${rest}`;
|
|
214
|
+
}
|
|
215
|
+
get(id, as) {
|
|
216
|
+
return this.#t.json('GET', this.#p(id), { query: listenerQuery(as) });
|
|
217
|
+
}
|
|
218
|
+
pause(id) {
|
|
219
|
+
return this.#t.json('POST', this.#p(id, '/pause'));
|
|
220
|
+
}
|
|
221
|
+
resume(id) {
|
|
222
|
+
return this.#t.json('POST', this.#p(id, '/resume'));
|
|
223
|
+
}
|
|
224
|
+
stop(id, reason) {
|
|
225
|
+
return this.#t.json('POST', this.#p(id, '/stop'), { body: { reason } });
|
|
226
|
+
}
|
|
227
|
+
settings(id, patch) {
|
|
228
|
+
return this.#t.json('PATCH', this.#p(id, '/settings'), { body: patch });
|
|
229
|
+
}
|
|
230
|
+
publishToken(id, opts = {}) {
|
|
231
|
+
return this.#t.json('POST', this.#p(id, '/publish-token'), { body: { takeover: opts.takeover ?? false } });
|
|
232
|
+
}
|
|
233
|
+
publish(grant, opts = {}) {
|
|
234
|
+
if (!grant.streamUrl)
|
|
235
|
+
throw new VocalStackError('invalid_state', 'this grant has no streamUrl (an upload recording has no publisher socket)');
|
|
236
|
+
return RealtimeSession.connect({
|
|
237
|
+
sessionId: grant.sessionId ?? grant.recording.id,
|
|
238
|
+
streamUrl: grant.streamUrl,
|
|
239
|
+
readyDeadlineMs: opts.readyDeadlineMs ?? this.#t.opts.readyDeadlineMs,
|
|
240
|
+
onLoading: opts.onLoading,
|
|
241
|
+
onFrame: opts.onFrame,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
archive(id) {
|
|
245
|
+
return this.#t.json('POST', this.#p(id, '/archive'));
|
|
246
|
+
}
|
|
247
|
+
restore(id) {
|
|
248
|
+
return this.#t.json('POST', this.#p(id, '/restore'));
|
|
249
|
+
}
|
|
250
|
+
async purge(id) {
|
|
251
|
+
await this.#t.json('POST', this.#p(id, '/purge'));
|
|
252
|
+
}
|
|
253
|
+
archiveMany(ids) {
|
|
254
|
+
return this.#t.json('POST', '/v1/recordings/archive', { body: { ids } });
|
|
255
|
+
}
|
|
256
|
+
restoreMany(ids) {
|
|
257
|
+
return this.#t.json('POST', '/v1/recordings/restore', { body: { ids } });
|
|
258
|
+
}
|
|
259
|
+
purgeMany(ids) {
|
|
260
|
+
return this.#t.json('POST', '/v1/recordings/purge', { body: { ids } });
|
|
261
|
+
}
|
|
262
|
+
transcript(id, opts = {}) {
|
|
263
|
+
return this.#t.json('GET', this.#p(id, '/transcript'), {
|
|
264
|
+
query: { ...listenerQuery(opts), after: opts.after, lang: opts.lang, role: opts.asListener ? 'listener' : undefined },
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
translation(id, lang, sentences = [], as) {
|
|
268
|
+
return this.#t.json('GET', this.#p(id, '/translation'), {
|
|
269
|
+
query: { ...listenerQuery(as), lang, sentences: sentences.length ? sentences.join(',') : undefined },
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
summarize(id, call = {}) {
|
|
273
|
+
return this.#t.json('POST', this.#p(id, '/summarize'), call);
|
|
274
|
+
}
|
|
275
|
+
summary(id, opts = {}) {
|
|
276
|
+
return this.#t.json('GET', this.#p(id, '/summary'), { query: { ...listenerQuery(opts), lang: opts.lang } });
|
|
277
|
+
}
|
|
278
|
+
export(id, format, opts = {}) {
|
|
279
|
+
return this.#t.raw('GET', this.#p(id, '/export'), { headers: { accept: '*/*' }, query: { ...listenerQuery(opts), format, lang: opts.lang } });
|
|
280
|
+
}
|
|
281
|
+
async exportUrl(id, format, opts = {}) {
|
|
282
|
+
const token = opts.shareCode || opts.listenerToken ? undefined : await this.#t.bearer();
|
|
283
|
+
return this.#t.url(this.#p(id, '/export'), { format, lang: opts.lang, shareCode: opts.shareCode, listenerToken: opts.listenerToken, access_token: token });
|
|
284
|
+
}
|
|
285
|
+
async audioUrl(id, opts = {}) {
|
|
286
|
+
const res = await this.#t.raw('GET', this.#p(id, '/audio'), {
|
|
287
|
+
query: { ...listenerQuery(opts), kind: opts.kind, lang: opts.lang, format: opts.format },
|
|
288
|
+
});
|
|
289
|
+
const location = res.headers.get('location');
|
|
290
|
+
if (!location)
|
|
291
|
+
throw new VocalStackError('not_found', 'no signed URL in the redirect');
|
|
292
|
+
return location;
|
|
293
|
+
}
|
|
294
|
+
audioSrc(id, opts = {}) {
|
|
295
|
+
return this.#t.url(this.#p(id, '/audio'), { kind: opts.kind ?? 'delivery', lang: opts.lang, format: opts.format, voice: opts.voice, ...mediaQuery(opts) });
|
|
296
|
+
}
|
|
297
|
+
clipSrc(id, startS, endS, opts = {}) {
|
|
298
|
+
return this.#t.url(this.#p(id, '/audio/clip'), { startS: startS.toFixed(2), endS: endS.toFixed(2), clean: opts.clean ? 'true' : undefined, ...mediaQuery(opts) });
|
|
299
|
+
}
|
|
300
|
+
voiceClipSrc(id, sentence, lang, opts = {}) {
|
|
301
|
+
return this.#t.url(this.#p(id, `/voice/${seg(sentence)}`), {
|
|
302
|
+
lang,
|
|
303
|
+
voice: opts.voice,
|
|
304
|
+
format: opts.format,
|
|
305
|
+
part: opts.part ? opts.part : undefined,
|
|
306
|
+
v: opts.v,
|
|
307
|
+
...mediaQuery(opts),
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
clip(id, startS, endS, as) {
|
|
311
|
+
return this.#t.raw('GET', this.#p(id, '/audio/clip'), { headers: { accept: '*/*' }, query: { ...listenerQuery(as), startS, endS } });
|
|
312
|
+
}
|
|
313
|
+
voice(id, lang, as) {
|
|
314
|
+
return this.#t.json('GET', this.#p(id, '/voice'), { query: { ...listenerQuery(as), lang } });
|
|
315
|
+
}
|
|
316
|
+
renderVoice(id, req, call = {}) {
|
|
317
|
+
const r = Array.isArray(req) ? { langs: req } : req;
|
|
318
|
+
return this.#t.json('POST', this.#p(id, '/voice'), {
|
|
319
|
+
query: listenerQuery(r),
|
|
320
|
+
body: { langs: r.langs, voice: r.voice, voiceBySpeaker: r.voiceBySpeaker, near: r.near },
|
|
321
|
+
...call,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
voiceClip(id, sentence, lang, opts = {}) {
|
|
325
|
+
return this.#t.raw('GET', this.#p(id, `/voice/${seg(sentence)}`), { headers: { accept: '*/*' }, query: { ...listenerQuery(opts), lang, format: opts.format } });
|
|
326
|
+
}
|
|
327
|
+
mediaToken(id) {
|
|
328
|
+
return this.#t.json('GET', this.#p(id, '/media-token'));
|
|
329
|
+
}
|
|
330
|
+
async nameSpeaker(id, speaker, name) {
|
|
331
|
+
await this.#t.json('PUT', this.#p(id, `/speakers/${seg(speaker)}`), { body: { name } });
|
|
332
|
+
}
|
|
333
|
+
events(id, opts = {}) {
|
|
334
|
+
return allPages(async (cursor) => {
|
|
335
|
+
const r = await this.eventsPage(id, cursor ? { cursor, limit: opts.limit } : { after: opts.after, limit: opts.limit });
|
|
336
|
+
return { items: r.events, nextCursor: r.nextCursor };
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
eventsPage(id, opts = {}) {
|
|
340
|
+
return this.#t.json('GET', this.#p(id, '/events'), { query: { cursor: opts.cursor, after: opts.after, limit: opts.limit } });
|
|
341
|
+
}
|
|
342
|
+
async subscribe(id, opts = {}) {
|
|
343
|
+
const token = opts.asOwner === false ? undefined : await this.#t.bearer();
|
|
344
|
+
const url = this.#t.wsUrl(this.#p(id, '/subscribe'), {
|
|
345
|
+
lang: opts.lang,
|
|
346
|
+
voice: opts.voice,
|
|
347
|
+
...listenerQuery(opts),
|
|
348
|
+
role: opts.asOwner === false ? 'listener' : undefined,
|
|
349
|
+
device: this.#t.opts.deviceId,
|
|
350
|
+
});
|
|
351
|
+
return Subscription.open(id, url, token, opts, (after) => this.transcript(id, {
|
|
352
|
+
after,
|
|
353
|
+
lang: opts.lang,
|
|
354
|
+
listenerToken: opts.listenerToken,
|
|
355
|
+
shareCode: opts.shareCode,
|
|
356
|
+
asListener: opts.asOwner === false,
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
function mediaQuery(a) {
|
|
361
|
+
return { media: a.media, access_token: a.media ? undefined : a.token, listenerToken: a.listenerToken, shareCode: a.shareCode };
|
|
362
|
+
}
|
|
363
|
+
export function playableFormat() {
|
|
364
|
+
try {
|
|
365
|
+
const doc = globalThis.document;
|
|
366
|
+
return doc?.createElement('audio').canPlayType?.('audio/x-caf; codecs=opus') === 'probably' ? 'caf' : 'ogg';
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
return 'ogg';
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const TERMINAL = new Set(['done', 'failed', 'stopped', 'cropping']);
|
|
373
|
+
export class Jobs {
|
|
374
|
+
#t;
|
|
375
|
+
constructor(t) {
|
|
376
|
+
this.#t = t;
|
|
377
|
+
}
|
|
378
|
+
get(recordingId) {
|
|
379
|
+
return this.#t.json('GET', `/v1/recordings/${seg(recordingId)}/job`);
|
|
380
|
+
}
|
|
381
|
+
start(recordingId, window = {}) {
|
|
382
|
+
return this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/job/start`, { body: window });
|
|
383
|
+
}
|
|
384
|
+
retry(recordingId) {
|
|
385
|
+
return this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/job/retry`);
|
|
386
|
+
}
|
|
387
|
+
preview(recordingId) {
|
|
388
|
+
return this.#t.raw('GET', `/v1/recordings/${seg(recordingId)}/job/preview`, { headers: { accept: '*/*' } });
|
|
389
|
+
}
|
|
390
|
+
async wait(recordingId, opts = {}) {
|
|
391
|
+
const interval = opts.intervalMs ?? 2000;
|
|
392
|
+
const deadline = opts.timeoutMs ? Date.now() + opts.timeoutMs : Infinity;
|
|
393
|
+
for (;;) {
|
|
394
|
+
const job = await this.get(recordingId);
|
|
395
|
+
opts.onProgress?.(job);
|
|
396
|
+
if (TERMINAL.has(job.state))
|
|
397
|
+
return job;
|
|
398
|
+
if (Date.now() + interval > deadline)
|
|
399
|
+
throw new VocalStackError('timeout', `job still ${job.state} after ${opts.timeoutMs} ms`, { retryable: true });
|
|
400
|
+
if (opts.signal?.aborted)
|
|
401
|
+
throw opts.signal.reason ?? new Error('aborted');
|
|
402
|
+
await new Promise((r) => setTimeout(r, interval));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
export class Corrections {
|
|
407
|
+
#t;
|
|
408
|
+
constructor(t) {
|
|
409
|
+
this.#t = t;
|
|
410
|
+
}
|
|
411
|
+
list(recordingId) {
|
|
412
|
+
return allPages(async (cursor) => {
|
|
413
|
+
const r = await this.listPage(recordingId, { cursor });
|
|
414
|
+
return { items: r.corrections, nextCursor: r.nextCursor };
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
listPage(recordingId, opts = {}) {
|
|
418
|
+
return this.#t.json('GET', `/v1/recordings/${seg(recordingId)}/corrections`, { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
419
|
+
}
|
|
420
|
+
async apply(recordingId, batch) {
|
|
421
|
+
const r = await this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/corrections`, { body: batch });
|
|
422
|
+
return r.corrections ?? [];
|
|
423
|
+
}
|
|
424
|
+
async retranscribe(recordingId, req) {
|
|
425
|
+
const r = await this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/retranscribe`, { body: req });
|
|
426
|
+
return r.corrections ?? [];
|
|
427
|
+
}
|
|
428
|
+
revert(correctionId) {
|
|
429
|
+
return this.#t.json('POST', `/v1/corrections/${seg(correctionId)}/revert`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
export class ShareLinks {
|
|
433
|
+
#t;
|
|
434
|
+
constructor(t) {
|
|
435
|
+
this.#t = t;
|
|
436
|
+
}
|
|
437
|
+
open(code) {
|
|
438
|
+
return this.#t.json('GET', `/v1/shares/${seg(code)}`, { anonymous: true });
|
|
439
|
+
}
|
|
440
|
+
update(id, req) {
|
|
441
|
+
return this.#t.json('PATCH', `/v1/share-links/${seg(id)}`, { body: req });
|
|
442
|
+
}
|
|
443
|
+
revoke(id) {
|
|
444
|
+
return this.#t.json('DELETE', `/v1/share-links/${seg(id)}`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
export class Tags {
|
|
448
|
+
#t;
|
|
449
|
+
constructor(t) {
|
|
450
|
+
this.#t = t;
|
|
451
|
+
}
|
|
452
|
+
async list() {
|
|
453
|
+
const r = await this.#t.json('GET', '/v1/tags');
|
|
454
|
+
return r.tags ?? [];
|
|
455
|
+
}
|
|
456
|
+
create(req) {
|
|
457
|
+
return this.#t.json('POST', '/v1/tags', { body: req });
|
|
458
|
+
}
|
|
459
|
+
update(id, req) {
|
|
460
|
+
return this.#t.json('PATCH', `/v1/tags/${seg(id)}`, { body: req });
|
|
461
|
+
}
|
|
462
|
+
async delete(id) {
|
|
463
|
+
await this.#t.json('DELETE', `/v1/tags/${seg(id)}`);
|
|
464
|
+
}
|
|
465
|
+
tagTranscriptions(id, req) {
|
|
466
|
+
return this.#t.json('POST', `/v1/tags/${seg(id)}/transcriptions`, { body: req });
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
export class Voices {
|
|
470
|
+
#t;
|
|
471
|
+
constructor(t) {
|
|
472
|
+
this.#t = t;
|
|
473
|
+
}
|
|
474
|
+
async list(opts = {}) {
|
|
475
|
+
const r = await this.#t.json('GET', '/v1/voices', { query: { lifecycle: opts.lifecycle } });
|
|
476
|
+
return r.voices ?? [];
|
|
477
|
+
}
|
|
478
|
+
get(id) {
|
|
479
|
+
return this.#t.json('GET', `/v1/voices/${seg(id)}`);
|
|
480
|
+
}
|
|
481
|
+
archive(id) {
|
|
482
|
+
return this.#t.json('POST', `/v1/voices/${seg(id)}/archive`);
|
|
483
|
+
}
|
|
484
|
+
restore(id) {
|
|
485
|
+
return this.#t.json('POST', `/v1/voices/${seg(id)}/restore`);
|
|
486
|
+
}
|
|
487
|
+
archiveMany(ids) {
|
|
488
|
+
return this.#t.json('POST', '/v1/voices/archive', { body: { ids } });
|
|
489
|
+
}
|
|
490
|
+
restoreMany(ids) {
|
|
491
|
+
return this.#t.json('POST', '/v1/voices/restore', { body: { ids } });
|
|
492
|
+
}
|
|
493
|
+
deleteMany(ids) {
|
|
494
|
+
return this.#t.json('POST', '/v1/voices/delete', { body: { ids } });
|
|
495
|
+
}
|
|
496
|
+
async create(req, call = {}) {
|
|
497
|
+
const { sample, fileName, ...meta } = req;
|
|
498
|
+
const form = new FormData();
|
|
499
|
+
form.append('meta', JSON.stringify({ consent: !meta.draft, ...meta }));
|
|
500
|
+
if (sample !== undefined) {
|
|
501
|
+
const { blob, name } = await toBlob(sample);
|
|
502
|
+
form.append('sample', blob, fileName ?? name ?? 'sample.webm');
|
|
503
|
+
}
|
|
504
|
+
return this.#t.json('POST', '/v1/voices', { body: form, ...call });
|
|
505
|
+
}
|
|
506
|
+
createFromSpeaker(req, call = {}) {
|
|
507
|
+
return this.#t.json('POST', '/v1/voices/from-speaker', { body: { consent: true, ...req }, ...call });
|
|
508
|
+
}
|
|
509
|
+
setConsent(id, consent, opts = {}) {
|
|
510
|
+
return this.#t.json('POST', `/v1/voices/${seg(id)}/consent`, { body: { consent, ...opts } });
|
|
511
|
+
}
|
|
512
|
+
update(id, patch) {
|
|
513
|
+
return this.#t.json('PATCH', `/v1/voices/${seg(id)}`, { body: patch });
|
|
514
|
+
}
|
|
515
|
+
async delete(id) {
|
|
516
|
+
await this.#t.json('DELETE', `/v1/voices/${seg(id)}`);
|
|
517
|
+
}
|
|
518
|
+
available(lang, opts = {}) {
|
|
519
|
+
return this.#t.json('GET', '/v1/voices/available', {
|
|
520
|
+
query: { ...listenerQuery(opts), lang, recording: opts.recording },
|
|
521
|
+
anonymous: opts.asOwner === false,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
preview(voice, lang, opts = {}) {
|
|
525
|
+
return this.#t.raw('GET', '/v1/voices/preview', { headers: { accept: '*/*' }, query: { voice, lang, format: opts.format } });
|
|
526
|
+
}
|
|
527
|
+
clonePreview(id, opts = {}) {
|
|
528
|
+
return this.#t.raw('GET', `/v1/voices/${seg(id)}/preview`, {
|
|
529
|
+
headers: { accept: '*/*' },
|
|
530
|
+
query: { ...listenerQuery(opts), lang: opts.lang, format: opts.format, recording: opts.recording },
|
|
531
|
+
anonymous: opts.asOwner === false,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
sample(id) {
|
|
535
|
+
return this.#t.raw('GET', `/v1/voices/${seg(id)}/sample`, { headers: { accept: '*/*' } });
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
export class Search {
|
|
539
|
+
#t;
|
|
540
|
+
constructor(t) {
|
|
541
|
+
this.#t = t;
|
|
542
|
+
}
|
|
543
|
+
query(q, opts = {}) {
|
|
544
|
+
const query = {
|
|
545
|
+
q,
|
|
546
|
+
broadcasts: opts.broadcasts?.join(','),
|
|
547
|
+
listenerTokens: opts.listenerTokens?.join(','),
|
|
548
|
+
recording: opts.recording,
|
|
549
|
+
lang: opts.lang,
|
|
550
|
+
limit: opts.limit,
|
|
551
|
+
};
|
|
552
|
+
return this.#t.json('GET', '/v1/search', { query });
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
export class MeApi {
|
|
556
|
+
#t;
|
|
557
|
+
constructor(t) {
|
|
558
|
+
this.#t = t;
|
|
559
|
+
}
|
|
560
|
+
get() {
|
|
561
|
+
return this.#t.json('GET', '/v1/me');
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
export class Connect {
|
|
565
|
+
#t;
|
|
566
|
+
constructor(t) {
|
|
567
|
+
this.#t = t;
|
|
568
|
+
}
|
|
569
|
+
token(input, opts = {}) {
|
|
570
|
+
const body = new URLSearchParams({
|
|
571
|
+
grant_type: 'authorization_code',
|
|
572
|
+
code: input.code,
|
|
573
|
+
code_verifier: input.codeVerifier,
|
|
574
|
+
redirect_uri: input.redirectUri,
|
|
575
|
+
});
|
|
576
|
+
return this.#t.json('POST', '/v1/connect/token', { body, anonymous: true, noRetry: true, oauthErrors: true, signal: opts.signal });
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
export class Billing {
|
|
580
|
+
#t;
|
|
581
|
+
constructor(t) {
|
|
582
|
+
this.#t = t;
|
|
583
|
+
}
|
|
584
|
+
get() {
|
|
585
|
+
return this.#t.json('GET', '/v1/billing');
|
|
586
|
+
}
|
|
587
|
+
plans() {
|
|
588
|
+
return this.#t.json('GET', '/v1/billing/plans');
|
|
589
|
+
}
|
|
590
|
+
ledger(opts = {}) {
|
|
591
|
+
return this.#t.json('GET', '/v1/billing/ledger', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
592
|
+
}
|
|
593
|
+
charges(opts = {}) {
|
|
594
|
+
return this.#t.json('GET', '/v1/billing/charges', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
595
|
+
}
|
|
596
|
+
createCheckout(req, opts = {}) {
|
|
597
|
+
return this.#t.json('POST', '/v1/billing/checkouts', { body: req, ...opts });
|
|
598
|
+
}
|
|
599
|
+
getCheckout(id) {
|
|
600
|
+
return this.#t.json('GET', `/v1/billing/checkouts/${seg(id)}`);
|
|
601
|
+
}
|
|
602
|
+
cancelSubscription() {
|
|
603
|
+
return this.#t.json('POST', '/v1/billing/subscription/cancel');
|
|
604
|
+
}
|
|
605
|
+
uncancelSubscription() {
|
|
606
|
+
return this.#t.json('POST', '/v1/billing/subscription/uncancel');
|
|
607
|
+
}
|
|
608
|
+
setAutoTopUp(req) {
|
|
609
|
+
return this.#t.json('PUT', '/v1/billing/auto-top-up', { body: req });
|
|
610
|
+
}
|
|
611
|
+
portal() {
|
|
612
|
+
return this.#t.json('GET', '/v1/billing/portal');
|
|
613
|
+
}
|
|
614
|
+
retryAutoTopUp() {
|
|
615
|
+
return this.#t.json('POST', '/v1/billing/auto-top-up/retry');
|
|
616
|
+
}
|
|
617
|
+
setLowBalanceAlert(req) {
|
|
618
|
+
return this.#t.json('PUT', '/v1/billing/low-balance-alert', { body: req });
|
|
619
|
+
}
|
|
620
|
+
setInterval(req) {
|
|
621
|
+
return this.#t.json('POST', '/v1/billing/subscription/interval', { body: req });
|
|
622
|
+
}
|
|
623
|
+
optInYearly(req) {
|
|
624
|
+
return this.#t.json('POST', '/v1/billing/subscription/yearly-opt-in', { body: req });
|
|
625
|
+
}
|
|
626
|
+
unsetInterval() {
|
|
627
|
+
return this.#t.json('DELETE', '/v1/billing/subscription/interval');
|
|
628
|
+
}
|
|
629
|
+
usage(opts = {}) {
|
|
630
|
+
return this.#t.json('GET', '/v1/billing/usage', { query: { startDate: opts.startDate, endDate: opts.endDate, tz: opts.tz } });
|
|
631
|
+
}
|
|
632
|
+
usageCharges(opts = {}) {
|
|
633
|
+
const query = { startDate: opts.startDate, endDate: opts.endDate, tz: opts.tz, kind: opts.kind, source: opts.source, cursor: opts.cursor, limit: opts.limit };
|
|
634
|
+
return this.#t.json('GET', '/v1/billing/usage/charges', { query });
|
|
635
|
+
}
|
|
636
|
+
payments(opts = {}) {
|
|
637
|
+
return this.#t.json('GET', '/v1/billing/payments', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
638
|
+
}
|
|
639
|
+
paymentDocument(id) {
|
|
640
|
+
return this.#t.json('GET', `/v1/billing/payments/${seg(id)}/document`);
|
|
641
|
+
}
|
|
642
|
+
paymentMethods() {
|
|
643
|
+
return this.#t.json('GET', '/v1/billing/payment-methods');
|
|
644
|
+
}
|
|
645
|
+
async deletePaymentMethod(id) {
|
|
646
|
+
await this.#t.json('DELETE', `/v1/billing/payment-methods/${seg(id)}`);
|
|
647
|
+
}
|
|
648
|
+
managePaymentMethods() {
|
|
649
|
+
return this.#t.json('POST', '/v1/billing/payment-methods/manage');
|
|
650
|
+
}
|
|
651
|
+
details() {
|
|
652
|
+
return this.#t.json('GET', '/v1/billing/details');
|
|
653
|
+
}
|
|
654
|
+
setDetails(req) {
|
|
655
|
+
return this.#t.json('PUT', '/v1/billing/details', { body: req });
|
|
656
|
+
}
|
|
657
|
+
checkPromoCode(req) {
|
|
658
|
+
return this.#t.json('POST', '/v1/billing/promo-codes/check', { body: req });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
export class Languages {
|
|
662
|
+
#t;
|
|
663
|
+
constructor(t) {
|
|
664
|
+
this.#t = t;
|
|
665
|
+
}
|
|
666
|
+
list() {
|
|
667
|
+
return this.#t.json('GET', '/v1/languages');
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
export class Rtmp {
|
|
671
|
+
#t;
|
|
672
|
+
constructor(t) {
|
|
673
|
+
this.#t = t;
|
|
674
|
+
}
|
|
675
|
+
status() {
|
|
676
|
+
return this.#t.json('GET', '/v1/rtmp');
|
|
677
|
+
}
|
|
678
|
+
createKey(req) {
|
|
679
|
+
return this.#t.json('POST', '/v1/rtmp/keys', { body: req });
|
|
680
|
+
}
|
|
681
|
+
async listKeys() {
|
|
682
|
+
const r = await this.#t.json('GET', '/v1/rtmp/keys');
|
|
683
|
+
return r.keys ?? [];
|
|
684
|
+
}
|
|
685
|
+
async deleteKey(id) {
|
|
686
|
+
await this.#t.json('DELETE', `/v1/rtmp/keys/${seg(id)}`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
export class Events {
|
|
690
|
+
#t;
|
|
691
|
+
constructor(t) {
|
|
692
|
+
this.#t = t;
|
|
693
|
+
}
|
|
694
|
+
async account() {
|
|
695
|
+
return openWebSocket(this.#t.wsUrl('/v1/events'), await this.#t.bearer());
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
export class Meta {
|
|
699
|
+
#t;
|
|
700
|
+
constructor(t) {
|
|
701
|
+
this.#t = t;
|
|
702
|
+
}
|
|
703
|
+
health() {
|
|
704
|
+
return this.#t.json('GET', '/v1/health');
|
|
705
|
+
}
|
|
706
|
+
openapi() {
|
|
707
|
+
return this.#t.json('GET', '/v1/openapi.json');
|
|
708
|
+
}
|
|
709
|
+
clientPolicy(client) {
|
|
710
|
+
return this.#t.json('GET', `/v1/clients/${encodeURIComponent(client)}`);
|
|
711
|
+
}
|
|
712
|
+
}
|