@omelhorsite/sdk 0.4.1 → 0.4.2

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/dist/index.js CHANGED
@@ -12560,11 +12560,9 @@ function songImportProgress(record) {
12560
12560
 
12561
12561
  class MusicImportsNamespace extends Resource {
12562
12562
  spotify;
12563
- srMachine;
12564
12563
  constructor(http) {
12565
12564
  super(http);
12566
12565
  this.spotify = new SpotifySyncNamespace(http);
12567
- this.srMachine = new SRMachineNamespace(http);
12568
12566
  }
12569
12567
  async list(params = {}, options = {}) {
12570
12568
  const base = {
@@ -12658,48 +12656,6 @@ class SpotifySyncNamespace extends Resource {
12658
12656
  };
12659
12657
  }
12660
12658
  }
12661
-
12662
- class SRMachineNamespace extends Resource {
12663
- async metadata(url, options = {}) {
12664
- return this.http.get("/s_r_machine/metadata", {
12665
- timeoutMs: 120000,
12666
- ...options,
12667
- query: { url },
12668
- retry: options.retry ?? false
12669
- });
12670
- }
12671
- async artwork(url, options = {}) {
12672
- return this.http.download("/s_r_machine/artwork", {
12673
- timeoutMs: 120000,
12674
- ...options,
12675
- query: { url },
12676
- retry: options.retry ?? false
12677
- });
12678
- }
12679
- async audio(url, options = {}) {
12680
- return this.http.download("/s_r_machine/audio", {
12681
- timeoutMs: 600000,
12682
- ...options,
12683
- query: { url },
12684
- retry: options.retry ?? false
12685
- });
12686
- }
12687
- async convertToOpus(file2, options = {}) {
12688
- const form = await buildFormData({ file: file2 });
12689
- const response = await this.http.raw("POST", "/s_r_machine/convert-opus", {
12690
- timeoutMs: 600000,
12691
- ...options,
12692
- body: form
12693
- });
12694
- const data = await response.blob();
12695
- return {
12696
- data,
12697
- filename: filenameFromDisposition(response.headers.get("content-disposition")),
12698
- contentType: response.headers.get("content-type") ?? undefined,
12699
- size: data.size
12700
- };
12701
- }
12702
- }
12703
12659
  function spotifySyncProgress(status) {
12704
12660
  const playlists = status.sync_progress?.playlists ?? [];
12705
12661
  let loaded = 0;
@@ -14818,23 +14774,29 @@ class CaptionsNamespace extends Resource {
14818
14774
  return this.http.get(`/caption_jobs/${encodeURIComponent(id)}`, options);
14819
14775
  }
14820
14776
  async transcribe(id, input, options = {}) {
14821
- const started = await this.http.post(`/caption_jobs/${encodeURIComponent(id)}/transcribe`, {
14777
+ const started = await this.startTranscribe(id, input, options);
14778
+ return this.settle(id, started, options, "the transcription");
14779
+ }
14780
+ async startTranscribe(id, input, options = {}) {
14781
+ return this.http.post(`/caption_jobs/${encodeURIComponent(id)}/transcribe`, {
14822
14782
  start: input.start,
14823
14783
  end: input.end,
14824
14784
  ...input.language === undefined ? {} : { language: input.language }
14825
14785
  }, { ...options, retry: options.retry ?? false });
14826
- return this.settle(id, started, options, "the transcription");
14827
14786
  }
14828
14787
  async render(id, input = {}, options = {}) {
14788
+ const started = await this.startRender(id, input, options);
14789
+ return this.settle(id, started, options, "the render");
14790
+ }
14791
+ async startRender(id, input = {}, options = {}) {
14829
14792
  const words = input.words ?? (await this.get(id, options)).words ?? [];
14830
14793
  if (words.length === 0) {
14831
14794
  throw new OmsError(`Caption job ${id} has no words to render. Transcribe a window first with transcribe(), ` + `or pass an edited list as \`words\`.`, "conflict");
14832
14795
  }
14833
- const started = await this.http.post(`/caption_jobs/${encodeURIComponent(id)}/render`, {
14796
+ return this.http.post(`/caption_jobs/${encodeURIComponent(id)}/render`, {
14834
14797
  words,
14835
14798
  ...input.style === undefined ? {} : { style: input.style }
14836
14799
  }, { ...options, retry: options.retry ?? false });
14837
- return this.settle(id, started, options, "the render");
14838
14800
  }
14839
14801
  async delete(id, options = {}) {
14840
14802
  await this.http.delete(`/caption_jobs/${encodeURIComponent(id)}`, options);
@@ -15136,6 +15098,49 @@ class UpscaleNamespace extends Resource {
15136
15098
  }
15137
15099
  }
15138
15100
 
15101
+ // src/resources/tools/srMachine.ts
15102
+ class SRMachineNamespace extends Resource {
15103
+ async metadata(url, options = {}) {
15104
+ return this.http.get("/s_r_machine/metadata", {
15105
+ timeoutMs: 120000,
15106
+ ...options,
15107
+ query: { url },
15108
+ retry: options.retry ?? false
15109
+ });
15110
+ }
15111
+ async artwork(url, options = {}) {
15112
+ return this.http.download("/s_r_machine/artwork", {
15113
+ timeoutMs: 120000,
15114
+ ...options,
15115
+ query: { url },
15116
+ retry: options.retry ?? false
15117
+ });
15118
+ }
15119
+ async audio(url, options = {}) {
15120
+ return this.http.download("/s_r_machine/audio", {
15121
+ timeoutMs: 600000,
15122
+ ...options,
15123
+ query: { url },
15124
+ retry: options.retry ?? false
15125
+ });
15126
+ }
15127
+ async convertToOpus(file2, options = {}) {
15128
+ const form = await buildFormData({ file: file2 });
15129
+ const response = await this.http.raw("POST", "/s_r_machine/convert-opus", {
15130
+ timeoutMs: 600000,
15131
+ ...options,
15132
+ body: form
15133
+ });
15134
+ const data = await response.blob();
15135
+ return {
15136
+ data,
15137
+ filename: filenameFromDisposition(response.headers.get("content-disposition")),
15138
+ contentType: response.headers.get("content-type") ?? undefined,
15139
+ size: data.size
15140
+ };
15141
+ }
15142
+ }
15143
+
15139
15144
  // src/resources/tools/vocalSeparation.ts
15140
15145
  function vocalSeparationProgress(record) {
15141
15146
  const base = toolProgress(record);
@@ -15267,6 +15272,7 @@ class ToolsNamespace extends Resource {
15267
15272
  captions;
15268
15273
  jumpstyle;
15269
15274
  downloader;
15275
+ srMachine;
15270
15276
  constructor(http) {
15271
15277
  super(http);
15272
15278
  this.backgroundRemoval = new BackgroundRemovalNamespace(http);
@@ -15276,6 +15282,7 @@ class ToolsNamespace extends Resource {
15276
15282
  this.captions = new CaptionsNamespace(http);
15277
15283
  this.jumpstyle = new JumpstyleNamespace(http);
15278
15284
  this.downloader = new DownloaderNamespace(http);
15285
+ this.srMachine = new SRMachineNamespace(http);
15279
15286
  }
15280
15287
  }
15281
15288
 
@@ -10,7 +10,6 @@
10
10
  * | `/song_imports` | one track, downloaded from a URL or found by search | any signed-in user |
11
11
  * | `/playlist_imports/preview` | read a playlist URL without importing it | any signed-in user |
12
12
  * | `/spotify_syncs/*` | mirror a Spotify account into playlists | Spotify-enabled accounts only |
13
- * | `/s_r_machine/*` | raw fetch + transcode helpers | **admins only** |
14
13
  *
15
14
  * ## An import is not a `Job`
16
15
  *
@@ -48,7 +47,7 @@
48
47
  */
49
48
  import { type ApiClient, Resource } from "../../http";
50
49
  import type { ListParams } from "../../listing";
51
- import type { BaseRecord, FileInput, FileOutput, Id, NativeFile, Paginated, Progress, RequestOptions, Timestamp, WaitOptions } from "../../types";
50
+ import type { BaseRecord, Id, Paginated, Progress, RequestOptions, Timestamp, WaitOptions } from "../../types";
52
51
  import type { DownloaderPreview } from "../tools/downloader";
53
52
  /**
54
53
  * Primary key of a song import. An **integer**, like `songs` and `playlists`
@@ -314,8 +313,6 @@ export declare function songImportProgress(record: SongImport): Progress;
314
313
  export declare class MusicImportsNamespace extends Resource {
315
314
  /** Spotify account mirroring. Spotify-enabled accounts only - see the class. */
316
315
  readonly spotify: SpotifySyncNamespace;
317
- /** Admin-only fetch and transcode helpers. */
318
- readonly srMachine: SRMachineNamespace;
319
316
  constructor(http: ApiClient);
320
317
  /**
321
318
  * `GET /song_imports` - your import history, newest first only if you ask.
@@ -788,117 +785,6 @@ export declare class SpotifySyncNamespace extends Resource {
788
785
  /** The one description of "watching a sync", shared by the two watchers. */
789
786
  private syncPollPlan;
790
787
  }
791
- /** What `GET /s_r_machine/metadata` answers. Two keys, both nullable. */
792
- export interface SRMachineMetadata {
793
- readonly title: string | null;
794
- readonly artist: string | null;
795
- }
796
- /**
797
- * The SR Machine helpers, reachable as `oms.music.imports.srMachine`.
798
- *
799
- * ## Admin only, and the check is blunt
800
- *
801
- * Every route answers `403 "You SHALL NOT use this resource"` to anyone who is
802
- * not an administrator. There is no allowlist, no scope and no per-user
803
- * quota.
804
- *
805
- * ## What it is
806
- *
807
- * Four unrelated primitives left over from the "slowed + reverb" video tool:
808
- * fetch the artwork behind a URL, fetch its audio, read its title and artist,
809
- * and transcode an arbitrary audio blob to Opus. Nothing here touches the
810
- * library - no song is created, nothing is stored, and the bytes come back in
811
- * the response body. If you want a track IN the library, use
812
- * {@link MusicImportsNamespace.create} instead; this is the raw pipe.
813
- *
814
- * Three of the four fetch a caller-supplied URL and hold a server thread while
815
- * they do it, exactly like {@link MusicImportsNamespace.previewPlaylist} - but
816
- * they have no budget of their own, so the only ceiling is the general
817
- * 600/min. The admin gate is what stands in for a budget here. Do not build a
818
- * batch loop on top of these.
819
- *
820
- * A YouTube URL carrying `?list=` is truncated at the `?list=` before the fetch,
821
- * so a link copied from inside a playlist resolves to the single video rather
822
- * than the playlist. That happens server-side, in all three fetchers.
823
- *
824
- * All three fetchers respond with `Content-Disposition: attachment` and a
825
- * fixed filename (`artwork.jpg`, `audio.opus`), so `FileOutput.filename` is
826
- * that constant rather than anything derived from the source.
827
- */
828
- export declare class SRMachineNamespace extends Resource {
829
- /**
830
- * `GET /s_r_machine/metadata` - the title and artist read off a URL.
831
- *
832
- * Both keys can be `null` for a source that carries no tags; it is a
833
- * best-effort read, not a lookup.
834
- *
835
- * Not retried by default: it parks a server thread for up to 60 seconds,
836
- * and a failure means the source refused.
837
- *
838
- * @throws {OmsAuthError} 403 `"You SHALL NOT use this resource"` for a
839
- * non-admin.
840
- * @throws {OmsApiError} 400 `"url is not allowed"` for a URL that is not
841
- * public http(s); 500 when the fetch itself fails - an upstream failure
842
- * surfaces as a server error here, not as a 502.
843
- */
844
- metadata(url: string, options?: RequestOptions): Promise<SRMachineMetadata>;
845
- /**
846
- * `GET /s_r_machine/artwork` - the cover behind a URL, as `image/jpeg`.
847
- *
848
- * Buffered fully into memory in every runtime, React Native included. It is a
849
- * cover, so that is fine; {@link SRMachineNamespace.audio} is where it is not.
850
- *
851
- * @throws {OmsAuthError} 403 for a non-admin.
852
- * @throws {OmsApiError} 400 `"url is not allowed"`; 500 on a fetch failure.
853
- */
854
- artwork(url: string, options?: RequestOptions): Promise<FileOutput>;
855
- /**
856
- * `GET /s_r_machine/audio` - the audio behind a URL, as `audio/opus`.
857
- *
858
- * The whole track is downloaded server-side, held in memory there, and sent
859
- * back in one body which this then buffers into memory again on the client.
860
- * Nothing streams. On a phone that is a whole track in the JavaScript heap,
861
- * and there is no signed-URL alternative here the way there is for library
862
- * media - this endpoint has no storage node behind it. Reach for it on
863
- * desktop, think twice on React Native, and never for a batch.
864
- *
865
- * Generous `timeoutMs` by default because a long track legitimately takes
866
- * minutes.
867
- *
868
- * @throws {OmsAuthError} 403 for a non-admin.
869
- * @throws {OmsApiError} 400 `"url is not allowed"`; 500 on a fetch failure.
870
- */
871
- audio(url: string, options?: RequestOptions): Promise<FileOutput>;
872
- /**
873
- * `POST /s_r_machine/convert-opus` - transcode an audio file to Opus.
874
- *
875
- * Multipart, field name `file`, and the only method in this namespace that
876
- * uploads. The response is `audio/opus` bytes, not JSON.
877
- *
878
- * Works on all three runtimes: a React Native `{ uri, name, type }` descriptor
879
- * goes into the `FormData` verbatim and is streamed off disk by the native
880
- * layer, while a browser or Bun caller passes a `FileInput` carrying a Blob
881
- * or a `Uint8Array`. A `ReadableStream` is buffered first, because
882
- * `FormData` has no streaming entry.
883
- *
884
- * The upload has no client-side size cap here because the server declares
885
- * none - but the CDN in front of the API rejects a request body over roughly
886
- * 100 MB with a 413 that never reaches it. There is no chunked path for this
887
- * route, so a file above that simply cannot go through it.
888
- *
889
- * The server reads the whole part into memory before transcoding, so a large
890
- * input is a large allocation on both sides.
891
- *
892
- * @throws {OmsAuthError} 403 `"You SHALL NOT use this resource"` for a
893
- * non-admin.
894
- * @throws {OmsApiError} 500 when no `file` part was sent or when the input
895
- * cannot be transcoded. Neither is a graceful 400.
896
- * @throws {TypeError} when a React Native descriptor is passed on a runtime
897
- * whose `FormData` is the web one, which would otherwise upload the literal
898
- * text `"[object Object]"` and answer 500.
899
- */
900
- convertToOpus(file: FileInput | NativeFile, options?: RequestOptions): Promise<FileOutput>;
901
- }
902
788
  /**
903
789
  * Renders a sync run as the SDK's shared {@link Progress}.
904
790
  *
@@ -506,6 +506,13 @@ export declare class CaptionsNamespace extends Resource {
506
506
  * quota is spent either way and the words still land on the row.
507
507
  */
508
508
  transcribe(id: Id, input: TranscribeCaptionInput, options?: ToolRunOptions): Promise<CaptionJob>;
509
+ /**
510
+ * `POST /caption_jobs/:id/transcribe` without waiting: answers the row as
511
+ * soon as it is `"transcribing"`. For a screen that polls {@link get} itself
512
+ * and shows progress meanwhile. Same errors as {@link transcribe}, minus the
513
+ * timeouts.
514
+ */
515
+ startTranscribe(id: Id, input: TranscribeCaptionInput, options?: RequestOptions): Promise<CaptionJob>;
509
516
  /**
510
517
  * `POST /caption_jobs/:id/render` - step 3.
511
518
  *
@@ -532,6 +539,12 @@ export declare class CaptionsNamespace extends Resource {
532
539
  * `code: "aborted"` when the signal fires. Neither cancels the render.
533
540
  */
534
541
  render(id: Id, input?: RenderCaptionInput, options?: ToolRunOptions): Promise<CaptionJob>;
542
+ /**
543
+ * `POST /caption_jobs/:id/render` without waiting: answers the row as soon
544
+ * as it is `"rendering"`. Same word handling and errors as {@link render},
545
+ * minus the timeouts.
546
+ */
547
+ startRender(id: Id, input?: RenderCaptionInput, options?: RequestOptions): Promise<CaptionJob>;
535
548
  /**
536
549
  * `DELETE /caption_jobs/:id` - drops the job and its uploaded video.
537
550
  *
@@ -40,6 +40,7 @@ import { DownloaderNamespace } from "./downloader";
40
40
  import { JumpstyleNamespace } from "./jumpstyle";
41
41
  import { TranscriptionNamespace } from "./transcription";
42
42
  import { UpscaleNamespace } from "./upscale";
43
+ import { SRMachineNamespace } from "./srMachine";
43
44
  import { VocalSeparationNamespace } from "./vocalSeparation";
44
45
  export * from "./backgroundRemoval";
45
46
  export * from "./captions";
@@ -47,6 +48,7 @@ export * from "./downloader";
47
48
  export * from "./jumpstyle";
48
49
  export * from "./transcription";
49
50
  export * from "./upscale";
51
+ export * from "./srMachine";
50
52
  export * from "./vocalSeparation";
51
53
  /**
52
54
  * Lifecycle of a tool row.
@@ -255,5 +257,7 @@ export declare class ToolsNamespace extends Resource {
255
257
  readonly jumpstyle: JumpstyleNamespace;
256
258
  /** Fetches media from a public URL. */
257
259
  readonly downloader: DownloaderNamespace;
260
+ /** Fetch, convert and package a track. Administrators only. */
261
+ readonly srMachine: SRMachineNamespace;
258
262
  constructor(http: ApiClient);
259
263
  }
@@ -0,0 +1,114 @@
1
+ /** The `tools.srMachine` namespace: fetch a track's audio, artwork and metadata by URL, and transcode audio to Opus. Administrators only. */
2
+ import { Resource } from "../../http";
3
+ import type { FileInput, FileOutput, NativeFile, RequestOptions } from "../../types";
4
+ /** What `GET /s_r_machine/metadata` answers. Two keys, both nullable. */
5
+ export interface SRMachineMetadata {
6
+ readonly title: string | null;
7
+ readonly artist: string | null;
8
+ }
9
+ /**
10
+ * The SR Machine helpers, reachable as `oms.tools.srMachine`.
11
+ *
12
+ * ## Admin only, and the check is blunt
13
+ *
14
+ * Every route answers `403 "You SHALL NOT use this resource"` to anyone who is
15
+ * not an administrator. There is no allowlist, no scope and no per-user
16
+ * quota.
17
+ *
18
+ * ## What it is
19
+ *
20
+ * Four unrelated primitives left over from the "slowed + reverb" video tool:
21
+ * fetch the artwork behind a URL, fetch its audio, read its title and artist,
22
+ * and transcode an arbitrary audio blob to Opus. Nothing here touches the
23
+ * library - no song is created, nothing is stored, and the bytes come back in
24
+ * the response body. If you want a track IN the library, use
25
+ * `oms.music.imports.create()` instead; this is the raw pipe.
26
+ *
27
+ * Three of the four fetch a caller-supplied URL and hold a server thread while
28
+ * they do it, exactly like `oms.music.imports.previewPlaylist()` - but
29
+ * they have no budget of their own, so the only ceiling is the general
30
+ * 600/min. The admin gate is what stands in for a budget here. Do not build a
31
+ * batch loop on top of these.
32
+ *
33
+ * A YouTube URL carrying `?list=` is truncated at the `?list=` before the fetch,
34
+ * so a link copied from inside a playlist resolves to the single video rather
35
+ * than the playlist. That happens server-side, in all three fetchers.
36
+ *
37
+ * All three fetchers respond with `Content-Disposition: attachment` and a
38
+ * fixed filename (`artwork.jpg`, `audio.opus`), so `FileOutput.filename` is
39
+ * that constant rather than anything derived from the source.
40
+ */
41
+ export declare class SRMachineNamespace extends Resource {
42
+ /**
43
+ * `GET /s_r_machine/metadata` - the title and artist read off a URL.
44
+ *
45
+ * Both keys can be `null` for a source that carries no tags; it is a
46
+ * best-effort read, not a lookup.
47
+ *
48
+ * Not retried by default: it parks a server thread for up to 60 seconds,
49
+ * and a failure means the source refused.
50
+ *
51
+ * @throws {OmsAuthError} 403 `"You SHALL NOT use this resource"` for a
52
+ * non-admin.
53
+ * @throws {OmsApiError} 400 `"url is not allowed"` for a URL that is not
54
+ * public http(s); 500 when the fetch itself fails - an upstream failure
55
+ * surfaces as a server error here, not as a 502.
56
+ */
57
+ metadata(url: string, options?: RequestOptions): Promise<SRMachineMetadata>;
58
+ /**
59
+ * `GET /s_r_machine/artwork` - the cover behind a URL, as `image/jpeg`.
60
+ *
61
+ * Buffered fully into memory in every runtime, React Native included. It is a
62
+ * cover, so that is fine; {@link SRMachineNamespace.audio} is where it is not.
63
+ *
64
+ * @throws {OmsAuthError} 403 for a non-admin.
65
+ * @throws {OmsApiError} 400 `"url is not allowed"`; 500 on a fetch failure.
66
+ */
67
+ artwork(url: string, options?: RequestOptions): Promise<FileOutput>;
68
+ /**
69
+ * `GET /s_r_machine/audio` - the audio behind a URL, as `audio/opus`.
70
+ *
71
+ * The whole track is downloaded server-side, held in memory there, and sent
72
+ * back in one body which this then buffers into memory again on the client.
73
+ * Nothing streams. On a phone that is a whole track in the JavaScript heap,
74
+ * and there is no signed-URL alternative here the way there is for library
75
+ * media - this endpoint has no storage node behind it. Reach for it on
76
+ * desktop, think twice on React Native, and never for a batch.
77
+ *
78
+ * Generous `timeoutMs` by default because a long track legitimately takes
79
+ * minutes.
80
+ *
81
+ * @throws {OmsAuthError} 403 for a non-admin.
82
+ * @throws {OmsApiError} 400 `"url is not allowed"`; 500 on a fetch failure.
83
+ */
84
+ audio(url: string, options?: RequestOptions): Promise<FileOutput>;
85
+ /**
86
+ * `POST /s_r_machine/convert-opus` - transcode an audio file to Opus.
87
+ *
88
+ * Multipart, field name `file`, and the only method in this namespace that
89
+ * uploads. The response is `audio/opus` bytes, not JSON.
90
+ *
91
+ * Works on all three runtimes: a React Native `{ uri, name, type }` descriptor
92
+ * goes into the `FormData` verbatim and is streamed off disk by the native
93
+ * layer, while a browser or Bun caller passes a `FileInput` carrying a Blob
94
+ * or a `Uint8Array`. A `ReadableStream` is buffered first, because
95
+ * `FormData` has no streaming entry.
96
+ *
97
+ * The upload has no client-side size cap here because the server declares
98
+ * none - but the CDN in front of the API rejects a request body over roughly
99
+ * 100 MB with a 413 that never reaches it. There is no chunked path for this
100
+ * route, so a file above that simply cannot go through it.
101
+ *
102
+ * The server reads the whole part into memory before transcoding, so a large
103
+ * input is a large allocation on both sides.
104
+ *
105
+ * @throws {OmsAuthError} 403 `"You SHALL NOT use this resource"` for a
106
+ * non-admin.
107
+ * @throws {OmsApiError} 500 when no `file` part was sent or when the input
108
+ * cannot be transcoded. Neither is a graceful 400.
109
+ * @throws {TypeError} when a React Native descriptor is passed on a runtime
110
+ * whose `FormData` is the web one, which would otherwise upload the literal
111
+ * text `"[object Object]"` and answer 500.
112
+ */
113
+ convertToOpus(file: FileInput | NativeFile, options?: RequestOptions): Promise<FileOutput>;
114
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omelhorsite/sdk",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "TypeScript SDK for the omelhorsite API. Isolate-safe: no node builtins, no environment access, no stdout.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",