@omelhorsite/sdk 0.1.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 +321 -0
- package/dist/index.js +11589 -0
- package/dist/types/auth/device.d.ts +156 -0
- package/dist/types/auth/index.d.ts +127 -0
- package/dist/types/auth/tokens.d.ts +356 -0
- package/dist/types/client.d.ts +133 -0
- package/dist/types/errors.d.ts +202 -0
- package/dist/types/http.d.ts +204 -0
- package/dist/types/index.d.ts +33 -0
- package/dist/types/local/index.d.ts +42 -0
- package/dist/types/local/password.d.ts +169 -0
- package/dist/types/local/qr.d.ts +127 -0
- package/dist/types/local/wordlist.d.ts +26 -0
- package/dist/types/resources/account.d.ts +296 -0
- package/dist/types/resources/chests.d.ts +194 -0
- package/dist/types/resources/dynamicQrs.d.ts +172 -0
- package/dist/types/resources/forms.d.ts +331 -0
- package/dist/types/resources/index.d.ts +30 -0
- package/dist/types/resources/ipLookup.d.ts +63 -0
- package/dist/types/resources/jobs.d.ts +233 -0
- package/dist/types/resources/linkTrees.d.ts +249 -0
- package/dist/types/resources/notepads.d.ts +96 -0
- package/dist/types/resources/shortLinks.d.ts +248 -0
- package/dist/types/resources/storage/upload.d.ts +459 -0
- package/dist/types/resources/storage.d.ts +527 -0
- package/dist/types/resources/tickets.d.ts +236 -0
- package/dist/types/resources/tools/backgroundRemoval.d.ts +99 -0
- package/dist/types/resources/tools/captions.d.ts +318 -0
- package/dist/types/resources/tools/downloader.d.ts +397 -0
- package/dist/types/resources/tools/index.d.ts +215 -0
- package/dist/types/resources/tools/jumpstyle.d.ts +194 -0
- package/dist/types/resources/tools/transcription.d.ts +178 -0
- package/dist/types/resources/tools/upscale.d.ts +94 -0
- package/dist/types/resources/tools/vocalSeparation.d.ts +183 -0
- package/dist/types/types.d.ts +245 -0
- package/package.json +37 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Downloader: fetches media from a public URL, or finds it from artist plus
|
|
3
|
+
* title.
|
|
4
|
+
*
|
|
5
|
+
* Different from its siblings in five ways worth knowing before writing a line
|
|
6
|
+
* against it:
|
|
7
|
+
*
|
|
8
|
+
* - it uploads nothing, so there is no captcha and no multipart form. It is
|
|
9
|
+
* also the one tool with NO anonymous door at all: every endpoint here needs
|
|
10
|
+
* a credential;
|
|
11
|
+
* - its job lives on the yt-dlp sidecar, NOT in the `jobs` table and NOT in the
|
|
12
|
+
* database, so poll it with {@link DownloaderNamespace.getJob} and never with
|
|
13
|
+
* `oms.jobs`;
|
|
14
|
+
* - the sidecar's job record has no id in it, so this namespace stamps one on.
|
|
15
|
+
* See {@link DownloaderJob.id};
|
|
16
|
+
* - every URL it is given passes an SSRF guard server-side. A private or
|
|
17
|
+
* link-local address comes back as a 400, by design. Do not try to work
|
|
18
|
+
* around it;
|
|
19
|
+
* - {@link DownloaderNamespace.download} is ONE SHOT. Streaming the file
|
|
20
|
+
* deletes it from the sidecar, so a second call is a 404. Buffer the blob and
|
|
21
|
+
* write it somewhere before you need it twice.
|
|
22
|
+
*
|
|
23
|
+
* Limits, all enforced by the backend:
|
|
24
|
+
*
|
|
25
|
+
* | | |
|
|
26
|
+
* |---|---|
|
|
27
|
+
* | download jobs | 30 an hour |
|
|
28
|
+
* | previews and artwork lookups | 60 an hour, together |
|
|
29
|
+
* | all three `POST`s | 20 a minute, shared with every other expensive tool |
|
|
30
|
+
*
|
|
31
|
+
* The hourly limits are keyed on the user id, so they follow the account rather
|
|
32
|
+
* than the machine. Unlike the other tools there is no daily quota and so no
|
|
33
|
+
* `quota()` here: what bounds this tool is a rate, and the only way to read it
|
|
34
|
+
* is to hit it. A 429 from the hourly limit and a 429 from the 20-a-minute
|
|
35
|
+
* throttle are told apart by `retryAfterMs`, which only the latter sets.
|
|
36
|
+
*
|
|
37
|
+
* There is nothing metered about a byte of this: a two-hour video costs the
|
|
38
|
+
* same one job as a three-minute song.
|
|
39
|
+
*/
|
|
40
|
+
import { Resource } from "../../http";
|
|
41
|
+
import type { FileOutput, Id, Progress, RequestOptions, WaitOptions } from "../../types";
|
|
42
|
+
import type { ToolRunOptions } from "./index";
|
|
43
|
+
/**
|
|
44
|
+
* One downloadable rendition the source offers.
|
|
45
|
+
*
|
|
46
|
+
* VIDEO renditions only: the sidecar drops every format whose `vcodec` is
|
|
47
|
+
* `"none"` before answering, so an audio-only download has no format list to
|
|
48
|
+
* choose from and `formatId` is not worth passing for one.
|
|
49
|
+
*/
|
|
50
|
+
export interface DownloaderFormat {
|
|
51
|
+
/** What to pass as {@link CreateDownloadInput.formatId}. */
|
|
52
|
+
readonly format_id?: string;
|
|
53
|
+
readonly ext?: string;
|
|
54
|
+
/** `"1920x1080"`, or built from width and height when the source omits it. */
|
|
55
|
+
readonly resolution?: string | null;
|
|
56
|
+
readonly width?: number | null;
|
|
57
|
+
readonly height?: number | null;
|
|
58
|
+
readonly fps?: number | null;
|
|
59
|
+
readonly vcodec?: string;
|
|
60
|
+
readonly acodec?: string;
|
|
61
|
+
/** Bytes, exact or approximate. `null` when the source will not say. */
|
|
62
|
+
readonly filesize?: number | null;
|
|
63
|
+
/** Total bitrate, kbps. */
|
|
64
|
+
readonly tbr?: number | null;
|
|
65
|
+
readonly format_note?: string | null;
|
|
66
|
+
/** False for a video-only rendition that would need muxing. */
|
|
67
|
+
readonly has_audio?: boolean;
|
|
68
|
+
readonly [key: string]: unknown;
|
|
69
|
+
}
|
|
70
|
+
/** One entry of a playlist preview. */
|
|
71
|
+
export interface DownloaderPlaylistTrack {
|
|
72
|
+
readonly title?: string | null;
|
|
73
|
+
readonly artist?: string | null;
|
|
74
|
+
readonly duration_s?: number | null;
|
|
75
|
+
readonly thumbnails?: ReadonlyArray<{
|
|
76
|
+
readonly url?: string;
|
|
77
|
+
readonly [key: string]: unknown;
|
|
78
|
+
}>;
|
|
79
|
+
/** Feed this back to {@link DownloaderNamespace.preview} or as a `sourceUrl`. */
|
|
80
|
+
readonly webpage_url?: string | null;
|
|
81
|
+
readonly id?: string | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `POST /tools_downloader/preview` - what the source says about a URL.
|
|
85
|
+
*
|
|
86
|
+
* Two shapes behind one type, told apart by {@link DownloaderPreview.kind}: a
|
|
87
|
+
* `"track"` carries the metadata fields, a `"playlist"` carries `count` and
|
|
88
|
+
* `tracks` and none of the rest. Check `kind` before reading anything else.
|
|
89
|
+
*/
|
|
90
|
+
export interface DownloaderPreview {
|
|
91
|
+
readonly kind?: "track" | "playlist" | string;
|
|
92
|
+
readonly title?: string | null;
|
|
93
|
+
readonly artist?: string | null;
|
|
94
|
+
readonly album?: string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Seconds. Named `duration_s` because that is what the sidecar sends; there
|
|
97
|
+
* is no `duration` key on this payload.
|
|
98
|
+
*/
|
|
99
|
+
readonly duration_s?: number | null;
|
|
100
|
+
/** Every thumbnail the source offers, worst first. */
|
|
101
|
+
readonly thumbnails?: ReadonlyArray<{
|
|
102
|
+
readonly url?: string;
|
|
103
|
+
readonly [key: string]: unknown;
|
|
104
|
+
}>;
|
|
105
|
+
readonly webpage_url?: string | null;
|
|
106
|
+
/** The source's own id for the item, not a job id. */
|
|
107
|
+
readonly id?: string | null;
|
|
108
|
+
/** Which yt-dlp extractor matched. */
|
|
109
|
+
readonly extractor?: string | null;
|
|
110
|
+
/** Video renditions. Absent on a playlist. */
|
|
111
|
+
readonly formats?: DownloaderFormat[];
|
|
112
|
+
/** Entries, on a `"playlist"`. */
|
|
113
|
+
readonly tracks?: DownloaderPlaylistTrack[];
|
|
114
|
+
/** How many entries the playlist has. */
|
|
115
|
+
readonly count?: number | null;
|
|
116
|
+
readonly [key: string]: unknown;
|
|
117
|
+
}
|
|
118
|
+
/** One cover-art candidate. */
|
|
119
|
+
export interface ArtworkCandidate {
|
|
120
|
+
/** Full-size image. Pass it as {@link CreateDownloadInput.artworkUrl}. */
|
|
121
|
+
readonly url: string;
|
|
122
|
+
/** Smaller version of the same image, for a picker. */
|
|
123
|
+
readonly thumb_url?: string | null;
|
|
124
|
+
/** `"itunes"`, `"deezer"` or `"musicbrainz"`. */
|
|
125
|
+
readonly source?: string;
|
|
126
|
+
/** Pixels, when the source states them. `null` is common and means unknown. */
|
|
127
|
+
readonly width?: number | null;
|
|
128
|
+
readonly height?: number | null;
|
|
129
|
+
/** Album or track name the candidate was found under. */
|
|
130
|
+
readonly label?: string | null;
|
|
131
|
+
/** Artist, or whatever the source offers as a second line. */
|
|
132
|
+
readonly subtitle?: string | null;
|
|
133
|
+
readonly [key: string]: unknown;
|
|
134
|
+
}
|
|
135
|
+
/** Arguments for an artwork lookup. Pass either the triple or a free query. */
|
|
136
|
+
export interface ArtworkSearchInput {
|
|
137
|
+
readonly artist?: string;
|
|
138
|
+
readonly title?: string;
|
|
139
|
+
readonly album?: string;
|
|
140
|
+
/**
|
|
141
|
+
* Free text, used when neither `artist` nor `title` is given. It is thrown at
|
|
142
|
+
* the sources as a title with no artist, so the triple beats it when you have
|
|
143
|
+
* one.
|
|
144
|
+
*/
|
|
145
|
+
readonly query?: string;
|
|
146
|
+
}
|
|
147
|
+
/** What to fetch. */
|
|
148
|
+
export type DownloaderKind = "audio" | "video";
|
|
149
|
+
/**
|
|
150
|
+
* Arguments for a download job.
|
|
151
|
+
*
|
|
152
|
+
* Either `sourceUrl`, or BOTH `artist` and `title` so the sidecar can search.
|
|
153
|
+
* Anything else is a 400.
|
|
154
|
+
*/
|
|
155
|
+
export interface CreateDownloadInput {
|
|
156
|
+
/** Direct URL. Must be public: the SSRF guard rejects private ranges. */
|
|
157
|
+
readonly sourceUrl?: string;
|
|
158
|
+
/** Search terms, used when there is no `sourceUrl`. Both are required together. */
|
|
159
|
+
readonly artist?: string;
|
|
160
|
+
readonly title?: string;
|
|
161
|
+
readonly album?: string;
|
|
162
|
+
/** Which site to search. */
|
|
163
|
+
readonly source?: string;
|
|
164
|
+
readonly kind?: DownloaderKind;
|
|
165
|
+
/**
|
|
166
|
+
* A `format_id` from {@link DownloaderPreview.formats}. Video only - the
|
|
167
|
+
* format list never contains an audio-only rendition.
|
|
168
|
+
*/
|
|
169
|
+
readonly formatId?: string;
|
|
170
|
+
/** Metadata written into the finished file, overriding what was detected. */
|
|
171
|
+
readonly overrideTitle?: string;
|
|
172
|
+
readonly overrideArtist?: string;
|
|
173
|
+
readonly overrideAlbum?: string;
|
|
174
|
+
/** Cover art by URL. Same SSRF guard as `sourceUrl`. */
|
|
175
|
+
readonly artworkUrl?: string;
|
|
176
|
+
/** Cover art inline, base64, when you already hold the bytes. */
|
|
177
|
+
readonly artworkDataB64?: string;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* How far along a sidecar download is.
|
|
181
|
+
*
|
|
182
|
+
* These are the sidecar's own five strings. Note the two that trip people up:
|
|
183
|
+
* it finishes on `"complete"` - the same word a tool row uses, NOT the `"done"`
|
|
184
|
+
* an older draft of this SDK claimed - and it fails on `"failed"`, not
|
|
185
|
+
* `"error"`.
|
|
186
|
+
*/
|
|
187
|
+
export type DownloaderJobStatus = "queued" | "fetching" | "downloading" | "complete" | "failed";
|
|
188
|
+
/** Statuses a sidecar download never leaves. */
|
|
189
|
+
export declare const DOWNLOADER_TERMINAL_STATUSES: readonly DownloaderJobStatus[];
|
|
190
|
+
/** True once a sidecar download can never change again. */
|
|
191
|
+
export declare function isDownloaderTerminal(status: string): boolean;
|
|
192
|
+
/** A download job, as the sidecar reports it. */
|
|
193
|
+
export interface DownloaderJob {
|
|
194
|
+
/**
|
|
195
|
+
* The job id.
|
|
196
|
+
*
|
|
197
|
+
* Stamped on by this namespace, not sent by the server: the create call
|
|
198
|
+
* answers `{ request_id, target }` and the progress call answers a bare
|
|
199
|
+
* `{ status, message, progress }` with no identifier in it at all. Both are
|
|
200
|
+
* normalised here so a job can be passed around as one object.
|
|
201
|
+
*/
|
|
202
|
+
readonly id: Id;
|
|
203
|
+
/** The sidecar's own name for {@link DownloaderJob.id}, on the create answer. */
|
|
204
|
+
readonly request_id?: string;
|
|
205
|
+
/** What the job was pointed at - the URL, or the search string. Create answer only. */
|
|
206
|
+
readonly target?: string;
|
|
207
|
+
/**
|
|
208
|
+
* Absent on the create answer, which carries no status at all. Treat a
|
|
209
|
+
* missing status as `"queued"`: the sidecar has just registered it.
|
|
210
|
+
*/
|
|
211
|
+
readonly status?: DownloaderJobStatus | string;
|
|
212
|
+
/** Fraction in `[0, 1]`, NOT a percentage. `1` once complete. */
|
|
213
|
+
readonly progress?: number | null;
|
|
214
|
+
/** Human sentence about the current step, and the failure reason once failed. */
|
|
215
|
+
readonly message?: string | null;
|
|
216
|
+
/**
|
|
217
|
+
* Never sent by this sidecar. The real filename arrives on the download
|
|
218
|
+
* response instead - see {@link DownloaderNamespace.downloadFile} - and
|
|
219
|
+
* `message` holds `"saved <name>"` once complete.
|
|
220
|
+
*/
|
|
221
|
+
readonly filename?: string | null;
|
|
222
|
+
/**
|
|
223
|
+
* Failure reason. The sidecar reports it as `message`, so this is normally
|
|
224
|
+
* absent; read `message` when `status` is `"failed"`.
|
|
225
|
+
*/
|
|
226
|
+
readonly error?: string | null;
|
|
227
|
+
/** Metadata the sidecar resolved, present once complete. */
|
|
228
|
+
readonly title?: string | null;
|
|
229
|
+
readonly uploader?: string | null;
|
|
230
|
+
readonly duration_s?: number | null;
|
|
231
|
+
readonly source_url?: string | null;
|
|
232
|
+
readonly source_provider?: string | null;
|
|
233
|
+
readonly source_id?: string | null;
|
|
234
|
+
readonly audio_codec?: string | null;
|
|
235
|
+
readonly audio_bitrate_kbps?: number | null;
|
|
236
|
+
readonly [key: string]: unknown;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Renders a sidecar download as a {@link Progress}.
|
|
240
|
+
*
|
|
241
|
+
* The sidecar's `progress` is a FRACTION, so it is scaled to the 0-100 every
|
|
242
|
+
* other tool in this SDK reports. `total` stays `undefined` while there is no
|
|
243
|
+
* number at all, rather than inventing a denominator a spinner would then
|
|
244
|
+
* render as 0%.
|
|
245
|
+
*/
|
|
246
|
+
export declare function downloaderProgress(job: DownloaderJob): Progress;
|
|
247
|
+
/** The `downloader` tool, reachable as `oms.tools.downloader`. */
|
|
248
|
+
export declare class DownloaderNamespace extends Resource {
|
|
249
|
+
/**
|
|
250
|
+
* `POST /tools_downloader/preview` - metadata and available formats for a
|
|
251
|
+
* URL, without downloading anything.
|
|
252
|
+
*
|
|
253
|
+
* Answers a `"track"` or a `"playlist"`; check {@link DownloaderPreview.kind}
|
|
254
|
+
* before reading anything else. A playlist URL previews as a listing, but
|
|
255
|
+
* {@link createJob} downloads only what a single `sourceUrl` resolves to, so
|
|
256
|
+
* feed the entries' `webpage_url` back one at a time.
|
|
257
|
+
*
|
|
258
|
+
* SLOW, and slower than the SDK's own default deadline: the sidecar waits up
|
|
259
|
+
* to 60 seconds for yt-dlp and the transport's default `timeoutMs` is also
|
|
260
|
+
* 60 seconds, so a source that is merely sluggish aborts client-side just
|
|
261
|
+
* before the answer. Pass a `timeoutMs` above 60000 for anything but a
|
|
262
|
+
* well-behaved host.
|
|
263
|
+
*
|
|
264
|
+
* NOT retried by default. The controller turns every failure into a 502,
|
|
265
|
+
* including "this source refuses yt-dlp", which no amount of retrying fixes -
|
|
266
|
+
* and each attempt spends one of the 60 hourly lookups and parks a server
|
|
267
|
+
* thread for up to a minute. Pass `retry: {}` to opt back in.
|
|
268
|
+
*
|
|
269
|
+
* @throws {OmsApiError} 400 when the URL is blank or fails the SSRF guard,
|
|
270
|
+
* 502 when the source refuses or yt-dlp cannot read it.
|
|
271
|
+
* @throws {OmsQuotaError} 429 past 60 lookups an hour, or from the
|
|
272
|
+
* 20-a-minute expensive-tools throttle - only the second sets
|
|
273
|
+
* `retryAfterMs`.
|
|
274
|
+
* @throws {OmsAuthError} 401 when there is no credential. This tool has no
|
|
275
|
+
* anonymous door.
|
|
276
|
+
*/
|
|
277
|
+
preview(url: string, options?: RequestOptions): Promise<DownloaderPreview>;
|
|
278
|
+
/**
|
|
279
|
+
* `POST /tools_downloader/artwork_search` - cover-art candidates.
|
|
280
|
+
*
|
|
281
|
+
* Fans out to iTunes, Deezer and MusicBrainz in parallel and returns whatever
|
|
282
|
+
* answered in time, deduplicated by URL. A source that is slow or down is
|
|
283
|
+
* skipped silently, so an empty list means "nothing found right now", not
|
|
284
|
+
* "this record has no cover".
|
|
285
|
+
*
|
|
286
|
+
* The controller answers `{ items: [...] }`; this unwraps it.
|
|
287
|
+
*
|
|
288
|
+
* NOT retried by default, for the same reason as {@link preview}: a failure
|
|
289
|
+
* here is a 502 that means an upstream said no, and every attempt costs one
|
|
290
|
+
* of the 60 hourly lookups.
|
|
291
|
+
*
|
|
292
|
+
* @throws {OmsQuotaError} 429 past 60 lookups an hour, or from the
|
|
293
|
+
* expensive-tools throttle.
|
|
294
|
+
* @throws {OmsApiError} 502 when the lookup itself blew up.
|
|
295
|
+
*/
|
|
296
|
+
artworkSearch(input: ArtworkSearchInput, options?: RequestOptions): Promise<ArtworkCandidate[]>;
|
|
297
|
+
/**
|
|
298
|
+
* `POST /tools_downloader/jobs` - starts a download.
|
|
299
|
+
*
|
|
300
|
+
* Answers immediately with an id and nothing else - no status, no progress.
|
|
301
|
+
* The sidecar's own answer is `{ request_id, target }`; this maps
|
|
302
|
+
* `request_id` onto {@link DownloaderJob.id} so the record matches every
|
|
303
|
+
* other tool in the SDK, and keeps both original keys alongside it.
|
|
304
|
+
*
|
|
305
|
+
* NOT retried by default: replaying this `POST` after a 502 starts a SECOND
|
|
306
|
+
* download of the same thing and spends another of the 30 hourly jobs, with
|
|
307
|
+
* the first still running and no way to reach its id. Pass `retry: {}` to opt
|
|
308
|
+
* back in.
|
|
309
|
+
*
|
|
310
|
+
* @throws {OmsApiError} 400 when neither a `sourceUrl` nor an artist+title
|
|
311
|
+
* pair was given, or when `sourceUrl` or `artworkUrl` fails the SSRF guard.
|
|
312
|
+
* 502 when the sidecar refused the job.
|
|
313
|
+
* @throws {OmsQuotaError} 429 past 30 jobs an hour, or from the 20-a-minute
|
|
314
|
+
* expensive-tools throttle - only the second sets `retryAfterMs`.
|
|
315
|
+
* @throws {OmsAuthError} 401 when there is no credential.
|
|
316
|
+
*/
|
|
317
|
+
createJob(input: CreateDownloadInput, options?: RequestOptions): Promise<DownloaderJob>;
|
|
318
|
+
/**
|
|
319
|
+
* `GET /tools_downloader/jobs/:id` - one poll against the sidecar.
|
|
320
|
+
*
|
|
321
|
+
* The sidecar answers a bare `{ status, message, progress }` with no id in
|
|
322
|
+
* it, so the id you asked with is stamped back on. Once complete the same
|
|
323
|
+
* payload carries the resolved metadata - title, uploader, duration, codec.
|
|
324
|
+
*
|
|
325
|
+
* The sidecar holds jobs in memory with a TTL sweep, so a 404 here means
|
|
326
|
+
* "unknown or swept", and a process restart loses every job at once.
|
|
327
|
+
*
|
|
328
|
+
* @throws {OmsApiError} 404 when the sidecar does not know the id, 502 when
|
|
329
|
+
* the sidecar itself is unreachable.
|
|
330
|
+
*/
|
|
331
|
+
getJob(id: Id, options?: RequestOptions): Promise<DownloaderJob>;
|
|
332
|
+
/**
|
|
333
|
+
* Starts a download and waits for it.
|
|
334
|
+
*
|
|
335
|
+
* Polls {@link getJob} directly: this job is not in the `jobs` table, so
|
|
336
|
+
* `oms.jobs.wait` cannot see it. The loop is still `pollUntilTerminal` from
|
|
337
|
+
* the jobs module - the same one every other tool uses.
|
|
338
|
+
*
|
|
339
|
+
* Resolves for `"failed"` as well as `"complete"`, because a source that
|
|
340
|
+
* refuses is an answer. Check `status` before calling {@link download}, and
|
|
341
|
+
* read `message` for the reason - the sidecar reports failures there, not in
|
|
342
|
+
* `error`.
|
|
343
|
+
*
|
|
344
|
+
* Pass `waitTimeoutMs` (or a `signal`) to bound the wait; there is no default
|
|
345
|
+
* deadline. The sidecar gives one download up to 10 minutes of its own before
|
|
346
|
+
* giving up.
|
|
347
|
+
*
|
|
348
|
+
* @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
|
|
349
|
+
* `code: "aborted"` when the signal fires. Neither cancels the download,
|
|
350
|
+
* which keeps running and can still be picked up with {@link getJob}.
|
|
351
|
+
*/
|
|
352
|
+
run(input: CreateDownloadInput, options?: ToolRunOptions): Promise<DownloaderJob>;
|
|
353
|
+
/**
|
|
354
|
+
* Waits for a download somebody else started.
|
|
355
|
+
*
|
|
356
|
+
* The half of {@link run} that does not spend one of the 30 hourly jobs -
|
|
357
|
+
* useful after a `--no-wait` create, or in a second process holding only the
|
|
358
|
+
* id.
|
|
359
|
+
*/
|
|
360
|
+
wait(id: Id, options?: WaitOptions): Promise<DownloaderJob>;
|
|
361
|
+
/**
|
|
362
|
+
* `GET /tools_downloader/jobs/:id/file` - the finished file, streamed
|
|
363
|
+
* through Rails from the sidecar.
|
|
364
|
+
*
|
|
365
|
+
* ONE SHOT. The sidecar deletes the file as soon as it has finished streaming
|
|
366
|
+
* it, so a second call to this - or to {@link downloadFile} - is a 404. Write
|
|
367
|
+
* the blob somewhere before you need it twice.
|
|
368
|
+
*
|
|
369
|
+
* Buffers the whole file, because it is a file the caller is about to write
|
|
370
|
+
* somewhere. That is fine for a song and a real consideration for a two-hour
|
|
371
|
+
* video, which is the one place in this SDK where `http.raw` and the response
|
|
372
|
+
* stream are worth reaching for directly.
|
|
373
|
+
*
|
|
374
|
+
* Unlike a tool artefact, this URL is a Rails endpoint and NOT a signed
|
|
375
|
+
* object-store link, so it does carry the bearer token like any other call.
|
|
376
|
+
*
|
|
377
|
+
* NOT retried by default, and this is the one download in the SDK where that
|
|
378
|
+
* is not a style choice: a retry after a torn stream finds a file the sidecar
|
|
379
|
+
* has already deleted.
|
|
380
|
+
*
|
|
381
|
+
* @throws {OmsApiError} 409 when the job is not finished yet, 404 once the
|
|
382
|
+
* sidecar has swept it or once this has already been called, 502 when the
|
|
383
|
+
* result expired underneath the sidecar or the stream broke.
|
|
384
|
+
*/
|
|
385
|
+
download(id: Id, options?: RequestOptions): Promise<Blob>;
|
|
386
|
+
/**
|
|
387
|
+
* The same one-shot download as {@link download}, keeping the filename.
|
|
388
|
+
*
|
|
389
|
+
* The only place the real filename appears: the sidecar's job record does not
|
|
390
|
+
* carry one, and Rails forwards the sidecar's `Content-Disposition` on this
|
|
391
|
+
* response and nowhere else. Prefer this over {@link download} whenever the
|
|
392
|
+
* file is going to disk.
|
|
393
|
+
*
|
|
394
|
+
* @throws {OmsApiError} exactly as {@link download}.
|
|
395
|
+
*/
|
|
396
|
+
downloadFile(id: Id, options?: RequestOptions): Promise<FileOutput>;
|
|
397
|
+
}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `tools` namespace: the metered media tools.
|
|
3
|
+
*
|
|
4
|
+
* Every tool here shares four traits, and this file owns the types for them so
|
|
5
|
+
* the seven sibling modules never redefine them:
|
|
6
|
+
*
|
|
7
|
+
* - it accepts an upload through Rails as `multipart/form-data`, not through
|
|
8
|
+
* the storage direct-upload path;
|
|
9
|
+
* - it is asynchronous: the create call answers with a row in `"pending"` and
|
|
10
|
+
* the work happens on a sidecar;
|
|
11
|
+
* - it has a daily quota, metered in seconds for the audio and video tools and
|
|
12
|
+
* in edits for jumpstyle, and smaller for an anonymous caller;
|
|
13
|
+
* - an anonymous caller must pass a Turnstile token
|
|
14
|
+
* ({@link ToolCaptcha.captchaToken}, sent as `cf_turnstile_token`).
|
|
15
|
+
*
|
|
16
|
+
* A trap worth naming once, because it is not the one it looks like: a tool row
|
|
17
|
+
* and its {@link Job} row are TWO records with two different ids, and both
|
|
18
|
+
* spell a finished run `"complete"`. What differs is the set of states - a job
|
|
19
|
+
* has a fifth, `"canceled"`, that no tool row can ever hold - and which of the
|
|
20
|
+
* two a given tool is polled through. Compare against
|
|
21
|
+
* {@link TOOL_TERMINAL_STATUSES} / {@link isToolTerminal} and against
|
|
22
|
+
* `JOB_TERMINAL_STATUSES` / `isJobTerminal`, never against a literal you typed
|
|
23
|
+
* from memory.
|
|
24
|
+
*
|
|
25
|
+
* Two tools (background removal, upscale) hand back a `job_id` and are waited
|
|
26
|
+
* on through `oms.jobs`; the other five are waited on by re-reading their own
|
|
27
|
+
* row. Neither case opens a loop here: both go through `pollUntilTerminal`
|
|
28
|
+
* from the jobs module.
|
|
29
|
+
*
|
|
30
|
+
* Re-exports every sibling module so nobody has to touch this file again.
|
|
31
|
+
*/
|
|
32
|
+
import { type ApiClient, Resource } from "../../http";
|
|
33
|
+
import type { BaseRecord, OperationOptions, Progress, QuotaStatus, RequestOptions, Timestamp, WaitOptions } from "../../types";
|
|
34
|
+
import { type JobsNamespace } from "../jobs";
|
|
35
|
+
import { BackgroundRemovalNamespace } from "./backgroundRemoval";
|
|
36
|
+
import { CaptionsNamespace } from "./captions";
|
|
37
|
+
import { DownloaderNamespace } from "./downloader";
|
|
38
|
+
import { JumpstyleNamespace } from "./jumpstyle";
|
|
39
|
+
import { TranscriptionNamespace } from "./transcription";
|
|
40
|
+
import { UpscaleNamespace } from "./upscale";
|
|
41
|
+
import { VocalSeparationNamespace } from "./vocalSeparation";
|
|
42
|
+
export * from "./backgroundRemoval";
|
|
43
|
+
export * from "./captions";
|
|
44
|
+
export * from "./downloader";
|
|
45
|
+
export * from "./jumpstyle";
|
|
46
|
+
export * from "./transcription";
|
|
47
|
+
export * from "./upscale";
|
|
48
|
+
export * from "./vocalSeparation";
|
|
49
|
+
/**
|
|
50
|
+
* Lifecycle of a tool row.
|
|
51
|
+
*
|
|
52
|
+
* Note `"complete"`, not `"completed"`: that spelling belongs to
|
|
53
|
+
* {@link Job}. Captions add their own intermediate states on top.
|
|
54
|
+
*/
|
|
55
|
+
export type ToolStatus = "pending" | "processing" | "complete" | "failed";
|
|
56
|
+
/** Terminal states. A row in one of these will never change again. */
|
|
57
|
+
export declare const TOOL_TERMINAL_STATUSES: readonly ToolStatus[];
|
|
58
|
+
/** Fields every tool row carries. */
|
|
59
|
+
export interface ToolRecord extends BaseRecord {
|
|
60
|
+
readonly status: ToolStatus | string;
|
|
61
|
+
/** Failure message, set once the status is `"failed"`. */
|
|
62
|
+
readonly error?: string | null;
|
|
63
|
+
readonly finished_at?: Timestamp | null;
|
|
64
|
+
readonly user_id?: string | null;
|
|
65
|
+
/** Set instead of `user_id` for an anonymous run. */
|
|
66
|
+
readonly ip_address?: string | null;
|
|
67
|
+
/** Percentage the sidecar reports while running. `null` when idle or done. */
|
|
68
|
+
readonly progress_percent?: number | null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Handle for polling an enqueued run.
|
|
72
|
+
*
|
|
73
|
+
* `job_id` and `watch_token` come back from the tools that enqueue through the
|
|
74
|
+
* generic job table (upscale, background removal). The token is what lets an
|
|
75
|
+
* anonymous caller poll `GET /jobs/:id`; the other tools are polled by reading
|
|
76
|
+
* their own row instead.
|
|
77
|
+
*/
|
|
78
|
+
export interface ToolJobHandle {
|
|
79
|
+
readonly job_id?: string;
|
|
80
|
+
/** Signed, scoped to this one job. Required when anonymous. */
|
|
81
|
+
readonly watch_token?: string;
|
|
82
|
+
}
|
|
83
|
+
/** Mixed into every create input: the anonymous caller's captcha. */
|
|
84
|
+
export interface ToolCaptcha {
|
|
85
|
+
/**
|
|
86
|
+
* Cloudflare Turnstile token, sent as `cf_turnstile_token`. Required when
|
|
87
|
+
* there is no credential; ignored when there is one.
|
|
88
|
+
*/
|
|
89
|
+
readonly captchaToken?: string;
|
|
90
|
+
}
|
|
91
|
+
/** A daily quota metered in seconds of media. */
|
|
92
|
+
export interface SecondsQuota extends QuotaStatus {
|
|
93
|
+
readonly used_seconds: number;
|
|
94
|
+
/** `null` when the account is unlimited. */
|
|
95
|
+
readonly limit_seconds: number | null;
|
|
96
|
+
/** `null` when the account is unlimited. */
|
|
97
|
+
readonly remaining_seconds: number | null;
|
|
98
|
+
}
|
|
99
|
+
/** A daily quota metered in finished edits. */
|
|
100
|
+
export interface EditsQuota extends QuotaStatus {
|
|
101
|
+
readonly used_edits: number;
|
|
102
|
+
readonly limit_edits: number | null;
|
|
103
|
+
readonly remaining_edits: number | null;
|
|
104
|
+
}
|
|
105
|
+
/** One selectable model of a tool. */
|
|
106
|
+
export interface ToolModel {
|
|
107
|
+
readonly id: string;
|
|
108
|
+
/** i18n key for the display name. The SDK does not translate. */
|
|
109
|
+
readonly translation_key?: string;
|
|
110
|
+
/** True on the model the server picks when none is named. */
|
|
111
|
+
readonly default?: boolean;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Options for a tool's `run()`: the upload half and the waiting half at once.
|
|
115
|
+
*
|
|
116
|
+
* `onProgress` fires as bytes move and again on every poll. `waitTimeoutMs` and
|
|
117
|
+
* `pollIntervalMs` are passed straight through to the jobs module's loop, which
|
|
118
|
+
* is the only loop involved.
|
|
119
|
+
*
|
|
120
|
+
* Note what `timeoutMs` does NOT mean here: it bounds one HTTP call, not the
|
|
121
|
+
* run. A ninety-minute separation with `timeoutMs: 60_000` is fine - each poll
|
|
122
|
+
* is a second-long request. Use `waitTimeoutMs` (or a `signal`) to bound the
|
|
123
|
+
* run itself.
|
|
124
|
+
*/
|
|
125
|
+
export interface ToolRunOptions extends OperationOptions, WaitOptions {
|
|
126
|
+
}
|
|
127
|
+
/** True once a tool row can never change again. */
|
|
128
|
+
export declare function isToolTerminal(status: string): boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Renders a tool row as a {@link Progress}.
|
|
131
|
+
*
|
|
132
|
+
* `total` is `undefined` while `progress_percent` is null, which is most of a
|
|
133
|
+
* run's life: the backend only fills it in while the sidecar is actually
|
|
134
|
+
* working, and inventing a denominator would be a lie a spinner then renders.
|
|
135
|
+
*/
|
|
136
|
+
export declare function toolProgress(record: ToolRecord): Progress;
|
|
137
|
+
/**
|
|
138
|
+
* The captcha form field, or nothing.
|
|
139
|
+
*
|
|
140
|
+
* Sent as `cf_turnstile_token`, and only when the caller supplied one: an empty
|
|
141
|
+
* string is not the same as an absent key to the Turnstile verifier.
|
|
142
|
+
*/
|
|
143
|
+
export declare function toolCaptchaFields(input: ToolCaptcha): Record<string, string>;
|
|
144
|
+
/**
|
|
145
|
+
* Picks an artefact URL off a finished row, or explains why there is not one.
|
|
146
|
+
*
|
|
147
|
+
* A tool row carries its outputs as URLs that are null until the run completes,
|
|
148
|
+
* so "no URL" has three different causes and a caller deserves to be told which
|
|
149
|
+
* one. Never returns an empty string.
|
|
150
|
+
*
|
|
151
|
+
* @param what Name of the artefact for the message: `"result"`, `"vocals"`.
|
|
152
|
+
* @throws {OmsError} `invalid_request` when the run failed, `conflict` when it
|
|
153
|
+
* has not finished yet, `not_found` when it finished without one - which
|
|
154
|
+
* after 24 hours means the retention sweep took it.
|
|
155
|
+
*/
|
|
156
|
+
export declare function requireToolArtifact(record: ToolRecord, url: string | null | undefined, what: string): string;
|
|
157
|
+
/**
|
|
158
|
+
* Fetches an artefact URL and buffers it.
|
|
159
|
+
*
|
|
160
|
+
* Sent on the injected transport with NO `Authorization` header, deliberately
|
|
161
|
+
* and for the same reason `storage.download` does it: these URLs are signed
|
|
162
|
+
* ActiveStorage links that redirect to the object store, and the store rejects
|
|
163
|
+
* a request that arrives with two authentication schemes. The signature in the
|
|
164
|
+
* URL is the credential.
|
|
165
|
+
*
|
|
166
|
+
* Buffers the whole artefact, because every one of them is a file a caller is
|
|
167
|
+
* about to write somewhere. Only the caller's `signal` is honoured - the
|
|
168
|
+
* transport's retry and per-request deadline do not apply to a bare `fetch`.
|
|
169
|
+
*
|
|
170
|
+
* @throws {OmsApiError} carrying the store's status when the URL has expired or
|
|
171
|
+
* the artefact has been swept.
|
|
172
|
+
*/
|
|
173
|
+
export declare function fetchToolArtifact(http: ApiClient, url: string, options?: RequestOptions): Promise<Blob>;
|
|
174
|
+
/**
|
|
175
|
+
* Waits for a run that enqueued through the generic `jobs` table, then reads
|
|
176
|
+
* the tool's own row back.
|
|
177
|
+
*
|
|
178
|
+
* Only background removal and upscale take this path - they are the two tools
|
|
179
|
+
* whose create call answers with a `job_id` and a `watch_token`. The job row is
|
|
180
|
+
* what carries progress and what an anonymous caller is allowed to read; the
|
|
181
|
+
* TOOL row is the typed answer, and it is written first (the worker marks the
|
|
182
|
+
* run complete or failed inside `perform`, and the job is settled around it),
|
|
183
|
+
* so re-reading after the job settles never races.
|
|
184
|
+
*
|
|
185
|
+
* When the server gave no `job_id` - no tracking row was minted - this falls
|
|
186
|
+
* back to polling the tool's own row, which is what the other five tools do
|
|
187
|
+
* anyway. Either way there is exactly one loop, and it lives in the jobs
|
|
188
|
+
* module.
|
|
189
|
+
*
|
|
190
|
+
* @param reread Reads the tool row. Called after the job settles.
|
|
191
|
+
* @param label What to call this in a timeout message: `"the upscale"`.
|
|
192
|
+
* @throws {OmsError} `server_error` when the job ended without completing AND
|
|
193
|
+
* left the tool row untouched - a cancelled or crashed worker. A run that
|
|
194
|
+
* genuinely failed comes back as a row with `status: "failed"`, because that
|
|
195
|
+
* is an answer.
|
|
196
|
+
*/
|
|
197
|
+
export declare function awaitToolJob<T extends ToolRecord>(jobs: JobsNamespace, created: T & ToolJobHandle, reread: (options: RequestOptions) => Promise<T>, options: ToolRunOptions, label: string): Promise<T>;
|
|
198
|
+
/** The `tools` namespace, reachable as `oms.tools`. */
|
|
199
|
+
export declare class ToolsNamespace extends Resource {
|
|
200
|
+
/** Cuts the subject out of an image. */
|
|
201
|
+
readonly backgroundRemoval: BackgroundRemovalNamespace;
|
|
202
|
+
/** Enlarges an image without the mush. */
|
|
203
|
+
readonly upscale: UpscaleNamespace;
|
|
204
|
+
/** Speech to text, with SRT and VTT output. */
|
|
205
|
+
readonly transcription: TranscriptionNamespace;
|
|
206
|
+
/** Splits a track into vocals and instrumental. */
|
|
207
|
+
readonly vocalSeparation: VocalSeparationNamespace;
|
|
208
|
+
/** Karaoke captions burned into a video. */
|
|
209
|
+
readonly captions: CaptionsNamespace;
|
|
210
|
+
/** Beat-synced clip edits over a track. */
|
|
211
|
+
readonly jumpstyle: JumpstyleNamespace;
|
|
212
|
+
/** Fetches media from a public URL. */
|
|
213
|
+
readonly downloader: DownloaderNamespace;
|
|
214
|
+
constructor(http: ApiClient);
|
|
215
|
+
}
|