@omelhorsite/sdk 0.12.2 → 0.13.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/dist/index.js CHANGED
@@ -11076,6 +11076,12 @@ class AuthSessionsNamespace extends Resource {
11076
11076
  async changeEmailStart(newEmail, options = {}) {
11077
11077
  return this.http.post("/users/update_email_start", { email: newEmail }, options);
11078
11078
  }
11079
+ async verifyEmailStart(options = {}) {
11080
+ return this.http.post("/users/verify_email_start", undefined, options);
11081
+ }
11082
+ async verifyEmailComplete(code, options = {}) {
11083
+ return this.http.post("/users/verify_email_end", { code }, options);
11084
+ }
11079
11085
  async changeEmailComplete(input, options = {}) {
11080
11086
  return this.http.post("/users/update_email_end", {
11081
11087
  email: input.email,
@@ -13868,6 +13874,13 @@ class MusicSongsNamespace extends Resource {
13868
13874
  async deleteSeparation(id, options = {}) {
13869
13875
  await this.http.delete(`/songs/${encodeURIComponent(String(id))}/separation`, options);
13870
13876
  }
13877
+ async matchCandidates(id, params = {}, options = {}) {
13878
+ const body = await this.http.get(`/songs/${encodeURIComponent(String(id))}/match_candidates`, { ...options, query: { limit: params.limit } });
13879
+ return body.items ?? [];
13880
+ }
13881
+ async rematch(id, input = {}, options = {}) {
13882
+ return this.http.post(`/songs/${encodeURIComponent(String(id))}/rematch`, input.sourceUrl === undefined ? {} : { source_url: input.sourceUrl }, options);
13883
+ }
13871
13884
  async listLiked(params = {}, options = {}) {
13872
13885
  const before = params.before instanceof Date ? params.before.toISOString() : params.before;
13873
13886
  const rows = await this.http.get("/liked_songs", {
@@ -30,6 +30,13 @@ export interface User extends BaseRecord {
30
30
  readonly country_code?: string | null;
31
31
  /** `en`, `pt` or `lv`: the language of the emails sent to this account. Own account only. */
32
32
  readonly language?: string | null;
33
+ /**
34
+ * Whether the address on the account was proven by an emailed code: the same
35
+ * fact the id token's `email_verified` carries. Own account only. `false`
36
+ * on accounts older than the emailed sign-up code until they confirm once
37
+ * with `sessions.verifyEmailStart` / `verifyEmailComplete`.
38
+ */
39
+ readonly email_verified?: boolean;
33
40
  readonly email_is_public?: boolean;
34
41
  readonly gender_is_public?: boolean;
35
42
  readonly library_public?: boolean;
@@ -692,6 +692,26 @@ export declare class AuthSessionsNamespace extends Resource {
692
692
  * rejected at write time - already taken, malformed. Both codes are
693
693
  * consumed by then and the flow restarts at {@link changeEmailStart}.
694
694
  */
695
+ /**
696
+ * `POST /users/verify_email_start` - emails a code to the address already on
697
+ * the account, so an account that never proved its mailbox (one older than
698
+ * the emailed sign-up code) can. Requires a live session; an account that is
699
+ * already verified answers `200` without sending anything.
700
+ *
701
+ * Same shared `*_start` throttle as the other code-issuing calls.
702
+ *
703
+ * @throws {OmsAuthError} 401 without a live session.
704
+ */
705
+ verifyEmailStart(options?: RequestOptions): Promise<string>;
706
+ /**
707
+ * `POST /users/verify_email_end` - presents the code; answers `200` with the
708
+ * updated {@link User}, `email_verified` now `true`.
709
+ *
710
+ * @throws {OmsAuthError} 401 without a live session.
711
+ * @throws {OmsApiError} 404 `"Invalid Verification"` when the code is wrong,
712
+ * expired or burned.
713
+ */
714
+ verifyEmailComplete(code: string, options?: RequestOptions): Promise<User>;
695
715
  changeEmailComplete(input: ChangeEmailInput, options?: RequestOptions): Promise<User>;
696
716
  /**
697
717
  * `POST /users/destroy_start` - emails a deletion code to the address on the
@@ -60,6 +60,7 @@ import { Resource } from "../../http";
60
60
  import type { ListParams } from "../../listing";
61
61
  import { type BaseRecord, type FileInput, type FileOutput, type Id, type NativeFile, type Paginated, type RequestOptions, type Timestamp } from "../../types";
62
62
  import type { VocalSeparation } from "../tools/vocalSeparation";
63
+ import type { SongImport } from "./imports";
63
64
  /**
64
65
  * Primary key of a song. An **integer**, unlike most ids in this API.
65
66
  *
@@ -113,7 +114,7 @@ export interface SongArtistCredit extends Omit<BaseRecord, "id"> {
113
114
  readonly compressed_image_fs_node_id?: Id | null;
114
115
  }
115
116
  /** Where a track came from. */
116
- export type SongSourceKind = "upload" | "yt_dlp" | "spotify_sync";
117
+ export type SongSourceKind = "upload" | "yt_dlp" | "spotify_sync" | "rematch";
117
118
  /**
118
119
  * A track in the library.
119
120
  *
@@ -254,6 +255,48 @@ export interface SongSeparationStatus {
254
255
  */
255
256
  readonly job: VocalSeparation | null;
256
257
  }
258
+ /** Providers a track can be matched against. */
259
+ export type SongMatchSource = "youtube" | "soundcloud" | "bandcamp";
260
+ /**
261
+ * One take the matcher found for a track, scored the way the import scores it.
262
+ *
263
+ * A candidate with `score: null` was REJECTED and `reject_reason` says why
264
+ * ("title says 'cover', query did not"); it is returned anyway, because the
265
+ * reason is often the whole answer to "why did I get this recording". Rejected
266
+ * candidates can still be passed to {@link MusicSongsNamespace.rematch}: a
267
+ * deliberate choice is not second-guessed.
268
+ */
269
+ export interface SongMatchCandidate {
270
+ readonly source: SongMatchSource;
271
+ readonly title: string | null;
272
+ readonly uploader: string | null;
273
+ /** The page to import from. Feed it back as `sourceUrl` to take this one. */
274
+ readonly url: string | null;
275
+ /** Absent for providers whose search does not carry a runtime. */
276
+ readonly duration_s: number | null;
277
+ readonly thumbnail: string | null;
278
+ readonly source_id: string | null;
279
+ /** Higher is a better match; `null` means rejected. */
280
+ readonly score: number | null;
281
+ readonly reject_reason: string | null;
282
+ /** Where the provider's own search ranked it, before scoring. */
283
+ readonly rank: number;
284
+ /** True for the source the song's audio already came from. */
285
+ readonly current: boolean;
286
+ }
287
+ /** Arguments for {@link MusicSongsNamespace.matchCandidates}. */
288
+ export interface ListSongMatchCandidatesParams {
289
+ /** Candidates per provider. Clamped to 1..20; defaults to 8. */
290
+ readonly limit?: number;
291
+ }
292
+ /** Arguments for {@link MusicSongsNamespace.rematch}. */
293
+ export interface RematchSongInput {
294
+ /**
295
+ * The page to download from, normally a {@link SongMatchCandidate.url}.
296
+ * Omit to re-run the matcher over the track's original search terms.
297
+ */
298
+ readonly sourceUrl?: string;
299
+ }
257
300
  /** A liked track. The join row, with the whole song inlined. */
258
301
  export interface LikedSong extends Omit<BaseRecord, "id"> {
259
302
  /** Integer primary key of the like itself. NOT the song id. */
@@ -901,6 +944,49 @@ export declare class MusicSongsNamespace extends Resource {
901
944
  * @throws {OmsAuthError} 401 when the song is not yours.
902
945
  */
903
946
  deleteSeparation(id: SongId, options?: RequestOptions): Promise<void>;
947
+ /**
948
+ * `GET /songs/:id/match_candidates` - every take the matcher can find for
949
+ * this track, across all three providers, scored and ordered best first.
950
+ *
951
+ * This is the answer to "this is the wrong recording". A track imported by
952
+ * artist and title is matched, not looked up, and the classic wrong result is
953
+ * a cover: same runtime, the original artist's name in its title, and nothing
954
+ * about it that a duration check can catch. The pool shows what the matcher
955
+ * chose between, with the rejects kept and labelled.
956
+ *
957
+ * Candidates are searched live on three providers, so this is SLOW - seconds,
958
+ * not milliseconds - and worth its own timeout. Nothing is cached: two calls
959
+ * a minute apart can return different pools.
960
+ *
961
+ * Scores are comparable within one response and meaningless across two.
962
+ *
963
+ * @throws {OmsApiError} 404 for a song that is not yours, 502 when the
964
+ * downloader is unreachable.
965
+ */
966
+ matchCandidates(id: SongId, params?: ListSongMatchCandidatesParams, options?: RequestOptions): Promise<SongMatchCandidate[]>;
967
+ /**
968
+ * `POST /songs/:id/rematch` - re-download this track onto the same record.
969
+ *
970
+ * The song row survives: its id, its credits, and every playlist entry, like
971
+ * and play event that points at it. Only the audio is replaced, along with
972
+ * the fields that describe the file (runtime, codec, source). Any vocal
973
+ * separation is thrown away, because the stems came out of audio the song no
974
+ * longer has.
975
+ *
976
+ * With a `sourceUrl` the pick is taken as given - no duration or cover check
977
+ * stands between you and the recording you named. Without one the matcher
978
+ * re-runs over the track's original search terms, which is the useful move
979
+ * after a bad match rather than a repeat of it.
980
+ *
981
+ * Returns the import to poll through `music.imports`; the swap lands when it
982
+ * reaches `complete`.
983
+ *
984
+ * @throws {OmsAuthError} 401 when the song is not yours.
985
+ * @throws {OmsApiError} 400 for a `sourceUrl` that is not http(s), or when no
986
+ * `sourceUrl` is given and the track has no search terms to re-run (an
987
+ * uploaded file, for one - there is nothing to match it against).
988
+ */
989
+ rematch(id: SongId, input?: RematchSongInput, options?: RequestOptions): Promise<SongImport>;
904
990
  /**
905
991
  * `GET /liked_songs` - the caller's likes, newest first, with each song
906
992
  * inlined in full.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omelhorsite/sdk",
3
- "version": "0.12.2",
3
+ "version": "0.13.0",
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",