@omelhorsite/sdk 0.2.0 → 0.4.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/README.md +112 -130
- package/dist/index.js +4800 -610
- package/dist/types/client.d.ts +60 -3
- package/dist/types/http.d.ts +444 -19
- package/dist/types/index.d.ts +5 -1
- package/dist/types/internal/attachments.d.ts +17 -0
- package/dist/types/internal/helpers.d.ts +9 -0
- package/dist/types/listing.d.ts +90 -0
- package/dist/types/resources/account.d.ts +71 -5
- package/dist/types/resources/admin/authorizedApplications.d.ts +133 -0
- package/dist/types/resources/admin/chests.d.ts +51 -0
- package/dist/types/resources/admin/eventAlerts.d.ts +60 -0
- package/dist/types/resources/admin/identities.d.ts +99 -0
- package/dist/types/resources/admin/index.d.ts +77 -0
- package/dist/types/resources/admin/jobs.d.ts +97 -0
- package/dist/types/resources/admin/myOauthApplications.d.ts +282 -0
- package/dist/types/resources/admin/notepads.d.ts +49 -0
- package/dist/types/resources/admin/oauthApplications.d.ts +309 -0
- package/dist/types/resources/admin/quotas.d.ts +114 -0
- package/dist/types/resources/admin/shortLinks.d.ts +201 -0
- package/dist/types/resources/admin/types.d.ts +222 -0
- package/dist/types/resources/admin/vocalSeparations.d.ts +98 -0
- package/dist/types/resources/admin.d.ts +1841 -0
- package/dist/types/resources/auth/index.d.ts +39 -0
- package/dist/types/resources/auth/passkeys.d.ts +652 -0
- package/dist/types/resources/auth/sessions.d.ts +844 -0
- package/dist/types/resources/chests.d.ts +54 -3
- package/dist/types/resources/content/analysis.d.ts +62 -0
- package/dist/types/resources/content/blogs.d.ts +471 -0
- package/dist/types/resources/content/feedbacks.d.ts +263 -0
- package/dist/types/resources/content/index.d.ts +52 -0
- package/dist/types/resources/content/intel/articles.d.ts +230 -0
- package/dist/types/resources/content/intel/config.d.ts +135 -0
- package/dist/types/resources/content/intel/index.d.ts +53 -0
- package/dist/types/resources/content/intel/items.d.ts +91 -0
- package/dist/types/resources/content/intel/reports.d.ts +108 -0
- package/dist/types/resources/content/intel/scripts.d.ts +152 -0
- package/dist/types/resources/content/intel/sources.d.ts +236 -0
- package/dist/types/resources/content/intel/stats.d.ts +105 -0
- package/dist/types/resources/content/intel/types.d.ts +86 -0
- package/dist/types/resources/content/jokes.d.ts +93 -0
- package/dist/types/resources/content/notifications.d.ts +147 -0
- package/dist/types/resources/content/serviceUsages.d.ts +73 -0
- package/dist/types/resources/content/servicesStatus.d.ts +194 -0
- package/dist/types/resources/content/siteConfig.d.ts +43 -0
- package/dist/types/resources/content/spaceInvaders.d.ts +154 -0
- package/dist/types/resources/content.d.ts +2775 -0
- package/dist/types/resources/dynamicQrs.d.ts +39 -3
- package/dist/types/resources/forms.d.ts +176 -35
- package/dist/types/resources/index.d.ts +19 -8
- package/dist/types/resources/ipLookup.d.ts +20 -4
- package/dist/types/resources/jobs.d.ts +69 -25
- package/dist/types/resources/library/annotations.d.ts +162 -0
- package/dist/types/resources/library/books.d.ts +534 -0
- package/dist/types/resources/library/chat.d.ts +259 -0
- package/dist/types/resources/library/index.d.ts +39 -0
- package/dist/types/resources/library/shelves.d.ts +237 -0
- package/dist/types/resources/library/types.d.ts +120 -0
- package/dist/types/resources/library.d.ts +1433 -0
- package/dist/types/resources/linkTrees.d.ts +142 -30
- package/dist/types/resources/media.d.ts +351 -0
- package/dist/types/resources/movies/addons.d.ts +388 -0
- package/dist/types/resources/movies/collections.d.ts +352 -0
- package/dist/types/resources/movies/index.d.ts +29 -0
- package/dist/types/resources/movies/types.d.ts +50 -0
- package/dist/types/resources/movies/watchProgress.d.ts +277 -0
- package/dist/types/resources/movies.d.ts +1160 -0
- package/dist/types/resources/music/artists.d.ts +1068 -0
- package/dist/types/resources/music/imports.d.ts +941 -0
- package/dist/types/resources/music/index.d.ts +61 -0
- package/dist/types/resources/music/playlists.d.ts +1027 -0
- package/dist/types/resources/music/social.d.ts +1132 -0
- package/dist/types/resources/music/songs.d.ts +1167 -0
- package/dist/types/resources/notepads.d.ts +4 -1
- package/dist/types/resources/quotas.d.ts +7 -1
- package/dist/types/resources/realtime.d.ts +855 -0
- package/dist/types/resources/shortLinks.d.ts +49 -5
- package/dist/types/resources/social/groupChats.d.ts +458 -0
- package/dist/types/resources/social/index.d.ts +19 -0
- package/dist/types/resources/social/messages.d.ts +424 -0
- package/dist/types/resources/social/relationships.d.ts +322 -0
- package/dist/types/resources/social/types.d.ts +37 -0
- package/dist/types/resources/social.d.ts +1348 -0
- package/dist/types/resources/storage/upload.d.ts +158 -11
- package/dist/types/resources/storage.d.ts +111 -27
- package/dist/types/resources/tickets.d.ts +101 -13
- package/dist/types/resources/tools/backgroundRemoval.d.ts +18 -3
- package/dist/types/resources/tools/captions.d.ts +448 -21
- package/dist/types/resources/tools/downloader.d.ts +21 -0
- package/dist/types/resources/tools/index.d.ts +57 -15
- package/dist/types/resources/tools/jumpstyle.d.ts +50 -17
- package/dist/types/resources/tools/transcription.d.ts +35 -13
- package/dist/types/resources/tools/upscale.d.ts +23 -3
- package/dist/types/resources/tools/vocalSeparation.d.ts +30 -13
- package/dist/types/types.d.ts +249 -17
- package/package.json +2 -1
|
@@ -37,9 +37,31 @@
|
|
|
37
37
|
* Read {@link CaptionsNamespace.quota} before uploading, and again once the
|
|
38
38
|
* window is known: the second check is the exact one, and the server refuses a
|
|
39
39
|
* window that would cross the ceiling rather than truncating it.
|
|
40
|
+
*
|
|
41
|
+
* **The 250 MiB ceiling is not reachable through {@link CaptionsNamespace.create}.**
|
|
42
|
+
* Cloudflare rejects any request body over 100 MB with a 413 of its own before
|
|
43
|
+
* Rails ever sees it, so the backend also exposes a three-call chunked upload -
|
|
44
|
+
* `POST /caption_jobs/uploads` opens a session and returns a signed
|
|
45
|
+
* `upload_token` plus a `part_size` (32 MiB), `POST /caption_jobs/uploads/parts?offset=`
|
|
46
|
+
* streams each raw part under an `X-Upload-Token` header, and
|
|
47
|
+
* `POST /caption_jobs/uploads/finish` probes the assembled file and creates the
|
|
48
|
+
* row. {@link CaptionsNamespace.upload} drives whichever of the two a given
|
|
49
|
+
* file needs, so step 1 is one call again: under
|
|
50
|
+
* {@link CAPTION_CHUNKED_THRESHOLD} it IS `create`, above it it is
|
|
51
|
+
* {@link CaptionsNamespace.createChunked}, and both answer the same
|
|
52
|
+
* `"uploaded"` row. Reach for `create` directly only when the file is known to
|
|
53
|
+
* be small, and for the three methods under `upload` only to drive the parts
|
|
54
|
+
* yourself.
|
|
55
|
+
*
|
|
56
|
+
* One caveat that is the backend's and not the SDK's: the three chunked routes
|
|
57
|
+
* carry no `oauth_scope` declaration, and scopes there are default-deny, so an
|
|
58
|
+
* OAuth access token gets `403 insufficient_scope` on all three (and on
|
|
59
|
+
* `fonts`). A session or a first-party bearer token reaches them; a token
|
|
60
|
+
* minted through the OIDC provider does not, and for one of those only
|
|
61
|
+
* `create` is open.
|
|
40
62
|
*/
|
|
41
63
|
import { Resource } from "../../http";
|
|
42
|
-
import type { FileInput, Id, Progress, RequestOptions } from "../../types";
|
|
64
|
+
import type { FileInput, Id, OperationOptions, Progress, RequestOptions } from "../../types";
|
|
43
65
|
import { type SecondsQuota, type ToolCaptcha, type ToolRecord, type ToolRunOptions } from "./index";
|
|
44
66
|
/**
|
|
45
67
|
* Lifecycle of a caption job. Wider than {@link ToolStatus} because the job
|
|
@@ -76,6 +98,13 @@ export interface CaptionWord {
|
|
|
76
98
|
/** Seconds from the video start. Must be `>= t0`. */
|
|
77
99
|
readonly t1: number;
|
|
78
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* An RGB triple, each channel clamped to `[0, 255]` server-side.
|
|
103
|
+
*
|
|
104
|
+
* The controller reads the key only when the array has EXACTLY three entries;
|
|
105
|
+
* two or four is dropped in silence, which costs a whole render to discover.
|
|
106
|
+
*/
|
|
107
|
+
export type CaptionRgb = readonly [number, number, number] | readonly number[];
|
|
79
108
|
/**
|
|
80
109
|
* Look of the burned-in captions.
|
|
81
110
|
*
|
|
@@ -89,8 +118,22 @@ export interface CaptionWord {
|
|
|
89
118
|
* back as the nearest end of it rather than as a 400.
|
|
90
119
|
*/
|
|
91
120
|
export interface CaptionStyle {
|
|
121
|
+
/**
|
|
122
|
+
* A font KEY from {@link CaptionsNamespace.fonts}, never a path: the sidecar
|
|
123
|
+
* resolves the key against the faces installed in its image, so a caller
|
|
124
|
+
* cannot point the renderer at a file. Must match `/\A[a-z0-9_-]{1,40}\z/`;
|
|
125
|
+
* anything else is dropped and the renderer uses its own default.
|
|
126
|
+
*/
|
|
127
|
+
readonly font?: string;
|
|
92
128
|
/** Font size as a fraction of the video height. Clamped to `[0.03, 0.09]`. */
|
|
93
129
|
readonly fontscale?: number;
|
|
130
|
+
/**
|
|
131
|
+
* Outline thickness as a fraction of the font size. Clamped to `[0, 0.3]`,
|
|
132
|
+
* and `0` is a legitimate choice meaning no outline at all - which is why
|
|
133
|
+
* the controller reads this key whenever it is PRESENT rather than when it
|
|
134
|
+
* is truthy, unlike every other numeric key here.
|
|
135
|
+
*/
|
|
136
|
+
readonly stroke_factor?: number;
|
|
94
137
|
/** Vertical placement, `0` top to `1` bottom. Clamped to `[0.3, 0.9]`. */
|
|
95
138
|
readonly pos?: number;
|
|
96
139
|
/** Words on screen at once. Clamped to `[1, 6]`. */
|
|
@@ -103,32 +146,64 @@ export interface CaptionStyle {
|
|
|
103
146
|
readonly preset?: "veryfast" | "medium" | "slow";
|
|
104
147
|
/**
|
|
105
148
|
* Colour of the word currently being sung, as `[r, g, b]`, each clamped to
|
|
106
|
-
* `[0, 255]`.
|
|
149
|
+
* `[0, 255]`. Named after its default rather than after its job. Ignored
|
|
150
|
+
* unless the array has exactly three entries.
|
|
107
151
|
*/
|
|
108
|
-
readonly yellow?:
|
|
152
|
+
readonly yellow?: CaptionRgb;
|
|
153
|
+
/** Colour of the words that are not highlighted. Same three-entry rule. */
|
|
154
|
+
readonly white?: CaptionRgb;
|
|
155
|
+
/** Colour of the outline. Same three-entry rule. */
|
|
156
|
+
readonly stroke?: CaptionRgb;
|
|
109
157
|
readonly [key: string]: unknown;
|
|
110
158
|
}
|
|
111
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* A caption job.
|
|
161
|
+
*
|
|
162
|
+
* Every route that answers with one - create, the chunked upload's finish
|
|
163
|
+
* call, show, transcribe and render - renders the `:extended` view, so every
|
|
164
|
+
* key below is present on every response.
|
|
165
|
+
*
|
|
166
|
+
* Five of the columns are `NOT NULL` with a numeric default, so `width`,
|
|
167
|
+
* `height`, `fps`, `duration` and `transcribed_seconds` are always real
|
|
168
|
+
* numbers. On a row whose upload has only just been probed they are the probed
|
|
169
|
+
* values; the sidecar's precise metadata overwrites them a moment later.
|
|
170
|
+
*/
|
|
112
171
|
export interface CaptionJob extends ToolRecord {
|
|
113
172
|
readonly status: CaptionStatus;
|
|
173
|
+
/** Original upload name. The controller substitutes `"video.mp4"` for a
|
|
174
|
+
* blank one, so this is never empty in practice. */
|
|
114
175
|
readonly filename: string;
|
|
115
|
-
|
|
116
|
-
readonly
|
|
117
|
-
readonly
|
|
118
|
-
/**
|
|
119
|
-
readonly
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
readonly
|
|
124
|
-
/**
|
|
125
|
-
readonly
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
readonly
|
|
176
|
+
/** Pixels. `NOT NULL DEFAULT 0`, so `0` means "not probed yet", not unknown. */
|
|
177
|
+
readonly width: number;
|
|
178
|
+
readonly height: number;
|
|
179
|
+
/** `NOT NULL DEFAULT 0.0`. */
|
|
180
|
+
readonly fps: number;
|
|
181
|
+
/** Seconds of video, probed at upload. `NOT NULL DEFAULT 0.0`. */
|
|
182
|
+
readonly duration: number;
|
|
183
|
+
/** Language of the last transcribe call, `"auto"` included. `null` before the first. */
|
|
184
|
+
readonly language: string | null;
|
|
185
|
+
/** The transcribed window, seconds from the video start. `null` before the first transcribe. */
|
|
186
|
+
readonly window_start: number | null;
|
|
187
|
+
readonly window_end: number | null;
|
|
188
|
+
/**
|
|
189
|
+
* Seconds charged against the quota so far, accumulated across every window
|
|
190
|
+
* transcribed on this job. `NOT NULL DEFAULT 0`.
|
|
191
|
+
*/
|
|
192
|
+
readonly transcribed_seconds: number;
|
|
193
|
+
/**
|
|
194
|
+
* Timed words, from `"transcribed"` onwards.
|
|
195
|
+
*
|
|
196
|
+
* `null` and not `[]` when there are none: the column is `NOT NULL DEFAULT
|
|
197
|
+
* '[]'`, but the blueprint renders `words.presence`, and `[].presence` is
|
|
198
|
+
* `nil` in Ruby. So an empty list arrives as `null`, and the key is present
|
|
199
|
+
* either way.
|
|
200
|
+
*/
|
|
201
|
+
readonly words: CaptionWord[] | null;
|
|
202
|
+
/** What the renderer is doing right now, read live off the sidecar. `null`
|
|
203
|
+
* unless the status is `"rendering"`. */
|
|
204
|
+
readonly render_stage: string | null;
|
|
205
|
+
/** Signed URL of the finished video once complete and attached, `null` otherwise. */
|
|
206
|
+
readonly output_url: string | null;
|
|
132
207
|
}
|
|
133
208
|
/** Arguments for uploading a video. */
|
|
134
209
|
export interface CreateCaptionJobInput extends ToolCaptcha {
|
|
@@ -168,6 +243,190 @@ export interface RenderCaptionInput {
|
|
|
168
243
|
* nothing.
|
|
169
244
|
*/
|
|
170
245
|
export declare function captionProgress(record: CaptionJob): Progress;
|
|
246
|
+
/**
|
|
247
|
+
* Largest file the backend will accept, chunked or not: 250 MiB exactly.
|
|
248
|
+
*
|
|
249
|
+
* Mirrors `CaptionJobsController::MAX_FILE_SIZE`. Not checked client-side on
|
|
250
|
+
* purpose - the server owns the number, and the chunked path finds out in one
|
|
251
|
+
* cheap round trip because {@link CaptionsNamespace.startUpload} is given the
|
|
252
|
+
* size before a single byte moves.
|
|
253
|
+
*/
|
|
254
|
+
export declare const CAPTION_MAX_UPLOAD_BYTES: number;
|
|
255
|
+
/**
|
|
256
|
+
* Where {@link CaptionsNamespace.upload} stops using one `POST` and starts
|
|
257
|
+
* using the three-call path: 64 MiB.
|
|
258
|
+
*
|
|
259
|
+
* This is a CLIENT-SIDE choice, not a protocol constant, and it is the same
|
|
260
|
+
* one the web tool makes (`frontend/services/CaptionsService.ts`). Cloudflare's
|
|
261
|
+
* ceiling is on the whole request, and a `multipart/form-data` envelope rides
|
|
262
|
+
* on top of the file's own bytes, so the switch sits well under the cap rather
|
|
263
|
+
* than at it. Below it a single request is one round trip and one probe; above
|
|
264
|
+
* it a single request is a 413 that says nothing useful.
|
|
265
|
+
*
|
|
266
|
+
* Override it per call with {@link UploadCaptionVideoInput.chunkedThreshold} -
|
|
267
|
+
* for instance `0`, to exercise the chunked path on a small file.
|
|
268
|
+
*/
|
|
269
|
+
export declare const CAPTION_CHUNKED_THRESHOLD: number;
|
|
270
|
+
/**
|
|
271
|
+
* Part size, 32 MiB. The same number as the storage tier's, and by coincidence
|
|
272
|
+
* rather than by sharing: `CaptionJobsController::PART_SIZE` copied it.
|
|
273
|
+
*
|
|
274
|
+
* It is BOTH the size the server reports in the session and the ceiling the
|
|
275
|
+
* server validates each part against, and the controller validates against its
|
|
276
|
+
* own constant rather than against the session it minted. See
|
|
277
|
+
* {@link resolveCaptionPartSize} for what that means when the two disagree.
|
|
278
|
+
*/
|
|
279
|
+
export declare const CAPTION_PART_SIZE: number;
|
|
280
|
+
/**
|
|
281
|
+
* Parts in flight at once by default.
|
|
282
|
+
*
|
|
283
|
+
* Three, as the web tool uses. A single sequential stream leaves a home
|
|
284
|
+
* connection idle between round trips; three fills it without holding an unfair
|
|
285
|
+
* number of the API's threads, and every part in flight parks one Rails thread
|
|
286
|
+
* for as long as it takes to arrive - these bytes pass THROUGH Rails on their
|
|
287
|
+
* way to the sidecar, unlike a storage upload.
|
|
288
|
+
*/
|
|
289
|
+
export declare const CAPTION_UPLOAD_CONCURRENCY = 3;
|
|
290
|
+
/**
|
|
291
|
+
* How long an upload session stays usable: six hours, from
|
|
292
|
+
* `CaptionJobsController::UPLOAD_TOKEN_TTL`.
|
|
293
|
+
*
|
|
294
|
+
* The window for a resume. Past it the token verifies as expired and every
|
|
295
|
+
* call answers 400 "Invalid or expired upload session", parts already sent
|
|
296
|
+
* included - there is no way to re-mint a token for a half-uploaded file,
|
|
297
|
+
* because the new session would carry a new job id.
|
|
298
|
+
*/
|
|
299
|
+
export declare const CAPTION_UPLOAD_TOKEN_TTL_MS: number;
|
|
300
|
+
/**
|
|
301
|
+
* What `POST /caption_jobs/uploads` answers.
|
|
302
|
+
*
|
|
303
|
+
* `upload_token` is a CREDENTIAL: a signed `(job_id, size, filename)` triple,
|
|
304
|
+
* and the only thing standing between a stranger and writing bytes into this
|
|
305
|
+
* job. It is not a job id and there is no job yet - the row is created by
|
|
306
|
+
* `finish`, out of the id sealed inside the token - so it cannot be handed to
|
|
307
|
+
* {@link CaptionsNamespace.get} and there is nothing to poll until then.
|
|
308
|
+
*/
|
|
309
|
+
export interface CaptionUploadSession {
|
|
310
|
+
/** Signed session token. Goes in the `X-Upload-Token` header on both later calls. */
|
|
311
|
+
readonly upload_token: string;
|
|
312
|
+
/** Bytes per part, and the ceiling the server validates each part against. */
|
|
313
|
+
readonly part_size: number;
|
|
314
|
+
/** `ceil(size / part_size)`, so a caller can size a progress bar before slicing. */
|
|
315
|
+
readonly part_count: number;
|
|
316
|
+
}
|
|
317
|
+
/** Arguments for opening a chunked upload session. */
|
|
318
|
+
export interface StartCaptionUploadInput extends ToolCaptcha {
|
|
319
|
+
/**
|
|
320
|
+
* EXACT byte length of the file.
|
|
321
|
+
*
|
|
322
|
+
* Not an estimate and not a ceiling: the number is sealed into the token and
|
|
323
|
+
* every part is checked against it, so a declared size below the real one
|
|
324
|
+
* makes the last part fail with 400 "Part exceeds declared size", and a
|
|
325
|
+
* declared size above it leaves the sidecar waiting for bytes that never
|
|
326
|
+
* come and `finish` probing a truncated file.
|
|
327
|
+
*/
|
|
328
|
+
readonly size: number;
|
|
329
|
+
/** Stored name. Trimmed to 255 characters, and `"video.mp4"` when blank. */
|
|
330
|
+
readonly filename?: string;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Arguments for {@link CaptionsNamespace.upload} and
|
|
334
|
+
* {@link CaptionsNamespace.createChunked}.
|
|
335
|
+
*/
|
|
336
|
+
export interface UploadCaptionVideoInput extends ToolCaptcha {
|
|
337
|
+
/**
|
|
338
|
+
* The video.
|
|
339
|
+
*
|
|
340
|
+
* For the chunked path this has to be sliceable, which means real bytes: a
|
|
341
|
+
* `Blob`/`File` on the web or in Bun, a `Uint8Array` anywhere. A React
|
|
342
|
+
* Native `{ uri, name, type }` descriptor is NOT sliceable - see the note on
|
|
343
|
+
* {@link CaptionsNamespace.createChunked}.
|
|
344
|
+
*/
|
|
345
|
+
readonly video: FileInput;
|
|
346
|
+
/** Overrides {@link CAPTION_CHUNKED_THRESHOLD} for this call only. */
|
|
347
|
+
readonly chunkedThreshold?: number;
|
|
348
|
+
/** Overrides {@link CAPTION_UPLOAD_CONCURRENCY} for this call only. */
|
|
349
|
+
readonly concurrency?: number;
|
|
350
|
+
/**
|
|
351
|
+
* Continues an upload that was cut off, rather than starting a new one.
|
|
352
|
+
*
|
|
353
|
+
* Present or not, this is what decides the path: a resume always goes
|
|
354
|
+
* chunked, whatever the file's size and threshold say. The session is only
|
|
355
|
+
* good for {@link CAPTION_UPLOAD_TOKEN_TTL_MS} from when it was minted.
|
|
356
|
+
*/
|
|
357
|
+
readonly resume?: CaptionUploadResume;
|
|
358
|
+
}
|
|
359
|
+
/** A chunked upload picked up where it stopped. */
|
|
360
|
+
export interface CaptionUploadResume {
|
|
361
|
+
/**
|
|
362
|
+
* The session from the interrupted attempt, or just its `upload_token`.
|
|
363
|
+
*
|
|
364
|
+
* Keep the whole session where you can. A bare token has no `part_size`, so
|
|
365
|
+
* the driver slices with {@link CAPTION_PART_SIZE} instead - which is the
|
|
366
|
+
* server's own number today, and therefore the same offsets, but it stops
|
|
367
|
+
* being true the day the backend changes it.
|
|
368
|
+
*/
|
|
369
|
+
readonly session: CaptionUploadSession | string;
|
|
370
|
+
/**
|
|
371
|
+
* Byte offsets already accepted, so their bytes are not sent twice.
|
|
372
|
+
*
|
|
373
|
+
* Offsets, not part numbers, and they must be multiples of the session's
|
|
374
|
+
* `part_size` - they are the same numbers the driver sliced with, which is
|
|
375
|
+
* what {@link UploadCaptionOptions.onPart} reports. Anything else is simply
|
|
376
|
+
* not in the list of offsets to skip and its part is sent again, which costs
|
|
377
|
+
* bandwidth and breaks nothing.
|
|
378
|
+
*
|
|
379
|
+
* Omit it to re-send every part. That is always correct, just slower: a part
|
|
380
|
+
* is written at its own offset, so sending it twice is idempotent.
|
|
381
|
+
*/
|
|
382
|
+
readonly uploaded?: readonly number[];
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Options for the two driving methods: everything a request takes, plus the
|
|
386
|
+
* two callbacks that make a 250 MiB upload watchable.
|
|
387
|
+
*/
|
|
388
|
+
export interface UploadCaptionOptions extends OperationOptions {
|
|
389
|
+
/**
|
|
390
|
+
* Called after each part the server has ACCEPTED, with the offset it was
|
|
391
|
+
* written at and its length.
|
|
392
|
+
*
|
|
393
|
+
* The point of it is resuming: collect these offsets, and hand them back as
|
|
394
|
+
* {@link CaptionUploadResume.uploaded} if the run is cut off.
|
|
395
|
+
*/
|
|
396
|
+
readonly onPart?: (offset: number, length: number) => void;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Byte length of a video about to be uploaded, or `undefined` when it cannot be
|
|
400
|
+
* known without reading it.
|
|
401
|
+
*
|
|
402
|
+
* `undefined` has exactly two causes, and both matter: a `ReadableStream`,
|
|
403
|
+
* which has no length until it is drained, and a React Native picker that
|
|
404
|
+
* reported no `size` for a `content://` URI. Neither can be sized cheaply, so
|
|
405
|
+
* {@link CaptionsNamespace.upload} sends them down the single-request path and
|
|
406
|
+
* lets the server judge - which is the right guess for a phone pick and the
|
|
407
|
+
* wrong one for a 200 MiB stream, so pass `video.size` when you know it.
|
|
408
|
+
*/
|
|
409
|
+
export declare function captionUploadSize(video: FileInput): number | undefined;
|
|
410
|
+
/**
|
|
411
|
+
* The size to slice parts with, from what the session reported.
|
|
412
|
+
*
|
|
413
|
+
* Follows the storage driver's rule - slice with the number the SERVER named,
|
|
414
|
+
* never with the SDK's copy of it - but with a ceiling the storage tier does
|
|
415
|
+
* not need, because the two ends of this protocol read the number from
|
|
416
|
+
* different places. `start_upload` reports `PART_SIZE` from the session it is
|
|
417
|
+
* minting, while `upload_part` validates `length <= PART_SIZE` against the
|
|
418
|
+
* CONTROLLER CONSTANT. They are the same constant today and can only disagree
|
|
419
|
+
* mid-deploy, and the disagreement is one-sided: honouring a larger reported
|
|
420
|
+
* size would make every part a 400 "Invalid part", while capping at the size
|
|
421
|
+
* this SDK knows the validator uses only sends smaller parts, which is always
|
|
422
|
+
* legal. So the reported number wins downwards and loses upwards.
|
|
423
|
+
*
|
|
424
|
+
* A missing, zero or nonsense `part_size` falls back to
|
|
425
|
+
* {@link CAPTION_PART_SIZE}. Note the web tool falls back to its 64 MiB
|
|
426
|
+
* threshold instead, which is above the validator's ceiling - a fallback that
|
|
427
|
+
* would reject every part if it ever fired.
|
|
428
|
+
*/
|
|
429
|
+
export declare function resolveCaptionPartSize(reported: number | undefined): number;
|
|
171
430
|
/** The `captions` tool, reachable as `oms.tools.captions`. */
|
|
172
431
|
export declare class CaptionsNamespace extends Resource {
|
|
173
432
|
/**
|
|
@@ -184,6 +443,24 @@ export declare class CaptionsNamespace extends Resource {
|
|
|
184
443
|
* ceiling rather than truncating it.
|
|
185
444
|
*/
|
|
186
445
|
quota(options?: RequestOptions): Promise<SecondsQuota>;
|
|
446
|
+
/**
|
|
447
|
+
* `GET /caption_jobs/fonts` - the font KEYS the renderer has, for
|
|
448
|
+
* {@link CaptionStyle.font}.
|
|
449
|
+
*
|
|
450
|
+
* Plain strings, not objects and not display names: they are the keys the
|
|
451
|
+
* sidecar resolves against the faces installed in its image, which is the
|
|
452
|
+
* whole reason a caller cannot pass a path.
|
|
453
|
+
*
|
|
454
|
+
* The controller answers `{ fonts: [...] }`; this unwraps it. It also
|
|
455
|
+
* NEVER fails: a sidecar that is down or slow is caught and answered as an
|
|
456
|
+
* empty list with a 200, so `[]` means "could not ask right now" just as
|
|
457
|
+
* much as it means "no fonts", and the two are not distinguishable. Do not
|
|
458
|
+
* treat an empty answer as a reason to refuse a render - omitting `font`
|
|
459
|
+
* lets the renderer use its own default.
|
|
460
|
+
*
|
|
461
|
+
* Cached server-side for ten minutes, so polling it buys nothing.
|
|
462
|
+
*/
|
|
463
|
+
fonts(options?: RequestOptions): Promise<string[]>;
|
|
187
464
|
/**
|
|
188
465
|
* `POST /caption_jobs` - step 1. Uploads the video and probes it.
|
|
189
466
|
*
|
|
@@ -308,6 +585,156 @@ export declare class CaptionsNamespace extends Resource {
|
|
|
308
585
|
* @throws {OmsError} explaining which of the three reasons there is no URL.
|
|
309
586
|
*/
|
|
310
587
|
outputUrl(record: CaptionJob): string;
|
|
588
|
+
/**
|
|
589
|
+
* Step 1 for any video, picking the path by size. Prefer this over
|
|
590
|
+
* {@link create} unless you control what the caller can hand you.
|
|
591
|
+
*
|
|
592
|
+
* Under {@link CAPTION_CHUNKED_THRESHOLD} it is exactly {@link create}: one
|
|
593
|
+
* `POST`, one probe, one round trip. At or above it, it is
|
|
594
|
+
* {@link createChunked}. The answer is the same `"uploaded"` row either way,
|
|
595
|
+
* because `finish` and `create` render the same `:extended` view - so the
|
|
596
|
+
* caller's step 2 does not need to know which path ran.
|
|
597
|
+
*
|
|
598
|
+
* The size comes from {@link captionUploadSize}, which cannot always find
|
|
599
|
+
* one: a `ReadableStream` and some React Native picks have no length until
|
|
600
|
+
* they are read. Those go down the single-request path, which is right for a
|
|
601
|
+
* phone pick and wrong for a large stream, so pass `video.size` whenever you
|
|
602
|
+
* know it rather than letting a 413 teach you.
|
|
603
|
+
*
|
|
604
|
+
* `onProgress` is honest about which path ran and reports COMPLETED
|
|
605
|
+
* transfers, never bytes handed to the runtime - the whole file as one tick
|
|
606
|
+
* on the direct path, one tick per 32 MiB part on the chunked one. `fetch`
|
|
607
|
+
* has no upload-progress event in any of the three runtimes this SDK targets;
|
|
608
|
+
* see the module note in `storage/upload.ts` for why neither XHR nor a
|
|
609
|
+
* counting stream body is the answer inside the core.
|
|
610
|
+
*
|
|
611
|
+
* Rate limit: the direct path and the chunked path's opening call both count
|
|
612
|
+
* against the expensive-tools bucket, 20 a minute shared with every other
|
|
613
|
+
* tool. The parts and the finish do not.
|
|
614
|
+
*/
|
|
615
|
+
upload(input: UploadCaptionVideoInput, options?: UploadCaptionOptions): Promise<CaptionJob>;
|
|
616
|
+
/**
|
|
617
|
+
* Step 1 in three calls, for a video too big for one request.
|
|
618
|
+
*
|
|
619
|
+
* Opens a session, sends the parts (three at a time by default, in whatever
|
|
620
|
+
* order the pool finishes them), then finishes - and `finish` is what probes
|
|
621
|
+
* the assembled file and creates the row, so this resolves with the same
|
|
622
|
+
* `"uploaded"` row {@link create} would have answered.
|
|
623
|
+
*
|
|
624
|
+
* Nothing is cleaned up on failure and nothing needs to be: the controller
|
|
625
|
+
* deletes the sidecar's copy itself when `finish` rejects the file, and a run
|
|
626
|
+
* abandoned before `finish` leaves only a partial file on the sidecar's disk
|
|
627
|
+
* with no row pointing at it. There is no client-visible way to abort a
|
|
628
|
+
* session - the backend has `CaptionsClient.abort_upload` but no route onto
|
|
629
|
+
* it - so an abandoned upload is simply abandoned.
|
|
630
|
+
*
|
|
631
|
+
* **React Native cannot take this path.** The bytes have to be sliced, and a
|
|
632
|
+
* picked `{ uri, name, type }` is a handle into the device that only a native
|
|
633
|
+
* module can open - so this throws for one rather than uploading an empty
|
|
634
|
+
* file with a 200 on it. What works on a phone is {@link create} (the
|
|
635
|
+
* transport hands the descriptor to RN's own `FormData` verbatim and the
|
|
636
|
+
* platform streams it off disk), which caps out at Cloudflare's ~100 MB. Past
|
|
637
|
+
* that the only ways through are reading the file into a `Uint8Array` first -
|
|
638
|
+
* Expo's `new File(uri).bytes()`, which means the whole video in the JS heap,
|
|
639
|
+
* so it is not a plan for 250 MiB - or shrinking the video on the device.
|
|
640
|
+
*
|
|
641
|
+
* Resuming: pass {@link CaptionUploadResume} with the earlier session and the
|
|
642
|
+
* offsets {@link UploadCaptionOptions.onPart} reported. Skipping is only an
|
|
643
|
+
* optimisation, because a part is written at its own offset and re-sending it
|
|
644
|
+
* is idempotent; the session itself expires after
|
|
645
|
+
* {@link CAPTION_UPLOAD_TOKEN_TTL_MS}.
|
|
646
|
+
*
|
|
647
|
+
* @throws {OmsError} `invalid_request` for a React Native descriptor, or for
|
|
648
|
+
* an empty file - which the server would answer as "Invalid size", after a
|
|
649
|
+
* round trip that had nothing to carry.
|
|
650
|
+
* @throws {OmsApiError} 413 over 250 MiB, raised by the opening call before
|
|
651
|
+
* any bytes move; 400 from `finish` when the assembled file does not decode
|
|
652
|
+
* or runs past 20 minutes; 400 "Invalid or expired upload session" once the
|
|
653
|
+
* token is six hours old.
|
|
654
|
+
* @throws {OmsAuthError} 401 when anonymous and the captcha is missing or
|
|
655
|
+
* bad. It is checked when the session is opened, not when the bytes land,
|
|
656
|
+
* so a bad token costs one round trip rather than the whole upload.
|
|
657
|
+
*/
|
|
658
|
+
createChunked(input: UploadCaptionVideoInput, options?: UploadCaptionOptions): Promise<CaptionJob>;
|
|
659
|
+
/**
|
|
660
|
+
* `POST /caption_jobs/uploads` - opens a chunked upload session.
|
|
661
|
+
*
|
|
662
|
+
* Public so a caller can drive the three calls itself, which is the supported
|
|
663
|
+
* way to get byte-level progress: wrap XHR (or a native uploader) around
|
|
664
|
+
* {@link uploadPart}'s job and keep the session and the finish from here.
|
|
665
|
+
*
|
|
666
|
+
* Three things happen here and nowhere else in the flow: the captcha is
|
|
667
|
+
* verified, the 250 MiB ceiling is checked against the DECLARED size, and the
|
|
668
|
+
* job id is minted - early, because the sidecar needs an id to write parts
|
|
669
|
+
* under and the row cannot exist until the file is whole. So a session is not
|
|
670
|
+
* a job: nothing is in the database yet, and {@link get} has nothing to find
|
|
671
|
+
* until {@link finishUpload} returns.
|
|
672
|
+
*
|
|
673
|
+
* NOT retried by default. A replay opens a SECOND sidecar upload under a
|
|
674
|
+
* second job id and hands back a second token, and the first is then orphaned
|
|
675
|
+
* on the sidecar's disk with no route to abort it.
|
|
676
|
+
*
|
|
677
|
+
* Rate limit: 20 a minute, shared with every other expensive tool - the same
|
|
678
|
+
* bucket `POST /caption_jobs` counts against, since this is its twin.
|
|
679
|
+
*
|
|
680
|
+
* @throws {OmsApiError} 413 when `size` is over 250 MiB, 400 when it is not
|
|
681
|
+
* positive, 503 when the captions sidecar is down.
|
|
682
|
+
* @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
|
|
683
|
+
* @throws {OmsQuotaError} 429 from the expensive-tools throttle. No daily
|
|
684
|
+
* quota is spent by an upload - only a transcribed window is metered.
|
|
685
|
+
*/
|
|
686
|
+
startUpload(input: StartCaptionUploadInput, options?: RequestOptions): Promise<CaptionUploadSession>;
|
|
687
|
+
/**
|
|
688
|
+
* `POST /caption_jobs/uploads/parts?offset=` - one part, as raw bytes.
|
|
689
|
+
*
|
|
690
|
+
* The body is the part itself, `application/octet-stream`, with no envelope:
|
|
691
|
+
* not a form field, not base64, not JSON. Rails never reads it into a string
|
|
692
|
+
* - it streams the request body straight through to the sidecar - so a 32 MiB
|
|
693
|
+
* part costs 32 MiB of socket rather than 32 MiB of Ruby heap, and that is
|
|
694
|
+
* why the endpoint takes bytes rather than a multipart part.
|
|
695
|
+
*
|
|
696
|
+
* RETRIED BY DEFAULT, which no other writing method in this SDK is. The
|
|
697
|
+
* server writes the part at the offset the query names, so a replay is the
|
|
698
|
+
* same bytes in the same place: there is no record to duplicate and no
|
|
699
|
+
* position to lose. Pass `retry: false` to opt out. For the same reason the
|
|
700
|
+
* parts may go in any order and in parallel, and a part sent twice is not an
|
|
701
|
+
* error.
|
|
702
|
+
*
|
|
703
|
+
* `part` must not be longer than the session's `part_size` (see
|
|
704
|
+
* {@link resolveCaptionPartSize}) and `offset + part.length` must not run
|
|
705
|
+
* past the size declared when the session was opened. Both are 400s whose
|
|
706
|
+
* text does not distinguish them from each other.
|
|
707
|
+
*
|
|
708
|
+
* @param session The session from {@link startUpload}, or its token.
|
|
709
|
+
* @param offset Byte offset of this part in the whole file, from `0`.
|
|
710
|
+
* @returns The byte count the sidecar acknowledged, from `{ received }`.
|
|
711
|
+
* @throws {OmsApiError} 400 for an over-long part, an offset past the
|
|
712
|
+
* declared size, or a token that has expired (six hours); 503 when the
|
|
713
|
+
* sidecar is down.
|
|
714
|
+
*/
|
|
715
|
+
uploadPart(session: CaptionUploadSession | string, offset: number, part: Blob | Uint8Array, options?: RequestOptions): Promise<number>;
|
|
716
|
+
/**
|
|
717
|
+
* `POST /caption_jobs/uploads/finish` - assembles, probes and creates the row.
|
|
718
|
+
*
|
|
719
|
+
* This is where a chunked upload becomes a job: the sidecar joins the parts,
|
|
720
|
+
* ffprobe reads the result, and only then is a `CaptionJob` written, with the
|
|
721
|
+
* id that was sealed into the token at the start. The answer is the same
|
|
722
|
+
* `:extended` view {@link create} returns, in `"uploaded"`.
|
|
723
|
+
*
|
|
724
|
+
* It is also where a bad upload is caught. A file that does not decode, or
|
|
725
|
+
* one over 20 minutes, is a 400 - and the controller deletes the sidecar's
|
|
726
|
+
* copy before answering, so there is nothing to clean up and nothing to
|
|
727
|
+
* retry: the parts are gone with it.
|
|
728
|
+
*
|
|
729
|
+
* NOT retried by default. The row is created with a fixed id, so a replay
|
|
730
|
+
* that lands after a lost answer fails on the id already existing and reports
|
|
731
|
+
* a 400 for an upload that worked.
|
|
732
|
+
*
|
|
733
|
+
* @throws {OmsApiError} 400 when the assembled file cannot be read or runs
|
|
734
|
+
* past 20 minutes, 400 "Invalid or expired upload session" for a token that
|
|
735
|
+
* is over six hours old, 503 when the sidecar is down.
|
|
736
|
+
*/
|
|
737
|
+
finishUpload(session: CaptionUploadSession | string, options?: RequestOptions): Promise<CaptionJob>;
|
|
311
738
|
/**
|
|
312
739
|
* Waits for a started step to settle, reusing the one polling loop.
|
|
313
740
|
*
|
|
@@ -394,4 +394,25 @@ export declare class DownloaderNamespace extends Resource {
|
|
|
394
394
|
* @throws {OmsApiError} exactly as {@link download}.
|
|
395
395
|
*/
|
|
396
396
|
downloadFile(id: Id, options?: RequestOptions): Promise<FileOutput>;
|
|
397
|
+
/**
|
|
398
|
+
* The unauthenticated URL of the file endpoint. Pure string building, no
|
|
399
|
+
* request.
|
|
400
|
+
*
|
|
401
|
+
* Deliberately does NOT carry a credential, which makes it useless for
|
|
402
|
+
* handing to an `<a download>` or a `<video src>`: the route needs one, and
|
|
403
|
+
* an anonymous request to it is a 401. It is here so a caller can log or
|
|
404
|
+
* display the address, and so the shape of the endpoint is documented
|
|
405
|
+
* somewhere other than inside {@link downloadFile}.
|
|
406
|
+
*
|
|
407
|
+
* The web app builds a *usable* browser URL by appending its session token
|
|
408
|
+
* as a `?token=` query parameter. The SDK will not do that: the transport
|
|
409
|
+
* holds the token so it can put it in an `Authorization` header, and copying
|
|
410
|
+
* it into a URL puts it in browser history, in the Referer, and in every
|
|
411
|
+
* proxy log between here and the API. A host that genuinely needs a
|
|
412
|
+
* browser-followable link should call {@link downloadFile} and hand the
|
|
413
|
+
* blob to a local object URL instead.
|
|
414
|
+
*
|
|
415
|
+
* Remember the endpoint is ONE SHOT whichever way it is reached.
|
|
416
|
+
*/
|
|
417
|
+
fileUrl(id: Id): string;
|
|
397
418
|
}
|
|
@@ -57,30 +57,72 @@ export * from "./vocalSeparation";
|
|
|
57
57
|
export type ToolStatus = "pending" | "processing" | "complete" | "failed";
|
|
58
58
|
/** Terminal states. A row in one of these will never change again. */
|
|
59
59
|
export declare const TOOL_TERMINAL_STATUSES: readonly ToolStatus[];
|
|
60
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Fields every tool row carries.
|
|
62
|
+
*
|
|
63
|
+
* All six blueprints in the family declare `status`, `error`, `finished_at`,
|
|
64
|
+
* `user_id` and `ip_address` in their DEFAULT view, and every route that
|
|
65
|
+
* answers with a tool row renders `:extended`, which inherits the default
|
|
66
|
+
* view's fields. So those five keys are always present; four of them hold a
|
|
67
|
+
* nullable column and are `null` rather than missing.
|
|
68
|
+
*
|
|
69
|
+
* `progress_percent` is the exception and is optional on purpose: it is an
|
|
70
|
+
* `:extended`-only field on FOUR of the six - transcription, vocal separation,
|
|
71
|
+
* captions and jumpstyle, the ones with a sidecar to ask - and does not exist
|
|
72
|
+
* at all on upscale or background removal, whose progress lives on the {@link
|
|
73
|
+
* Job} row instead. `toolProgress` reads it defensively for exactly that
|
|
74
|
+
* reason.
|
|
75
|
+
*/
|
|
61
76
|
export interface ToolRecord extends BaseRecord {
|
|
62
77
|
readonly status: ToolStatus | string;
|
|
63
|
-
/** Failure message, set once the status is `"failed"`. */
|
|
64
|
-
readonly error
|
|
65
|
-
|
|
66
|
-
readonly
|
|
67
|
-
/**
|
|
68
|
-
readonly
|
|
69
|
-
/**
|
|
78
|
+
/** Failure message, set once the status is `"failed"`. `null` otherwise. */
|
|
79
|
+
readonly error: string | null;
|
|
80
|
+
/** Set when the run reached a terminal state; `null` before that. */
|
|
81
|
+
readonly finished_at: Timestamp | null;
|
|
82
|
+
/** The owner, or `null` for an anonymous run. */
|
|
83
|
+
readonly user_id: string | null;
|
|
84
|
+
/**
|
|
85
|
+
* The address an anonymous run was started from, and `null` for a run with
|
|
86
|
+
* an owner - the controllers write one or the other, never both, and the
|
|
87
|
+
* models validate that at least one is set.
|
|
88
|
+
*
|
|
89
|
+
* It is also the credential for an anonymous run: `accessible_by?` matches
|
|
90
|
+
* this against the reader's own address, which is why picking up an
|
|
91
|
+
* anonymous run from a different network is a 401.
|
|
92
|
+
*/
|
|
93
|
+
readonly ip_address: string | null;
|
|
94
|
+
/**
|
|
95
|
+
* Percentage the sidecar reports while running, `null` when idle or done -
|
|
96
|
+
* and absent entirely on upscale and background removal, which have no such
|
|
97
|
+
* field. See the note on this interface.
|
|
98
|
+
*/
|
|
70
99
|
readonly progress_percent?: number | null;
|
|
71
100
|
}
|
|
72
101
|
/**
|
|
73
102
|
* Handle for polling an enqueued run.
|
|
74
103
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
104
|
+
* Both keys come back from the two tools that enqueue through the generic job
|
|
105
|
+
* table (upscale, background removal), and BOTH are always present on their
|
|
106
|
+
* create response: the controllers merge them onto the rendered row
|
|
107
|
+
* unconditionally. `watch_token` is nullable rather than absent, because
|
|
108
|
+
* `signed_id` is called on a tracking row that may not have been found.
|
|
109
|
+
*
|
|
110
|
+
* The token is what lets an anonymous caller poll `GET /jobs/:id`. The other
|
|
111
|
+
* five tools are polled by reading their own row and answer neither key, which
|
|
112
|
+
* is why this is a separate interface mixed into their `Created` types rather
|
|
113
|
+
* than part of {@link ToolRecord}.
|
|
79
114
|
*/
|
|
80
115
|
export interface ToolJobHandle {
|
|
81
|
-
readonly job_id
|
|
82
|
-
/**
|
|
83
|
-
|
|
116
|
+
readonly job_id: string;
|
|
117
|
+
/**
|
|
118
|
+
* Signed and scoped to this one job, and expiring with the tool's retention
|
|
119
|
+
* window. Required when anonymous.
|
|
120
|
+
*
|
|
121
|
+
* `null` when the tracking row could not be read back after enqueue, which
|
|
122
|
+
* leaves an anonymous caller with no way to poll at all - the job id alone
|
|
123
|
+
* is a 404 without a credential.
|
|
124
|
+
*/
|
|
125
|
+
readonly watch_token: string | null;
|
|
84
126
|
}
|
|
85
127
|
/** Mixed into every create input: the anonymous caller's captcha. */
|
|
86
128
|
export interface ToolCaptcha {
|