@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.
Files changed (36) hide show
  1. package/README.md +321 -0
  2. package/dist/index.js +11589 -0
  3. package/dist/types/auth/device.d.ts +156 -0
  4. package/dist/types/auth/index.d.ts +127 -0
  5. package/dist/types/auth/tokens.d.ts +356 -0
  6. package/dist/types/client.d.ts +133 -0
  7. package/dist/types/errors.d.ts +202 -0
  8. package/dist/types/http.d.ts +204 -0
  9. package/dist/types/index.d.ts +33 -0
  10. package/dist/types/local/index.d.ts +42 -0
  11. package/dist/types/local/password.d.ts +169 -0
  12. package/dist/types/local/qr.d.ts +127 -0
  13. package/dist/types/local/wordlist.d.ts +26 -0
  14. package/dist/types/resources/account.d.ts +296 -0
  15. package/dist/types/resources/chests.d.ts +194 -0
  16. package/dist/types/resources/dynamicQrs.d.ts +172 -0
  17. package/dist/types/resources/forms.d.ts +331 -0
  18. package/dist/types/resources/index.d.ts +30 -0
  19. package/dist/types/resources/ipLookup.d.ts +63 -0
  20. package/dist/types/resources/jobs.d.ts +233 -0
  21. package/dist/types/resources/linkTrees.d.ts +249 -0
  22. package/dist/types/resources/notepads.d.ts +96 -0
  23. package/dist/types/resources/shortLinks.d.ts +248 -0
  24. package/dist/types/resources/storage/upload.d.ts +459 -0
  25. package/dist/types/resources/storage.d.ts +527 -0
  26. package/dist/types/resources/tickets.d.ts +236 -0
  27. package/dist/types/resources/tools/backgroundRemoval.d.ts +99 -0
  28. package/dist/types/resources/tools/captions.d.ts +318 -0
  29. package/dist/types/resources/tools/downloader.d.ts +397 -0
  30. package/dist/types/resources/tools/index.d.ts +215 -0
  31. package/dist/types/resources/tools/jumpstyle.d.ts +194 -0
  32. package/dist/types/resources/tools/transcription.d.ts +178 -0
  33. package/dist/types/resources/tools/upscale.d.ts +94 -0
  34. package/dist/types/resources/tools/vocalSeparation.d.ts +183 -0
  35. package/dist/types/types.d.ts +245 -0
  36. package/package.json +37 -0
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Jumpstyle edits: a track plus a pile of clips, cut to the beat.
3
+ *
4
+ * The odd one out of the tool family in three ways. Its quota is metered in
5
+ * finished EDITS, not seconds, so it uses {@link EditsQuota}. Its create call
6
+ * is a multi-file form: one `track` plus a `clips[]` array. And its row has no
7
+ * `"pending"` state at all - the controller forwards the files to the sidecar
8
+ * inside the request and saves the row already `"processing"`, so a create that
9
+ * returns at all has work under way.
10
+ *
11
+ * Limits, all enforced by the backend:
12
+ *
13
+ * | | |
14
+ * |---|---|
15
+ * | track | 30 MiB, and the MIME type must be `audio/*` |
16
+ * | each clip | 120 MiB, and the MIME type must be `video/*` or `image/*` |
17
+ * | whole upload | 400 MiB |
18
+ * | clips | 20 |
19
+ * | output length | clamped to `[10, 60]` seconds |
20
+ * | daily quota, anonymous | 3 edits |
21
+ * | daily quota, signed in | 15 edits |
22
+ * | `POST /jumpstyle_jobs` | 20 a minute, shared with every other expensive tool |
23
+ *
24
+ * The MIME checks read the `contentType` on each {@link FileInput}, which the
25
+ * SDK sends as the part's `Content-Type`. A file handed over with the wrong one
26
+ * - or with none, which some hosts do for an unknown extension - is a 400 that
27
+ * has nothing to do with the bytes.
28
+ *
29
+ * Read {@link JumpstyleNamespace.quota} before uploading. It is the cheapest
30
+ * preflight in the family, because one invocation is exactly one edit: if
31
+ * `remaining_edits` is `0`, the upload cannot succeed.
32
+ */
33
+ import { Resource } from "../../http";
34
+ import type { FileInput, Id, RequestOptions } from "../../types";
35
+ import { type EditsQuota, type ToolCaptcha, type ToolRecord, type ToolRunOptions } from "./index";
36
+ /**
37
+ * How busy the cut is, in the sidecar's own words.
38
+ *
39
+ * `chill` cuts once a phrase, `normal` is the default, `hyper` cuts on
40
+ * subdivisions and is what the tool did before the setting existed.
41
+ *
42
+ * Get this wrong and nothing tells you: the controller replaces an
43
+ * unrecognised value with `"normal"` rather than refusing it, so a plausible
44
+ * synonym is a silent no-op that costs a whole edit to discover.
45
+ */
46
+ export type JumpstyleDensity = "chill" | "normal" | "hyper";
47
+ /** The three the backend accepts. Anything else is silently read as `"normal"`. */
48
+ export declare const JUMPSTYLE_DENSITIES: readonly JumpstyleDensity[];
49
+ /** A jumpstyle edit. */
50
+ export interface JumpstyleJob extends ToolRecord {
51
+ readonly track_filename?: string | null;
52
+ readonly n_clips?: number | null;
53
+ /** Seconds of output. */
54
+ readonly duration?: number | null;
55
+ /** The seed that produced this cut. Reuse it to reproduce the edit. */
56
+ readonly seed?: number | null;
57
+ readonly density?: string | null;
58
+ readonly rapid_fire?: boolean | null;
59
+ /** BPM that was forced, when one was. */
60
+ readonly bpm?: number | null;
61
+ /** BPM the sidecar detected, or the forced one once the run settles. */
62
+ readonly detected_bpm?: number | null;
63
+ /** What the sidecar is doing right now. `null` unless processing. */
64
+ readonly stage?: string | null;
65
+ /** The finished video, once the status is `"complete"`. */
66
+ readonly output_url?: string | null;
67
+ }
68
+ /** Arguments for starting an edit. */
69
+ export interface CreateJumpstyleInput extends ToolCaptcha {
70
+ /** The music. Must be `audio/*`. Backend cap: 30 MiB. */
71
+ readonly track: FileInput;
72
+ /** Footage to cut. Must be `video/*` or `image/*`. At most 20, 120 MiB each. */
73
+ readonly clips: FileInput[];
74
+ /**
75
+ * Seconds of output. Clamped by the server to `[10, 60]`.
76
+ *
77
+ * Omitting it is NOT the same as leaving it to a server default: the
78
+ * controller reads a missing value as `0` and then clamps, so an omitted
79
+ * duration produces a 10-second edit. Pass one.
80
+ */
81
+ readonly duration?: number;
82
+ /**
83
+ * Reproducibility seed. Omit and the server rolls one; read it back off the
84
+ * finished row to make the same cut again. Taken modulo 100000.
85
+ */
86
+ readonly seed?: number;
87
+ /** Defaults to `"normal"`. An unknown value is silently replaced by it. */
88
+ readonly density?: JumpstyleDensity;
89
+ /** Cuts on every beat rather than every phrase. */
90
+ readonly rapidFire?: boolean;
91
+ /**
92
+ * Forces the tempo. Pass it ONLY when detection has already failed; the
93
+ * server clamps it to `[100, 220]`.
94
+ */
95
+ readonly bpm?: number;
96
+ }
97
+ /** The `jumpstyle` tool, reachable as `oms.tools.jumpstyle`. */
98
+ export declare class JumpstyleNamespace extends Resource {
99
+ /**
100
+ * `GET /jumpstyle_jobs/quota` - edits spent and left today.
101
+ *
102
+ * Cheap and anonymous-safe. The only quota in the family counted in whole
103
+ * units, which makes the preflight exact: one call to {@link create} spends
104
+ * exactly one edit, so `remaining_edits === 0` means the next upload fails.
105
+ *
106
+ * `limit_edits` and `remaining_edits` are `null` exactly when `unlimited` is
107
+ * `true`. A failed edit does not count against the day.
108
+ */
109
+ quota(options?: RequestOptions): Promise<EditsQuota>;
110
+ /**
111
+ * `POST /jumpstyle_jobs` - uploads the track and the clips, enqueues the cut.
112
+ *
113
+ * The clips go up as a `clips[]` array, which is how Rails reads a list; the
114
+ * transport appends the suffix itself, so pass a plain array.
115
+ *
116
+ * The files are forwarded to the sidecar INSIDE this request, which is why it
117
+ * is the slowest create in the family - up to 400 MiB moves twice before it
118
+ * answers - and why the row comes back already `"processing"` rather than
119
+ * `"pending"`. If the sidecar refuses, the row is destroyed rather than left
120
+ * dangling, so a failure here leaves nothing to clean up.
121
+ *
122
+ * NOT retried by default: replaying this `POST` after a 502 re-uploads the
123
+ * whole pile and spends a second edit. Pass `retry: {}` to opt back in.
124
+ *
125
+ * @throws {OmsQuotaError} 429 when the daily edits are spent. The message is
126
+ * a bare string with no `Retry-After`, so `retryAfterMs` is `undefined` -
127
+ * the wait is until midnight. The expensive-tools throttle, also a 429,
128
+ * DOES set it.
129
+ * @throws {OmsApiError} 413 when a file or the total is over a cap, 400 for a
130
+ * wrong MIME type, no clips, or more than 20 of them, 503 when the edit
131
+ * sidecar is down.
132
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
133
+ */
134
+ create(input: CreateJumpstyleInput, options?: RequestOptions): Promise<JumpstyleJob>;
135
+ /**
136
+ * `GET /jumpstyle_jobs/:id` - one poll, carrying `stage` and
137
+ * `progress_percent` while the sidecar works.
138
+ *
139
+ * Both are read live off the sidecar and are `null` the moment the row
140
+ * settles - at which point `detected_bpm` stops being the sidecar's guess
141
+ * and becomes whatever was saved on the row.
142
+ *
143
+ * @throws {OmsApiError} 404 once the 24-hour retention sweep has taken it.
144
+ * @throws {OmsAuthError} 401 when the job belongs to someone else, which
145
+ * includes an anonymous job being read from a different address.
146
+ */
147
+ get(id: Id, options?: RequestOptions): Promise<JumpstyleJob>;
148
+ /**
149
+ * Uploads, waits, and returns the finished row.
150
+ *
151
+ * The wait is {@link get} on a loop, driven by `pollUntilTerminal` from the
152
+ * jobs module - the same loop every other tool uses.
153
+ *
154
+ * Resolves with a `"failed"` row rather than throwing when the work failed.
155
+ * Pass `waitTimeoutMs` (or a `signal`) to bound the wait; there is no default
156
+ * deadline.
157
+ *
158
+ * Read {@link JumpstyleJob.seed} off the row that comes back: it is the only
159
+ * way to reproduce a cut you liked, and the server rolls a fresh one every
160
+ * time it is not given one.
161
+ *
162
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
163
+ * `code: "aborted"` when the signal fires. Neither cancels the run: pick it
164
+ * up later with {@link get}.
165
+ */
166
+ run(input: CreateJumpstyleInput, options?: ToolRunOptions): Promise<JumpstyleJob>;
167
+ /**
168
+ * `DELETE /jumpstyle_jobs/:id`.
169
+ *
170
+ * @throws {OmsApiError} 404 when it is already gone.
171
+ * @throws {OmsAuthError} 401 when the job belongs to someone else.
172
+ */
173
+ delete(id: Id, options?: RequestOptions): Promise<void>;
174
+ /**
175
+ * Downloads the finished video.
176
+ *
177
+ * Two calls: one to read the row for its `output_url`, one to fetch the
178
+ * signed URL itself with NO credential attached. Hand a row you already hold
179
+ * to {@link outputUrl} plus `fetchToolArtifact` if you would rather not pay
180
+ * for the first.
181
+ *
182
+ * @throws {OmsError} `conflict` when the edit has not finished,
183
+ * `invalid_request` when it failed, `not_found` when the artefact is gone.
184
+ */
185
+ download(id: Id, options?: RequestOptions): Promise<Blob>;
186
+ /**
187
+ * The signed URL of the finished video, from a row you already hold.
188
+ *
189
+ * It is a credential: anyone holding it can watch the video.
190
+ *
191
+ * @throws {OmsError} explaining which of the three reasons there is no URL.
192
+ */
193
+ outputUrl(record: JumpstyleJob): string;
194
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Transcription: speech to text, with SRT and VTT subtitle output.
3
+ *
4
+ * Runs on the `transcriber` service, which is the ONLY place whisper lives in
5
+ * this project. No other sidecar embeds it, and no client should call whisper
6
+ * directly; that rule is deliberate and predates this SDK.
7
+ *
8
+ * The quota is metered in seconds of audio, so it is charged on the file's
9
+ * duration at create time, not on how long the run takes. A file whose
10
+ * duration alone exceeds the daily ceiling is a 400, not a 429: it can never
11
+ * fit, no matter how long you wait.
12
+ *
13
+ * Limits, all enforced by the backend:
14
+ *
15
+ * | | |
16
+ * |---|---|
17
+ * | file size | 250 MiB (`413`) |
18
+ * | daily quota, anonymous | 15 minutes of audio |
19
+ * | daily quota, signed in | 60 minutes of audio |
20
+ * | `POST /transcriptions` | 20 a minute, shared with every other expensive tool |
21
+ *
22
+ * The daily quota is per user when signed in and per IP when not, and an
23
+ * account can be given a bigger one - or none at all, in which case
24
+ * {@link SecondsQuota.unlimited} is `true` and the numbers mean nothing. Read
25
+ * {@link TranscriptionNamespace.quota} before uploading; it is cheap, it works
26
+ * anonymously, and it is the difference between a refusal and 250 MiB spent on
27
+ * a 429.
28
+ *
29
+ * The 20-a-minute throttle is shared across background removal, upscale,
30
+ * transcription, vocal separation, captions, jumpstyle and the downloader, and
31
+ * it is keyed on the credential when there is one and on the IP when there is
32
+ * not. It answers 429 with a `Retry-After`, which the transport honours, so a
33
+ * caller that hits it waits rather than failing - unless it passed
34
+ * `retry: false`, which {@link TranscriptionNamespace.create} does by default.
35
+ */
36
+ import { Resource } from "../../http";
37
+ import type { FileInput, Id, RequestOptions } from "../../types";
38
+ import { type SecondsQuota, type ToolCaptcha, type ToolModel, type ToolRecord, type ToolRunOptions } from "./index";
39
+ /** A transcription run. */
40
+ export interface Transcription extends ToolRecord {
41
+ readonly model_id: string;
42
+ /** Audio duration charged against the quota. */
43
+ readonly duration_seconds: number;
44
+ /** Language that was requested, or `null` when it was auto-detected. */
45
+ readonly language?: string | null;
46
+ /** Language the model actually detected. */
47
+ readonly detected_language?: string | null;
48
+ readonly has_original?: boolean;
49
+ /** The transcript. Only shipped once the status is `"complete"`. */
50
+ readonly text?: string | null;
51
+ /** SubRip subtitles, once complete. */
52
+ readonly srt_url?: string | null;
53
+ /** WebVTT subtitles, once complete. */
54
+ readonly vtt_url?: string | null;
55
+ }
56
+ /** Arguments for starting a transcription. */
57
+ export interface CreateTranscriptionInput extends ToolCaptcha {
58
+ /** The audio. Sent as the `audio` form field. Backend cap: 250 MiB. */
59
+ readonly audio: FileInput;
60
+ /** Model to use. Omit for the one flagged `default` in {@link TranscriptionNamespace.models}. */
61
+ readonly modelId?: string;
62
+ /** ISO language hint. Omit to let the model detect it. */
63
+ readonly language?: string;
64
+ }
65
+ /** Which subtitle format {@link TranscriptionNamespace.subtitles} should fetch. */
66
+ export type SubtitleFormat = "srt" | "vtt";
67
+ /** The `transcription` tool, reachable as `oms.tools.transcription`. */
68
+ export declare class TranscriptionNamespace extends Resource {
69
+ /**
70
+ * `GET /transcriptions/models` - the selectable models.
71
+ *
72
+ * The controller answers `{ models: [...] }`; this unwraps it, because a
73
+ * one-key envelope is not information a caller should have to know about.
74
+ * Exactly one entry carries `default: true`, and that is the model the
75
+ * server picks when {@link CreateTranscriptionInput.modelId} is omitted.
76
+ *
77
+ * `translation_key` is an i18n key, not a display name. The SDK does not
78
+ * translate; render the id if you have no catalogue.
79
+ */
80
+ models(options?: RequestOptions): Promise<ToolModel[]>;
81
+ /**
82
+ * `GET /transcriptions/quota` - seconds spent and left today.
83
+ *
84
+ * Cheap and anonymous-safe. Call it before a long file rather than
85
+ * discovering the ceiling through a 429 after the upload.
86
+ *
87
+ * `limit_seconds` and `remaining_seconds` are `null` exactly when
88
+ * `unlimited` is `true`; `used_seconds` is always a real number. The window
89
+ * is a calendar day, so "remaining" is not a rate - it does not tick back up
90
+ * until midnight.
91
+ */
92
+ quota(options?: RequestOptions): Promise<SecondsQuota>;
93
+ /**
94
+ * `POST /transcriptions` - uploads the audio and enqueues the run.
95
+ *
96
+ * Answers with the row in `"pending"`. There is no `job_id` here: this tool
97
+ * is polled by re-reading its own row with {@link get}, which is what
98
+ * {@link run} does.
99
+ *
100
+ * NOT retried by default, unlike most of the SDK. The transport's policy
101
+ * replays a `POST` that died with a 502, and here that would re-upload up to
102
+ * 250 MiB and start a second run - charged twice against the daily quota.
103
+ * Pass `retry: {}` to opt back in.
104
+ *
105
+ * `modelId` and `language` are omitted from the form when absent rather than
106
+ * sent empty, so the server applies its own defaults.
107
+ *
108
+ * @throws {OmsQuotaError} 429 when the daily seconds are spent, or when the
109
+ * 20-a-minute expensive-tools throttle fires. Only the throttle sets
110
+ * `Retry-After`, so `retryAfterMs` being `undefined` means the wait is
111
+ * until midnight, not seconds.
112
+ * @throws {OmsApiError} 413 when the file is over 250 MiB. 400 when the file
113
+ * is missing, undecodable, names an unknown model, or is longer on its own
114
+ * than the whole daily quota.
115
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
116
+ */
117
+ create(input: CreateTranscriptionInput, options?: RequestOptions): Promise<Transcription>;
118
+ /**
119
+ * `GET /transcriptions/:id` - one poll. Carries `progress_percent` while
120
+ * processing and the text once complete.
121
+ *
122
+ * `text`, `srt_url` and `vtt_url` are `null` until the status is
123
+ * `"complete"` - deliberately, so that polling a long run does not drag the
124
+ * whole transcript across the wire every few seconds.
125
+ *
126
+ * Ownership is the caller's session, or - for an anonymous run - the IP the
127
+ * run was started from.
128
+ *
129
+ * @throws {OmsApiError} 404 once the 24-hour retention sweep has taken it.
130
+ * @throws {OmsAuthError} 401 when the run belongs to someone else, which
131
+ * includes an anonymous run being read from a different address.
132
+ */
133
+ get(id: Id, options?: RequestOptions): Promise<Transcription>;
134
+ /**
135
+ * Uploads, waits, and returns the finished row with its text.
136
+ *
137
+ * There is no `job_id` for this tool, so the wait is {@link get} on a loop -
138
+ * but the loop itself is `pollUntilTerminal` from the jobs module, the same
139
+ * one every other tool uses. Nothing here opens a second one.
140
+ *
141
+ * Resolves with a `"failed"` row rather than throwing when the work failed:
142
+ * the request cycle worked, the work did not, and only the caller knows
143
+ * whether that is an exception. Check `status` before reading `text`.
144
+ *
145
+ * Pass `waitTimeoutMs` (or a `signal`) to bound the wait; there is no default
146
+ * deadline, because a two-hour file is a legitimate run.
147
+ *
148
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
149
+ * `code: "aborted"` when the signal fires. Neither cancels the run: pick it
150
+ * up later with {@link get}.
151
+ */
152
+ run(input: CreateTranscriptionInput, options?: ToolRunOptions): Promise<Transcription>;
153
+ /**
154
+ * Downloads the subtitles of a finished run.
155
+ *
156
+ * Two calls: one to read the row for the format's URL, one to fetch the
157
+ * signed URL itself with NO credential attached. Hand a row you already hold
158
+ * to {@link subtitleUrl} plus `fetchToolArtifact` if you would rather not pay
159
+ * for the first.
160
+ *
161
+ * For the plain transcript there is nothing to download: it rides on the row
162
+ * as {@link Transcription.text} once the run completes.
163
+ *
164
+ * @throws {OmsError} `conflict` when the run has not finished,
165
+ * `invalid_request` when it failed, `not_found` when the artefact is gone -
166
+ * which after 24 hours means the retention sweep took it.
167
+ */
168
+ subtitles(id: Id, format: SubtitleFormat, options?: RequestOptions): Promise<Blob>;
169
+ /**
170
+ * The signed URL of one subtitle format, from a row you already hold.
171
+ *
172
+ * Good for handing to a player or a browser instead of moving the bytes. It
173
+ * is a credential: anyone holding it can read the subtitles.
174
+ *
175
+ * @throws {OmsError} explaining which of the three reasons there is no URL.
176
+ */
177
+ subtitleUrl(record: Transcription, format: SubtitleFormat): string;
178
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Upscaling: enlarges an image without turning it to mush.
3
+ *
4
+ * Same shape as background removal: `POST /upscales` enqueues a proxy job and
5
+ * answers with a row plus a `job_id` and, when anonymous, a `watch_token`.
6
+ *
7
+ * Backend limits: 20 MiB, and the scale must be one the upscaler advertises.
8
+ *
9
+ * Like background removal, this tool is not metered by the `Quotas` catalogue,
10
+ * so there is no `quota()` to call first.
11
+ */
12
+ import { Resource } from "../../http";
13
+ import type { FileInput, Id, RequestOptions } from "../../types";
14
+ import { type ToolCaptcha, type ToolJobHandle, type ToolRecord, type ToolRunOptions } from "./index";
15
+ /**
16
+ * Enlargement factor, as the string the API expects.
17
+ *
18
+ * A string and not a number because the backend compares it against an
19
+ * allow-list of strings; `4` and `"4"` are not the same request.
20
+ */
21
+ export type UpscaleScale = "2" | "3" | "4";
22
+ /** An upscale run. */
23
+ export interface Upscale extends ToolRecord {
24
+ readonly scale: string;
25
+ /** URL of the enlarged image, once the status is `"complete"`. */
26
+ readonly result_url?: string | null;
27
+ }
28
+ /** What `POST /upscales` answers with. */
29
+ export type UpscaleCreated = Upscale & ToolJobHandle;
30
+ /** Arguments for starting a run. */
31
+ export interface CreateUpscaleInput extends ToolCaptcha {
32
+ /** The image. Backend cap: 20 MiB. */
33
+ readonly file: FileInput;
34
+ /** Defaults to `"4"`, which is what the backend picks when none is sent. */
35
+ readonly scale?: UpscaleScale;
36
+ }
37
+ /** The `upscale` tool, reachable as `oms.tools.upscale`. */
38
+ export declare class UpscaleNamespace extends Resource {
39
+ /**
40
+ * The one polling loop, reached through the jobs namespace. Built here for
41
+ * the same reason as in every other tool: the constructor stays
42
+ * one-argument, and the wrapper is stateless.
43
+ */
44
+ private readonly jobs;
45
+ /**
46
+ * `POST /upscales` - enqueues a run and returns straight away.
47
+ *
48
+ * `scale` is omitted from the form when the caller did not pick one, so the
49
+ * server applies its own default rather than the SDK guessing at it.
50
+ *
51
+ * NOT retried by default: replaying this `POST` after a 502 re-uploads the
52
+ * image and starts a second run. Pass `retry: {}` to opt back in.
53
+ *
54
+ * @throws {OmsApiError} 400 for an oversized image, an image bomb, or a
55
+ * scale outside the allow-list.
56
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
57
+ */
58
+ create(input: CreateUpscaleInput, options?: RequestOptions): Promise<UpscaleCreated>;
59
+ /**
60
+ * `GET /upscales/:id` - one poll.
61
+ *
62
+ * @throws {OmsApiError} 404 once the 24-hour retention sweep has taken it.
63
+ * @throws {OmsAuthError} 401 when the run belongs to someone else, which
64
+ * includes an anonymous run being read from a different address.
65
+ */
66
+ get(id: Id, options?: RequestOptions): Promise<Upscale>;
67
+ /**
68
+ * Creates a run and waits for it, through `oms.jobs.wait`.
69
+ *
70
+ * Resolves with a `"failed"` row rather than throwing when the work failed.
71
+ * Pass `waitTimeoutMs` (or a `signal`) to bound the wait; there is no default
72
+ * deadline.
73
+ *
74
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
75
+ * `code: "aborted"` when the signal fires. Neither cancels the run: pick it
76
+ * up later with {@link get}.
77
+ */
78
+ run(input: CreateUpscaleInput, options?: ToolRunOptions): Promise<Upscale>;
79
+ /**
80
+ * Downloads the enlarged image of a finished run.
81
+ *
82
+ * @throws {OmsError} `conflict` when the run has not finished,
83
+ * `invalid_request` when it failed, `not_found` when the artefact is gone.
84
+ */
85
+ download(id: Id, options?: RequestOptions): Promise<Blob>;
86
+ /**
87
+ * The signed URL of a finished run's image, from a row you already hold.
88
+ *
89
+ * It is a credential: anyone holding it can read the image.
90
+ *
91
+ * @throws {OmsError} explaining which of the three reasons there is no URL.
92
+ */
93
+ resultUrl(record: Upscale): string;
94
+ }
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Vocal separation: splits a track into a vocals stem and an instrumental.
3
+ *
4
+ * Runs are serialised against a single sidecar, so a pending row reports
5
+ * {@link VocalSeparation.queue_position} - how many live runs entered the queue
6
+ * ahead of it, `0` meaning next up. Surface it: on a busy day the wait is the
7
+ * queue, not the model. {@link vocalSeparationProgress} folds it into the
8
+ * progress line for exactly that reason.
9
+ *
10
+ * There is a second door onto the same machinery, `POST /songs/:id/separate`,
11
+ * which separates a track already in the music library and leaves the stems on
12
+ * the song rather than as attachments. Both doors share one daily ceiling, and
13
+ * both are behind the same expensive-tools throttle - the twin was added to
14
+ * that throttle only after a load generator drove it on its own and pushed the
15
+ * box into swap.
16
+ *
17
+ * Limits, all enforced by the backend:
18
+ *
19
+ * | | |
20
+ * |---|---|
21
+ * | file size | 100 MiB (`413`) |
22
+ * | daily quota, anonymous | 12 minutes of audio |
23
+ * | daily quota, signed in | 30 minutes of audio |
24
+ * | `POST /vocal_separations` | 20 a minute, shared with every other expensive tool |
25
+ *
26
+ * The quota is metered in seconds of audio and charged on the file's duration
27
+ * at create time, not on how long the run takes - which matters here more than
28
+ * anywhere else, because this is the slowest tool in the family. Read
29
+ * {@link VocalSeparationNamespace.quota} before uploading; it is cheap, it
30
+ * works anonymously, and a track longer than the whole daily ceiling is a 400
31
+ * that no amount of waiting fixes.
32
+ */
33
+ import { Resource } from "../../http";
34
+ import type { FileInput, Id, Progress, RequestOptions } from "../../types";
35
+ import { type SecondsQuota, type ToolCaptcha, type ToolModel, type ToolRecord, type ToolRunOptions } from "./index";
36
+ /** Which stem to fetch. */
37
+ export type VocalStem = "vocals" | "instrumental";
38
+ /** A vocal separation run. */
39
+ export interface VocalSeparation extends ToolRecord {
40
+ readonly model_id: string;
41
+ /** Audio duration charged against the quota. */
42
+ readonly duration_seconds: number;
43
+ /** Set when the run came from the music library rather than an upload. */
44
+ readonly song_id?: Id | null;
45
+ readonly song_title?: string | null;
46
+ readonly has_original?: boolean;
47
+ readonly has_vocals?: boolean;
48
+ readonly has_instrumental?: boolean;
49
+ /**
50
+ * Live runs queued ahead of this one; `0` means next up. `null` once the run
51
+ * is processing or terminal.
52
+ */
53
+ readonly queue_position?: number | null;
54
+ /**
55
+ * Stem URLs, once complete. Both stay `null` for a song-owned separation,
56
+ * whose stems live on the song as filesystem nodes instead.
57
+ */
58
+ readonly vocals_url?: string | null;
59
+ readonly instrumental_url?: string | null;
60
+ }
61
+ /** Arguments for starting a separation. */
62
+ export interface CreateVocalSeparationInput extends ToolCaptcha {
63
+ /** The audio. Sent as the `audio` form field. Backend cap: 100 MiB. */
64
+ readonly audio: FileInput;
65
+ /** Model to use. Omit for the one flagged `default` in {@link VocalSeparationNamespace.models}. */
66
+ readonly modelId?: string;
67
+ }
68
+ /**
69
+ * Renders a separation as a {@link Progress}, queue position included.
70
+ *
71
+ * Identical to the shared `toolProgress` except while the run is queued, where
72
+ * the position is folded into `status` - `"pending (2 ahead in the queue)"`.
73
+ * That is a deliberate liberty with a field documented as the server's own
74
+ * status string: `status` is the only part of a {@link Progress} a host
75
+ * renders as text, and "pending" for forty minutes with no explanation is the
76
+ * single worst thing this tool does to a person watching it.
77
+ */
78
+ export declare function vocalSeparationProgress(record: VocalSeparation): Progress;
79
+ /** The `vocalSeparation` tool, reachable as `oms.tools.vocalSeparation`. */
80
+ export declare class VocalSeparationNamespace extends Resource {
81
+ /**
82
+ * `GET /vocal_separations/models` - the selectable models.
83
+ *
84
+ * The controller answers `{ models: [...] }`; this unwraps it. Exactly one
85
+ * entry carries `default: true`, and that is what the server picks when
86
+ * {@link CreateVocalSeparationInput.modelId} is omitted.
87
+ *
88
+ * The models differ in cost as well as quality - the default is the heaviest
89
+ * of them - but the API exposes no such ranking, so there is nothing here to
90
+ * choose on but the id.
91
+ */
92
+ models(options?: RequestOptions): Promise<ToolModel[]>;
93
+ /**
94
+ * `GET /vocal_separations/quota` - seconds spent and left today.
95
+ *
96
+ * Cheap and anonymous-safe. `limit_seconds` and `remaining_seconds` are
97
+ * `null` exactly when `unlimited` is `true`. The window is a calendar day, so
98
+ * "remaining" is not a rate: it does not tick back up until midnight.
99
+ *
100
+ * This ceiling is shared with `POST /songs/:id/separate`, so a separation
101
+ * started from the music library spends the same budget.
102
+ */
103
+ quota(options?: RequestOptions): Promise<SecondsQuota>;
104
+ /**
105
+ * `POST /vocal_separations` - uploads the audio and enqueues the run.
106
+ *
107
+ * Answers with the row in `"pending"`, already carrying its
108
+ * {@link VocalSeparation.queue_position}. There is no `job_id` here: this
109
+ * tool is polled by re-reading its own row with {@link get}.
110
+ *
111
+ * NOT retried by default: replaying this `POST` after a 502 re-uploads up to
112
+ * 100 MiB, charges the quota twice, and puts a second run behind the first in
113
+ * a queue that is already the slowest thing in the API. Pass `retry: {}` to
114
+ * opt back in.
115
+ *
116
+ * @throws {OmsQuotaError} 429 for the daily ceiling or the expensive-tools
117
+ * throttle. Read `retryAfterMs`; the throttle sets it, the daily ceiling
118
+ * does not, so `undefined` means the wait is until midnight.
119
+ * @throws {OmsApiError} 413 when the file is over 100 MiB. 400 when it is
120
+ * missing, undecodable, names an unknown model, or is longer on its own
121
+ * than the whole daily quota.
122
+ * @throws {OmsAuthError} 401 when anonymous and the captcha is missing or bad.
123
+ */
124
+ create(input: CreateVocalSeparationInput, options?: RequestOptions): Promise<VocalSeparation>;
125
+ /**
126
+ * `GET /vocal_separations/:id` - one poll, carrying `queue_position` while
127
+ * pending and `progress_percent` while processing.
128
+ *
129
+ * Never both: the position is `null` from the moment the sidecar picks the
130
+ * run up, and the percentage is `null` before that and after it finishes.
131
+ *
132
+ * @throws {OmsApiError} 404 once the 24-hour retention sweep has taken it.
133
+ * @throws {OmsAuthError} 401 when the run belongs to someone else, which
134
+ * includes an anonymous run being read from a different address.
135
+ */
136
+ get(id: Id, options?: RequestOptions): Promise<VocalSeparation>;
137
+ /**
138
+ * Uploads, waits, and returns the finished row.
139
+ *
140
+ * The wait is {@link get} on a loop, driven by `pollUntilTerminal` from the
141
+ * jobs module - the same loop every other tool uses. Progress reports carry
142
+ * the queue position while the run is still waiting its turn; see
143
+ * {@link vocalSeparationProgress}.
144
+ *
145
+ * Resolves with a `"failed"` row rather than throwing when the work failed.
146
+ * Pass `waitTimeoutMs` (or a `signal`) to bound the wait; there is no default
147
+ * deadline, and this is the tool where that matters most - a busy queue plus
148
+ * a long track is comfortably an hour.
149
+ *
150
+ * @throws {OmsTimeoutError} `code: "timeout"` when `waitTimeoutMs` elapses,
151
+ * `code: "aborted"` when the signal fires. Neither cancels the run: pick it
152
+ * up later with {@link get}.
153
+ */
154
+ run(input: CreateVocalSeparationInput, options?: ToolRunOptions): Promise<VocalSeparation>;
155
+ /**
156
+ * Downloads one stem of a finished run.
157
+ *
158
+ * Two calls: one to read the row for the stem's URL, one to fetch the signed
159
+ * URL itself with NO credential attached. Hand a row you already hold to
160
+ * {@link stemUrl} plus `fetchToolArtifact` if you would rather not pay for
161
+ * the first.
162
+ *
163
+ * @throws {OmsError} `not_found` for a song-owned separation, whose stems are
164
+ * not attachments at all; read them through the music library instead.
165
+ * `conflict` when the run has not finished, `invalid_request` when it
166
+ * failed, `not_found` again once the 24-hour sweep has taken the stems.
167
+ */
168
+ download(id: Id, stem: VocalStem, options?: RequestOptions): Promise<Blob>;
169
+ /**
170
+ * The signed URL of one stem, from a row you already hold.
171
+ *
172
+ * Good for handing to a player instead of moving the bytes. It is a
173
+ * credential: anyone holding it can read the stem.
174
+ *
175
+ * A song-owned separation is answered separately and first, because
176
+ * otherwise it looks exactly like a swept artefact - complete, no URL - and
177
+ * the caller would be told to blame a retention sweep for a run whose stems
178
+ * were never attachments in the first place.
179
+ *
180
+ * @throws {OmsError} explaining which of the four reasons there is no URL.
181
+ */
182
+ stemUrl(record: VocalSeparation, stem: VocalStem): string;
183
+ }