@vocalstack/js-sdk 2.0.0-alpha.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/README.md +5 -1
- package/SKILL.md +3 -6
- package/dist/errors.d.ts +0 -18
- package/dist/errors.js +0 -18
- package/dist/http.d.ts +0 -32
- package/dist/http.js +26 -24
- package/dist/index.d.ts +4 -18
- package/dist/index.js +4 -18
- package/dist/mic.d.ts +1 -49
- package/dist/mic.js +0 -22
- package/dist/realtime.d.ts +4 -59
- package/dist/realtime.js +192 -152
- package/dist/resources.d.ts +60 -207
- package/dist/resources.js +101 -176
- package/dist/subscription.d.ts +0 -17
- package/dist/subscription.js +1 -12
- package/dist/ws.d.ts +0 -3
- package/dist/ws.js +0 -3
- package/package.json +2 -2
- package/skills/vocalstack/SKILL.md +3 -6
package/dist/resources.js
CHANGED
|
@@ -14,8 +14,6 @@ async function toBlob(file) {
|
|
|
14
14
|
}
|
|
15
15
|
return { blob: new Blob([file]) };
|
|
16
16
|
}
|
|
17
|
-
/** Every item of a paged list: each page asked for with the previous
|
|
18
|
-
* page's `nextCursor` until one comes back without it. */
|
|
19
17
|
async function allPages(page) {
|
|
20
18
|
const out = [];
|
|
21
19
|
let cursor;
|
|
@@ -27,8 +25,6 @@ async function allPages(page) {
|
|
|
27
25
|
cursor = r.nextCursor;
|
|
28
26
|
}
|
|
29
27
|
}
|
|
30
|
-
/** What broadcasts and transcriptions have in common: both hold
|
|
31
|
-
* recordings and share links. */
|
|
32
28
|
class ParentResource {
|
|
33
29
|
t;
|
|
34
30
|
constructor(t) {
|
|
@@ -37,8 +33,6 @@ class ParentResource {
|
|
|
37
33
|
p(id, rest = '') {
|
|
38
34
|
return `${this.base}/${seg(id)}${rest}`;
|
|
39
35
|
}
|
|
40
|
-
/** Every one, newest first: a paged list is read to its last page
|
|
41
|
-
* (`transcriptions.listPage` reads one). */
|
|
42
36
|
list(lifecycle = 'active') {
|
|
43
37
|
return allPages(async (cursor) => {
|
|
44
38
|
const r = await this.t.json('GET', this.base, { query: { lifecycle, cursor } });
|
|
@@ -48,36 +42,36 @@ class ParentResource {
|
|
|
48
42
|
get(id) {
|
|
49
43
|
return this.t.json('GET', this.p(id));
|
|
50
44
|
}
|
|
51
|
-
/** Rename, change the default capture settings (a broadcast: also its access, password or link). */
|
|
52
45
|
update(id, req) {
|
|
53
46
|
return this.t.json('PATCH', this.p(id), { body: req });
|
|
54
47
|
}
|
|
55
|
-
/** Hidden, its links dead, its data kept until `restore` or `purge`. */
|
|
56
48
|
archive(id) {
|
|
57
49
|
return this.t.json('POST', this.p(id, '/archive'));
|
|
58
50
|
}
|
|
59
51
|
restore(id) {
|
|
60
52
|
return this.t.json('POST', this.p(id, '/restore'));
|
|
61
53
|
}
|
|
62
|
-
/** Delete an archived one and everything in it, for good. */
|
|
63
54
|
async purge(id) {
|
|
64
55
|
await this.t.json('POST', this.p(id, '/purge'));
|
|
65
56
|
}
|
|
66
|
-
|
|
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
|
+
}
|
|
67
66
|
listRecordings(id, lifecycle = 'active') {
|
|
68
67
|
return allPages(async (cursor) => {
|
|
69
68
|
const r = await this.listRecordingsPage(id, { lifecycle, cursor });
|
|
70
69
|
return { items: r.recordings, nextCursor: r.nextCursor };
|
|
71
70
|
});
|
|
72
71
|
}
|
|
73
|
-
/** One page of recordings, newest first: pass its `nextCursor` as `cursor` for the next. */
|
|
74
72
|
listRecordingsPage(id, opts = {}) {
|
|
75
73
|
return this.t.json('GET', this.p(id, '/recordings'), { query: { lifecycle: opts.lifecycle, cursor: opts.cursor, limit: opts.limit } });
|
|
76
74
|
}
|
|
77
|
-
/** Start a live recording. The grant's `streamUrl` is the publisher
|
|
78
|
-
* socket (`vs.recordings.publish(grant)`). A 409 `conflict` with
|
|
79
|
-
* `details.takeover` means another device is recording: retry with
|
|
80
|
-
* `{ takeover: true }` to record here instead. */
|
|
81
75
|
startRecording(id, req = {}, call = {}) {
|
|
82
76
|
return this.t.json('POST', this.p(id, '/recordings'), { body: req, ...call });
|
|
83
77
|
}
|
|
@@ -88,52 +82,36 @@ class ParentResource {
|
|
|
88
82
|
createShareLink(id, req) {
|
|
89
83
|
return this.t.json('POST', this.p(id, '/share-links'), { body: req });
|
|
90
84
|
}
|
|
91
|
-
/** The events socket a waiting screen needs: `recording.started` (it is
|
|
92
|
-
* live), `recording.ended`, `job.progress`, `stream.ingest`.
|
|
93
|
-
* Each names the transcription the recording is made in (`transcriptionId`). */
|
|
94
85
|
async events(id, as = {}) {
|
|
95
86
|
const token = as.asOwner === false ? undefined : await this.t.bearer();
|
|
96
87
|
return openWebSocket(this.t.wsUrl(this.p(id, '/events'), listenerQuery(as)), token);
|
|
97
88
|
}
|
|
98
89
|
}
|
|
99
|
-
/** `vs.broadcasts`: the links listeners open (design 77). */
|
|
100
90
|
export class Broadcasts extends ParentResource {
|
|
101
91
|
base = '/v1/broadcasts';
|
|
102
92
|
key = 'broadcasts';
|
|
103
93
|
create(req, call = {}) {
|
|
104
94
|
return this.t.json('POST', this.base, { body: req, ...call });
|
|
105
95
|
}
|
|
106
|
-
/** Give a broadcast a stream it can record from (a YouTube live, an
|
|
107
|
-
* internet radio address). Returns the broadcast. */
|
|
108
96
|
addAudioSource(id, req) {
|
|
109
97
|
return this.t.json('POST', this.p(id, '/audio-sources'), { body: req });
|
|
110
98
|
}
|
|
111
99
|
removeAudioSource(id, sourceId) {
|
|
112
100
|
return this.t.json('DELETE', this.p(id, `/audio-sources/${seg(sourceId)}`));
|
|
113
101
|
}
|
|
114
|
-
/** Whether that source can be recorded right now; `fresh` skips the short cache. */
|
|
115
102
|
checkAudioSource(id, sourceId, opts = {}) {
|
|
116
103
|
return this.t.json('GET', this.p(id, `/audio-sources/${seg(sourceId)}/check`), { query: { fresh: opts.fresh || undefined } });
|
|
117
104
|
}
|
|
118
105
|
}
|
|
119
|
-
/** `vs.transcriptions`: a file, a link, a microphone or a stream, transcribed and stored. */
|
|
120
106
|
export class Transcriptions extends ParentResource {
|
|
121
107
|
base = '/v1/transcriptions';
|
|
122
108
|
key = 'transcriptions';
|
|
123
|
-
/** One page of transcriptions, newest first: pass its `nextCursor` as
|
|
124
|
-
* `cursor` for the next; the last page has none. */
|
|
125
109
|
listPage(opts = {}) {
|
|
126
110
|
return this.t.json('GET', this.base, { query: { lifecycle: opts.lifecycle, cursor: opts.cursor, limit: opts.limit } });
|
|
127
111
|
}
|
|
128
|
-
/** A file or link recording in this transcription: no live phase, transcribed at full speed.
|
|
129
|
-
* It has no job: open the grant's socket with `vs.recordings.publish(grant)` (with `url`
|
|
130
|
-
* the server fetches the link) and read it until the session is done. */
|
|
131
112
|
uploadRecording(id, req = {}, call = {}) {
|
|
132
113
|
return this.t.json('POST', this.p(id, '/recordings/upload'), { body: req, ...call });
|
|
133
114
|
}
|
|
134
|
-
/** From a file (`file`) or a public link (`url`). The answer's
|
|
135
|
-
* `job.recordingId` is the job: `vs.jobs.wait(recordingId)`,
|
|
136
|
-
* then `vs.recordings.transcript(recordingId)`. */
|
|
137
115
|
async create(req, call = {}) {
|
|
138
116
|
const { file, fileName, ...options } = req;
|
|
139
117
|
if (file === undefined)
|
|
@@ -144,20 +122,15 @@ export class Transcriptions extends ParentResource {
|
|
|
144
122
|
fd.append('file', blob, fileName ?? name ?? 'upload');
|
|
145
123
|
return this.t.json('POST', this.base, { body: fd, ...call });
|
|
146
124
|
}
|
|
147
|
-
/** `create({ url, ...opts })`. */
|
|
148
125
|
createFromUrl(url, opts = {}, call = {}) {
|
|
149
126
|
return this.create({ ...opts, url }, call);
|
|
150
127
|
}
|
|
151
|
-
/** A transcription captured as it is spoken: a microphone, or a stream
|
|
152
|
-
* the server pulls. Created idle: `startRecording(id)` starts it. */
|
|
153
128
|
createLive(req, call = {}) {
|
|
154
129
|
return this.t.json('POST', `${this.base}/live`, { body: req, ...call });
|
|
155
130
|
}
|
|
156
|
-
/** Whether a link is live (a live transcription) or a file. */
|
|
157
131
|
probe(url) {
|
|
158
132
|
return this.t.json('GET', `${this.base}/probe`, { query: { url } });
|
|
159
133
|
}
|
|
160
|
-
/** Feed a broadcast from a live transcription. Returns the transcription. */
|
|
161
134
|
attachBroadcast(id, broadcastId) {
|
|
162
135
|
return this.t.json('PUT', this.p(id, `/broadcasts/${seg(broadcastId)}`));
|
|
163
136
|
}
|
|
@@ -165,11 +138,6 @@ export class Transcriptions extends ParentResource {
|
|
|
165
138
|
return this.t.json('DELETE', this.p(id, `/broadcasts/${seg(broadcastId)}`));
|
|
166
139
|
}
|
|
167
140
|
}
|
|
168
|
-
/** `vs.batches`: several files transcribed together, for less than a
|
|
169
|
-
* transcription each (one create, one minimum charge). Create a batch, add
|
|
170
|
-
* each file or link with `addFile`, then `start` it; `get` shows its files
|
|
171
|
-
* (each a transcription with its job) and their counts. A batch's files get
|
|
172
|
-
* no summary, title, share link or spoken translations. */
|
|
173
141
|
export class Batches {
|
|
174
142
|
#t;
|
|
175
143
|
constructor(t) {
|
|
@@ -178,31 +146,24 @@ export class Batches {
|
|
|
178
146
|
#p(id, rest = '') {
|
|
179
147
|
return `/v1/batches/${seg(id)}${rest}`;
|
|
180
148
|
}
|
|
181
|
-
/** An open batch, ready for files (`name` defaults to "Batch of <date>"). */
|
|
182
149
|
create(req = {}, call = {}) {
|
|
183
150
|
return this.#t.json('POST', '/v1/batches', { body: req, ...call });
|
|
184
151
|
}
|
|
185
|
-
/** Every batch, newest first (every page of `listPage`); each with its counts, not its files. */
|
|
186
152
|
list() {
|
|
187
153
|
return allPages(async (cursor) => {
|
|
188
154
|
const r = await this.listPage({ cursor });
|
|
189
155
|
return { items: r.batches, nextCursor: r.nextCursor };
|
|
190
156
|
});
|
|
191
157
|
}
|
|
192
|
-
/** One page of batches, newest first: pass its `nextCursor` as `cursor` for the next. */
|
|
193
158
|
listPage(opts = {}) {
|
|
194
159
|
return this.#t.json('GET', '/v1/batches', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
195
160
|
}
|
|
196
|
-
/** A batch with its files (`files`, each a transcription with its job), in the order they were added. */
|
|
197
161
|
get(id) {
|
|
198
162
|
return this.#t.json('GET', this.#p(id));
|
|
199
163
|
}
|
|
200
|
-
/** Discard a batch that was never started, with its files (nothing was charged). */
|
|
201
164
|
async delete(id) {
|
|
202
165
|
await this.#t.json('DELETE', this.#p(id));
|
|
203
166
|
}
|
|
204
|
-
/** Add one file (`file`) or link (`url`) to an open batch: a transcription
|
|
205
|
-
* of its own (`batchId`), waiting for the batch's `start`. */
|
|
206
167
|
async addFile(id, req, call = {}) {
|
|
207
168
|
const { file, fileName, ...options } = req;
|
|
208
169
|
if (file === undefined)
|
|
@@ -213,36 +174,24 @@ export class Batches {
|
|
|
213
174
|
fd.append('file', blob, fileName ?? name ?? 'upload');
|
|
214
175
|
return this.#t.json('POST', this.#p(id, '/files'), { body: fd, ...call });
|
|
215
176
|
}
|
|
216
|
-
/** `addFile(id, { url, ...opts })`. */
|
|
217
177
|
addUrl(id, url, opts = {}, call = {}) {
|
|
218
178
|
return this.addFile(id, { ...opts, url }, call);
|
|
219
179
|
}
|
|
220
|
-
/** Seal the batch and queue its files, longest first; `wait` follows it to the end. */
|
|
221
180
|
start(id, call = {}) {
|
|
222
181
|
return this.#t.json('POST', this.#p(id, '/start'), call);
|
|
223
182
|
}
|
|
224
|
-
/** Every finished file's transcript in one `format` (as `recordings.export`
|
|
225
|
-
* makes one), in one zip, as a `Response`: `.arrayBuffer()` it. Files
|
|
226
|
-
* still running or failed are left out. */
|
|
227
183
|
export(id, format) {
|
|
228
184
|
return this.#t.raw('GET', this.#p(id, '/export'), { headers: { accept: '*/*' }, query: { format } });
|
|
229
185
|
}
|
|
230
|
-
/** Stop every file not finished yet: queued files never start, a running
|
|
231
|
-
* one is cancelled and billed for what it transcribed. */
|
|
232
186
|
stop(id) {
|
|
233
187
|
return this.#t.json('POST', this.#p(id, '/stop'));
|
|
234
188
|
}
|
|
235
|
-
/** Poll a started batch until it is `done` (every file finished or
|
|
236
|
-
* failed) or `stopped`, and return it with its files. It does not throw
|
|
237
|
-
* on a failed file: check `counts.failed` and each file's `job.state`.
|
|
238
|
-
* `onProgress` sees every read, the last one included. */
|
|
239
189
|
async wait(id, opts = {}) {
|
|
240
190
|
const interval = opts.intervalMs ?? 5000;
|
|
241
191
|
const deadline = opts.timeoutMs ? Date.now() + opts.timeoutMs : Infinity;
|
|
242
192
|
for (;;) {
|
|
243
193
|
const batch = await this.get(id);
|
|
244
194
|
opts.onProgress?.(batch);
|
|
245
|
-
// Closed: every file over (a stopped batch's running file too).
|
|
246
195
|
if ((batch.state === 'done' || batch.state === 'stopped') && batch.finishedAt)
|
|
247
196
|
return batch;
|
|
248
197
|
if (batch.state === 'open')
|
|
@@ -255,7 +204,6 @@ export class Batches {
|
|
|
255
204
|
}
|
|
256
205
|
}
|
|
257
206
|
}
|
|
258
|
-
/** `vs.recordings`: one take, live or uploaded. */
|
|
259
207
|
export class Recordings {
|
|
260
208
|
#t;
|
|
261
209
|
constructor(t) {
|
|
@@ -273,19 +221,15 @@ export class Recordings {
|
|
|
273
221
|
resume(id) {
|
|
274
222
|
return this.#t.json('POST', this.#p(id, '/resume'));
|
|
275
223
|
}
|
|
276
|
-
/** Only from paused. */
|
|
277
224
|
stop(id, reason) {
|
|
278
225
|
return this.#t.json('POST', this.#p(id, '/stop'), { body: { reason } });
|
|
279
226
|
}
|
|
280
227
|
settings(id, patch) {
|
|
281
228
|
return this.#t.json('PATCH', this.#p(id, '/settings'), { body: patch });
|
|
282
229
|
}
|
|
283
|
-
/** A new publisher grant for a recording that is already live. */
|
|
284
230
|
publishToken(id, opts = {}) {
|
|
285
231
|
return this.#t.json('POST', this.#p(id, '/publish-token'), { body: { takeover: opts.takeover ?? false } });
|
|
286
232
|
}
|
|
287
|
-
/** Open the publisher socket a grant names and wait until it is ready:
|
|
288
|
-
* send raw PCM (f32le, 16 kHz, mono) with `sendAudio`, end with `eof()`. */
|
|
289
233
|
publish(grant, opts = {}) {
|
|
290
234
|
if (!grant.streamUrl)
|
|
291
235
|
throw new VocalStackError('invalid_state', 'this grant has no streamUrl (an upload recording has no publisher socket)');
|
|
@@ -306,42 +250,38 @@ export class Recordings {
|
|
|
306
250
|
async purge(id) {
|
|
307
251
|
await this.#t.json('POST', this.#p(id, '/purge'));
|
|
308
252
|
}
|
|
309
|
-
|
|
310
|
-
|
|
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
|
+
}
|
|
311
262
|
transcript(id, opts = {}) {
|
|
312
263
|
return this.#t.json('GET', this.#p(id, '/transcript'), {
|
|
313
264
|
query: { ...listenerQuery(opts), after: opts.after, lang: opts.lang, role: opts.asListener ? 'listener' : undefined },
|
|
314
265
|
});
|
|
315
266
|
}
|
|
316
|
-
/** Translate the given sentences (at most 40 ids per call); the answer
|
|
317
|
-
* carries only what this call translated. */
|
|
318
267
|
translation(id, lang, sentences = [], as) {
|
|
319
268
|
return this.#t.json('GET', this.#p(id, '/translation'), {
|
|
320
269
|
query: { ...listenerQuery(as), lang, sentences: sentences.length ? sentences.join(',') : undefined },
|
|
321
270
|
});
|
|
322
271
|
}
|
|
323
|
-
/** Queue the AI title and summary of a finished recording. */
|
|
324
272
|
summarize(id, call = {}) {
|
|
325
273
|
return this.#t.json('POST', this.#p(id, '/summarize'), call);
|
|
326
274
|
}
|
|
327
|
-
/** The title and summary as they stand, in `lang` when given. */
|
|
328
275
|
summary(id, opts = {}) {
|
|
329
276
|
return this.#t.json('GET', this.#p(id, '/summary'), { query: { ...listenerQuery(opts), lang: opts.lang } });
|
|
330
277
|
}
|
|
331
|
-
/** An export file (JSON, SRT, XLSX, PDF) as a `Response`: `.text()` or
|
|
332
|
-
* `.arrayBuffer()` it. `lang` makes it read in that language. */
|
|
333
278
|
export(id, format, opts = {}) {
|
|
334
279
|
return this.#t.raw('GET', this.#p(id, '/export'), { headers: { accept: '*/*' }, query: { ...listenerQuery(opts), format, lang: opts.lang } });
|
|
335
280
|
}
|
|
336
|
-
/** The export as a plain link, for a browser download with a browser
|
|
337
|
-
* access token (it rides in the query). Never use it with an API key:
|
|
338
|
-
* the API refuses a key in a URL. */
|
|
339
281
|
async exportUrl(id, format, opts = {}) {
|
|
340
282
|
const token = opts.shareCode || opts.listenerToken ? undefined : await this.#t.bearer();
|
|
341
283
|
return this.#t.url(this.#p(id, '/export'), { format, lang: opts.lang, shareCode: opts.shareCode, listenerToken: opts.listenerToken, access_token: token });
|
|
342
284
|
}
|
|
343
|
-
/** The signed media URL the server redirects to. `kind`: `delivery`
|
|
344
|
-
* (default), `master`, or `source` (a transcription's original upload). */
|
|
345
285
|
async audioUrl(id, opts = {}) {
|
|
346
286
|
const res = await this.#t.raw('GET', this.#p(id, '/audio'), {
|
|
347
287
|
query: { ...listenerQuery(opts), kind: opts.kind, lang: opts.lang, format: opts.format },
|
|
@@ -351,44 +291,54 @@ export class Recordings {
|
|
|
351
291
|
throw new VocalStackError('not_found', 'no signed URL in the redirect');
|
|
352
292
|
return location;
|
|
353
293
|
}
|
|
354
|
-
|
|
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
|
+
}
|
|
355
310
|
clip(id, startS, endS, as) {
|
|
356
311
|
return this.#t.raw('GET', this.#p(id, '/audio/clip'), { headers: { accept: '*/*' }, query: { ...listenerQuery(as), startS, endS } });
|
|
357
312
|
}
|
|
358
|
-
/** The synthesized voice tracks of a recording (per language). */
|
|
359
313
|
voice(id, lang, as) {
|
|
360
314
|
return this.#t.json('GET', this.#p(id, '/voice'), { query: { ...listenerQuery(as), lang } });
|
|
361
315
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
return this.#t.json('POST', this.#p(id, '/voice'), {
|
|
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
|
+
});
|
|
365
323
|
}
|
|
366
|
-
/** One sentence spoken in `lang`. */
|
|
367
324
|
voiceClip(id, sentence, lang, opts = {}) {
|
|
368
325
|
return this.#t.raw('GET', this.#p(id, `/voice/${seg(sentence)}`), { headers: { accept: '*/*' }, query: { ...listenerQuery(opts), lang, format: opts.format } });
|
|
369
326
|
}
|
|
370
|
-
/** A short-lived token for the recording's media URLs. */
|
|
371
327
|
mediaToken(id) {
|
|
372
328
|
return this.#t.json('GET', this.#p(id, '/media-token'));
|
|
373
329
|
}
|
|
374
|
-
/** Name a speaker (the diarizer's index); `null` clears the name. */
|
|
375
330
|
async nameSpeaker(id, speaker, name) {
|
|
376
331
|
await this.#t.json('PUT', this.#p(id, `/speakers/${seg(speaker)}`), { body: { name } });
|
|
377
332
|
}
|
|
378
|
-
/** The durable event log with wall times (owner only): every event
|
|
379
|
-
* after `after` (a `seq`; the whole log without it), read `limit` a page. */
|
|
380
333
|
events(id, opts = {}) {
|
|
381
334
|
return allPages(async (cursor) => {
|
|
382
335
|
const r = await this.eventsPage(id, cursor ? { cursor, limit: opts.limit } : { after: opts.after, limit: opts.limit });
|
|
383
336
|
return { items: r.events, nextCursor: r.nextCursor };
|
|
384
337
|
});
|
|
385
338
|
}
|
|
386
|
-
/** One page of the event log (500 by default, 1000 at most): from `after`
|
|
387
|
-
* a `seq`, or the previous page's `nextCursor` as `cursor`. */
|
|
388
339
|
eventsPage(id, opts = {}) {
|
|
389
340
|
return this.#t.json('GET', this.#p(id, '/events'), { query: { cursor: opts.cursor, after: opts.after, limit: opts.limit } });
|
|
390
341
|
}
|
|
391
|
-
/** Attach to the live fan-out; resolves once the backfill snapshot is in place. */
|
|
392
342
|
async subscribe(id, opts = {}) {
|
|
393
343
|
const token = opts.asOwner === false ? undefined : await this.#t.bearer();
|
|
394
344
|
const url = this.#t.wsUrl(this.#p(id, '/subscribe'), {
|
|
@@ -407,10 +357,19 @@ export class Recordings {
|
|
|
407
357
|
}));
|
|
408
358
|
}
|
|
409
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
|
+
}
|
|
410
372
|
const TERMINAL = new Set(['done', 'failed', 'stopped', 'cropping']);
|
|
411
|
-
/** `vs.jobs`: the transcription job of a file or link recording
|
|
412
|
-
* (`/v1/recordings/{id}/job`). States: queued, fetching, transcribing,
|
|
413
|
-
* done, failed, and cropping (waiting for `start`). */
|
|
414
373
|
export class Jobs {
|
|
415
374
|
#t;
|
|
416
375
|
constructor(t) {
|
|
@@ -419,22 +378,15 @@ export class Jobs {
|
|
|
419
378
|
get(recordingId) {
|
|
420
379
|
return this.#t.json('GET', `/v1/recordings/${seg(recordingId)}/job`);
|
|
421
380
|
}
|
|
422
|
-
/** Release a job waiting in `cropping`: the window to transcribe, in seconds. */
|
|
423
381
|
start(recordingId, window = {}) {
|
|
424
382
|
return this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/job/start`, { body: window });
|
|
425
383
|
}
|
|
426
|
-
/** Re-queue a failed job from its stored source. */
|
|
427
384
|
retry(recordingId) {
|
|
428
385
|
return this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/job/retry`);
|
|
429
386
|
}
|
|
430
|
-
/** The file the server fetched for a link job waiting in `cropping`. */
|
|
431
387
|
preview(recordingId) {
|
|
432
388
|
return this.#t.raw('GET', `/v1/recordings/${seg(recordingId)}/job/preview`, { headers: { accept: '*/*' } });
|
|
433
389
|
}
|
|
434
|
-
/** Poll until the job is `done`, `failed` or `cropping`, and return it.
|
|
435
|
-
* It does not throw on `failed`: check `job.state` (and `job.error`).
|
|
436
|
-
* `onProgress` sees every job read, the last one included: its `state`
|
|
437
|
-
* and `progress` (`pct`, `processedS`, the fetch phase). */
|
|
438
390
|
async wait(recordingId, opts = {}) {
|
|
439
391
|
const interval = opts.intervalMs ?? 2000;
|
|
440
392
|
const deadline = opts.timeoutMs ? Date.now() + opts.timeoutMs : Infinity;
|
|
@@ -451,29 +403,24 @@ export class Jobs {
|
|
|
451
403
|
}
|
|
452
404
|
}
|
|
453
405
|
}
|
|
454
|
-
/** `vs.corrections`: edits to a finished transcript, each revertible. */
|
|
455
406
|
export class Corrections {
|
|
456
407
|
#t;
|
|
457
408
|
constructor(t) {
|
|
458
409
|
this.#t = t;
|
|
459
410
|
}
|
|
460
|
-
/** Every correction, newest first (every page of `listPage`). */
|
|
461
411
|
list(recordingId) {
|
|
462
412
|
return allPages(async (cursor) => {
|
|
463
413
|
const r = await this.listPage(recordingId, { cursor });
|
|
464
414
|
return { items: r.corrections, nextCursor: r.nextCursor };
|
|
465
415
|
});
|
|
466
416
|
}
|
|
467
|
-
/** One page of corrections, newest first: pass its `nextCursor` as `cursor` for the next. */
|
|
468
417
|
listPage(recordingId, opts = {}) {
|
|
469
418
|
return this.#t.json('GET', `/v1/recordings/${seg(recordingId)}/corrections`, { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
470
419
|
}
|
|
471
|
-
/** Apply a staged batch atomically; every target must be settled. */
|
|
472
420
|
async apply(recordingId, batch) {
|
|
473
421
|
const r = await this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/corrections`, { body: batch });
|
|
474
422
|
return r.corrections ?? [];
|
|
475
423
|
}
|
|
476
|
-
/** Read lines again with the language forced; lands as `line` corrections. */
|
|
477
424
|
async retranscribe(recordingId, req) {
|
|
478
425
|
const r = await this.#t.json('POST', `/v1/recordings/${seg(recordingId)}/retranscribe`, { body: req });
|
|
479
426
|
return r.corrections ?? [];
|
|
@@ -482,13 +429,14 @@ export class Corrections {
|
|
|
482
429
|
return this.#t.json('POST', `/v1/corrections/${seg(correctionId)}/revert`);
|
|
483
430
|
}
|
|
484
431
|
}
|
|
485
|
-
/** `vs.shareLinks`: named public links to a transcription or broadcast
|
|
486
|
-
* (create and list them on their owner). */
|
|
487
432
|
export class ShareLinks {
|
|
488
433
|
#t;
|
|
489
434
|
constructor(t) {
|
|
490
435
|
this.#t = t;
|
|
491
436
|
}
|
|
437
|
+
open(code) {
|
|
438
|
+
return this.#t.json('GET', `/v1/shares/${seg(code)}`, { anonymous: true });
|
|
439
|
+
}
|
|
492
440
|
update(id, req) {
|
|
493
441
|
return this.#t.json('PATCH', `/v1/share-links/${seg(id)}`, { body: req });
|
|
494
442
|
}
|
|
@@ -496,22 +444,55 @@ export class ShareLinks {
|
|
|
496
444
|
return this.#t.json('DELETE', `/v1/share-links/${seg(id)}`);
|
|
497
445
|
}
|
|
498
446
|
}
|
|
499
|
-
|
|
500
|
-
export class Voices {
|
|
447
|
+
export class Tags {
|
|
501
448
|
#t;
|
|
502
449
|
constructor(t) {
|
|
503
450
|
this.#t = t;
|
|
504
451
|
}
|
|
505
452
|
async list() {
|
|
506
|
-
const r = await this.#t.json('GET', '/v1/
|
|
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 } });
|
|
507
476
|
return r.voices ?? [];
|
|
508
477
|
}
|
|
509
478
|
get(id) {
|
|
510
479
|
return this.#t.json('GET', `/v1/voices/${seg(id)}`);
|
|
511
480
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
+
}
|
|
515
496
|
async create(req, call = {}) {
|
|
516
497
|
const { sample, fileName, ...meta } = req;
|
|
517
498
|
const form = new FormData();
|
|
@@ -522,34 +503,27 @@ export class Voices {
|
|
|
522
503
|
}
|
|
523
504
|
return this.#t.json('POST', '/v1/voices', { body: form, ...call });
|
|
524
505
|
}
|
|
525
|
-
/** Build a voice from one speaker of a recording (asserts permission). */
|
|
526
506
|
createFromSpeaker(req, call = {}) {
|
|
527
507
|
return this.#t.json('POST', '/v1/voices/from-speaker', { body: { consent: true, ...req }, ...call });
|
|
528
508
|
}
|
|
529
|
-
|
|
509
|
+
setConsent(id, consent, opts = {}) {
|
|
510
|
+
return this.#t.json('POST', `/v1/voices/${seg(id)}/consent`, { body: { consent, ...opts } });
|
|
511
|
+
}
|
|
530
512
|
update(id, patch) {
|
|
531
513
|
return this.#t.json('PATCH', `/v1/voices/${seg(id)}`, { body: patch });
|
|
532
514
|
}
|
|
533
515
|
async delete(id) {
|
|
534
516
|
await this.#t.json('DELETE', `/v1/voices/${seg(id)}`);
|
|
535
517
|
}
|
|
536
|
-
/** The voices that can speak `lang`, best first (with a token, also the
|
|
537
|
-
* account's ready clones). `recording` also offers the cloned voices that
|
|
538
|
-
* recording uses, to whoever may read it: its owner, or a listener with
|
|
539
|
-
* `listenerToken` or `shareCode`. */
|
|
540
518
|
available(lang, opts = {}) {
|
|
541
519
|
return this.#t.json('GET', '/v1/voices/available', {
|
|
542
520
|
query: { ...listenerQuery(opts), lang, recording: opts.recording },
|
|
543
521
|
anonymous: opts.asOwner === false,
|
|
544
522
|
});
|
|
545
523
|
}
|
|
546
|
-
/** A catalogue voice speaking `lang` (Ogg/Opus; `caf` for Apple). 202 while it is prepared. */
|
|
547
524
|
preview(voice, lang, opts = {}) {
|
|
548
525
|
return this.#t.raw('GET', '/v1/voices/preview', { headers: { accept: '*/*' }, query: { voice, lang, format: opts.format } });
|
|
549
526
|
}
|
|
550
|
-
/** A cloned voice speaking `lang`; 409 while it is not ready. With
|
|
551
|
-
* `recording`, as it speaks in that recording, for whoever may read it
|
|
552
|
-
* (its owner, or a listener with `listenerToken` or `shareCode`). */
|
|
553
527
|
clonePreview(id, opts = {}) {
|
|
554
528
|
return this.#t.raw('GET', `/v1/voices/${seg(id)}/preview`, {
|
|
555
529
|
headers: { accept: '*/*' },
|
|
@@ -557,18 +531,15 @@ export class Voices {
|
|
|
557
531
|
anonymous: opts.asOwner === false,
|
|
558
532
|
});
|
|
559
533
|
}
|
|
560
|
-
/** The audio a voice was made from. */
|
|
561
534
|
sample(id) {
|
|
562
535
|
return this.#t.raw('GET', `/v1/voices/${seg(id)}/sample`, { headers: { accept: '*/*' } });
|
|
563
536
|
}
|
|
564
537
|
}
|
|
565
|
-
/** `vs.search`: full-text search over the caller's transcripts. */
|
|
566
538
|
export class Search {
|
|
567
539
|
#t;
|
|
568
540
|
constructor(t) {
|
|
569
541
|
this.#t = t;
|
|
570
542
|
}
|
|
571
|
-
/** `q` is 2 or more characters. */
|
|
572
543
|
query(q, opts = {}) {
|
|
573
544
|
const query = {
|
|
574
545
|
q,
|
|
@@ -581,7 +552,6 @@ export class Search {
|
|
|
581
552
|
return this.#t.json('GET', '/v1/search', { query });
|
|
582
553
|
}
|
|
583
554
|
}
|
|
584
|
-
/** `vs.me`: who the credential is. */
|
|
585
555
|
export class MeApi {
|
|
586
556
|
#t;
|
|
587
557
|
constructor(t) {
|
|
@@ -591,18 +561,11 @@ export class MeApi {
|
|
|
591
561
|
return this.#t.json('GET', '/v1/me');
|
|
592
562
|
}
|
|
593
563
|
}
|
|
594
|
-
/** `vs.connect`: one-click Connect, on the app's server. */
|
|
595
564
|
export class Connect {
|
|
596
565
|
#t;
|
|
597
566
|
constructor(t) {
|
|
598
567
|
this.#t = t;
|
|
599
568
|
}
|
|
600
|
-
/** Redeem the `code` the dashboard sent to the app's `redirect_uri` for
|
|
601
|
-
* an API key (OAuth 2.0 authorization code + PKCE). No credential is
|
|
602
|
-
* sent, even when the client has one, and the call is never retried:
|
|
603
|
-
* the code works once. A refusal is a `VocalStackError` whose `code` is
|
|
604
|
-
* OAuth's `error` (`invalid_grant`: unknown, spent or expired code, or a
|
|
605
|
-
* verifier or `redirect_uri` that does not match). */
|
|
606
569
|
token(input, opts = {}) {
|
|
607
570
|
const body = new URLSearchParams({
|
|
608
571
|
grant_type: 'authorization_code',
|
|
@@ -613,165 +576,125 @@ export class Connect {
|
|
|
613
576
|
return this.#t.json('POST', '/v1/connect/token', { body, anonymous: true, noRetry: true, oauthErrors: true, signal: opts.signal });
|
|
614
577
|
}
|
|
615
578
|
}
|
|
616
|
-
/** `vs.billing`: the account's plan, credit balance and limits, the price
|
|
617
|
-
* list, the ledger, the per-recording charges, the usage report, the
|
|
618
|
-
* payments history, payment methods, invoice details and promo codes. */
|
|
619
579
|
export class Billing {
|
|
620
580
|
#t;
|
|
621
581
|
constructor(t) {
|
|
622
582
|
this.#t = t;
|
|
623
583
|
}
|
|
624
|
-
/** The account: plan, rates, balance, limits, subscription, top-up URL. */
|
|
625
584
|
get() {
|
|
626
585
|
return this.#t.json('GET', '/v1/billing');
|
|
627
586
|
}
|
|
628
|
-
/** The visible plans with their current rates and limits (no credential needed). */
|
|
629
587
|
plans() {
|
|
630
588
|
return this.#t.json('GET', '/v1/billing/plans');
|
|
631
589
|
}
|
|
632
|
-
/** The ledger, newest first. */
|
|
633
590
|
ledger(opts = {}) {
|
|
634
591
|
return this.#t.json('GET', '/v1/billing/ledger', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
635
592
|
}
|
|
636
|
-
/** The per-recording charges, newest first. */
|
|
637
593
|
charges(opts = {}) {
|
|
638
594
|
return this.#t.json('GET', '/v1/billing/charges', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
639
595
|
}
|
|
640
|
-
/** Open a checkout at the payment provider (a top-up or a subscription); send the customer to `url`.
|
|
641
|
-
* A signed-in person only, never an API key. */
|
|
642
596
|
createCheckout(req, opts = {}) {
|
|
643
597
|
return this.#t.json('POST', '/v1/billing/checkouts', { body: req, ...opts });
|
|
644
598
|
}
|
|
645
|
-
/** A checkout's status (by its id or the provider's). */
|
|
646
599
|
getCheckout(id) {
|
|
647
600
|
return this.#t.json('GET', `/v1/billing/checkouts/${seg(id)}`);
|
|
648
601
|
}
|
|
649
|
-
/** Cancel the subscription at the end of its period. */
|
|
650
602
|
cancelSubscription() {
|
|
651
603
|
return this.#t.json('POST', '/v1/billing/subscription/cancel');
|
|
652
604
|
}
|
|
653
|
-
/** Take back a scheduled cancellation. */
|
|
654
605
|
uncancelSubscription() {
|
|
655
606
|
return this.#t.json('POST', '/v1/billing/subscription/uncancel');
|
|
656
607
|
}
|
|
657
|
-
/** Turn auto top-up on (threshold and amount from `plans().autoTopUp`) or off. */
|
|
658
608
|
setAutoTopUp(req) {
|
|
659
609
|
return this.#t.json('PUT', '/v1/billing/auto-top-up', { body: req });
|
|
660
610
|
}
|
|
661
|
-
/** A link to the provider's customer portal (payment methods, invoices, receipts). */
|
|
662
611
|
portal() {
|
|
663
612
|
return this.#t.json('GET', '/v1/billing/portal');
|
|
664
613
|
}
|
|
665
|
-
/** Charge the configured auto top-up amount now (under the day's cap). */
|
|
666
614
|
retryAutoTopUp() {
|
|
667
615
|
return this.#t.json('POST', '/v1/billing/auto-top-up/retry');
|
|
668
616
|
}
|
|
669
|
-
/** Turn the low-balance email on or off and set its line. */
|
|
670
617
|
setLowBalanceAlert(req) {
|
|
671
618
|
return this.#t.json('PUT', '/v1/billing/low-balance-alert', { body: req });
|
|
672
619
|
}
|
|
673
|
-
/** Renew `month`ly or `year`ly from the next period, at `subscription.switchPrices` (needs `acceptTerms` and `noNewWithdrawalPeriodShown`). */
|
|
674
620
|
setInterval(req) {
|
|
675
621
|
return this.#t.json('POST', '/v1/billing/subscription/interval', { body: req });
|
|
676
622
|
}
|
|
677
|
-
/** Keep the subscription by switching to yearly when monthly billing is withdrawn (`subscription.yearlyOptIn`). */
|
|
678
623
|
optInYearly(req) {
|
|
679
624
|
return this.#t.json('POST', '/v1/billing/subscription/yearly-opt-in', { body: req });
|
|
680
625
|
}
|
|
681
|
-
/** Take back a scheduled interval switch. */
|
|
682
626
|
unsetInterval() {
|
|
683
627
|
return this.#t.json('DELETE', '/v1/billing/subscription/interval');
|
|
684
628
|
}
|
|
685
|
-
/** What the account spent per day, source (API key or Dashboard) and kind. */
|
|
686
629
|
usage(opts = {}) {
|
|
687
630
|
return this.#t.json('GET', '/v1/billing/usage', { query: { startDate: opts.startDate, endDate: opts.endDate, tz: opts.tz } });
|
|
688
631
|
}
|
|
689
|
-
/** The usage report's charges one by one, newest first, with how many match and their total. */
|
|
690
632
|
usageCharges(opts = {}) {
|
|
691
633
|
const query = { startDate: opts.startDate, endDate: opts.endDate, tz: opts.tz, kind: opts.kind, source: opts.source, cursor: opts.cursor, limit: opts.limit };
|
|
692
634
|
return this.#t.json('GET', '/v1/billing/usage/charges', { query });
|
|
693
635
|
}
|
|
694
|
-
/** The payments history, newest first. */
|
|
695
636
|
payments(opts = {}) {
|
|
696
637
|
return this.#t.json('GET', '/v1/billing/payments', { query: { cursor: opts.cursor, limit: opts.limit } });
|
|
697
638
|
}
|
|
698
|
-
/** A payment's invoice or receipt: `{url}`, or `{pending: true}` while the provider makes it. */
|
|
699
639
|
paymentDocument(id) {
|
|
700
640
|
return this.#t.json('GET', `/v1/billing/payments/${seg(id)}/document`);
|
|
701
641
|
}
|
|
702
|
-
/** The saved payment methods. */
|
|
703
642
|
paymentMethods() {
|
|
704
643
|
return this.#t.json('GET', '/v1/billing/payment-methods');
|
|
705
644
|
}
|
|
706
|
-
/** Remove a saved payment method. */
|
|
707
645
|
async deletePaymentMethod(id) {
|
|
708
646
|
await this.#t.json('DELETE', `/v1/billing/payment-methods/${seg(id)}`);
|
|
709
647
|
}
|
|
710
|
-
/** A link to the provider's portal to add a card or choose the default. */
|
|
711
648
|
managePaymentMethods() {
|
|
712
649
|
return this.#t.json('POST', '/v1/billing/payment-methods/manage');
|
|
713
650
|
}
|
|
714
|
-
/** The invoice details. */
|
|
715
651
|
details() {
|
|
716
652
|
return this.#t.json('GET', '/v1/billing/details');
|
|
717
653
|
}
|
|
718
|
-
/** Save the invoice details. */
|
|
719
654
|
setDetails(req) {
|
|
720
655
|
return this.#t.json('PUT', '/v1/billing/details', { body: req });
|
|
721
656
|
}
|
|
722
|
-
/** What a promo code gives for a top-up or a subscription. */
|
|
723
657
|
checkPromoCode(req) {
|
|
724
658
|
return this.#t.json('POST', '/v1/billing/promo-codes/check', { body: req });
|
|
725
659
|
}
|
|
726
660
|
}
|
|
727
|
-
/** `vs.languages`. */
|
|
728
661
|
export class Languages {
|
|
729
662
|
#t;
|
|
730
663
|
constructor(t) {
|
|
731
664
|
this.#t = t;
|
|
732
665
|
}
|
|
733
|
-
/** Spoken and translation languages. */
|
|
734
666
|
list() {
|
|
735
667
|
return this.#t.json('GET', '/v1/languages');
|
|
736
668
|
}
|
|
737
669
|
}
|
|
738
|
-
/** `vs.rtmp`. */
|
|
739
670
|
export class Rtmp {
|
|
740
671
|
#t;
|
|
741
672
|
constructor(t) {
|
|
742
673
|
this.#t = t;
|
|
743
674
|
}
|
|
744
|
-
/** The account's RTMP ingest: server URL (RTMPS where available), the stream key (in full only in the answer that made it, `maskedStreamKey` after), and whether an encoder is sending. */
|
|
745
675
|
status() {
|
|
746
676
|
return this.#t.json('GET', '/v1/rtmp');
|
|
747
677
|
}
|
|
748
|
-
/** A stream key of the app's own, beside the account's: `streamKey` is in full in this answer only. It works while the API key that made it does. */
|
|
749
678
|
createKey(req) {
|
|
750
679
|
return this.#t.json('POST', '/v1/rtmp/keys', { body: req });
|
|
751
680
|
}
|
|
752
|
-
/** The account's app stream keys, oldest first: masked, each with whether an encoder is sending on it (`live`, `audio`). */
|
|
753
681
|
async listKeys() {
|
|
754
682
|
const r = await this.#t.json('GET', '/v1/rtmp/keys');
|
|
755
683
|
return r.keys ?? [];
|
|
756
684
|
}
|
|
757
|
-
/** Delete an app stream key: an encoder publishing on it is dropped within seconds. */
|
|
758
685
|
async deleteKey(id) {
|
|
759
686
|
await this.#t.json('DELETE', `/v1/rtmp/keys/${seg(id)}`);
|
|
760
687
|
}
|
|
761
688
|
}
|
|
762
|
-
/** `vs.events`: the account socket. */
|
|
763
689
|
export class Events {
|
|
764
690
|
#t;
|
|
765
691
|
constructor(t) {
|
|
766
692
|
this.#t = t;
|
|
767
693
|
}
|
|
768
|
-
/** `events.ready`, then `lists.changed` whenever one of the owner's
|
|
769
|
-
* lists of broadcasts or transcriptions is out of date: re-read them. */
|
|
770
694
|
async account() {
|
|
771
695
|
return openWebSocket(this.#t.wsUrl('/v1/events'), await this.#t.bearer());
|
|
772
696
|
}
|
|
773
697
|
}
|
|
774
|
-
/** `vs.meta`. */
|
|
775
698
|
export class Meta {
|
|
776
699
|
#t;
|
|
777
700
|
constructor(t) {
|
|
@@ -780,8 +703,10 @@ export class Meta {
|
|
|
780
703
|
health() {
|
|
781
704
|
return this.#t.json('GET', '/v1/health');
|
|
782
705
|
}
|
|
783
|
-
/** The OpenAPI document this SDK is generated from. */
|
|
784
706
|
openapi() {
|
|
785
707
|
return this.#t.json('GET', '/v1/openapi.json');
|
|
786
708
|
}
|
|
709
|
+
clientPolicy(client) {
|
|
710
|
+
return this.#t.json('GET', `/v1/clients/${encodeURIComponent(client)}`);
|
|
711
|
+
}
|
|
787
712
|
}
|