@audd/sdk 1.5.5 → 1.5.7

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.d.cts CHANGED
@@ -38,11 +38,15 @@ type Source = string | URL | Blob | Uint8Array;
38
38
  * - RECOGNITION — `recognize`, `recognizeEnterprise`, `advanced.findLyrics`:
39
39
  * retry on pre-upload connection failures + 5xx.
40
40
  * DO NOT retry on read-timeout-after-upload (cost protection).
41
- * - MUTATING — `streams.add`, `streams.delete`, etc., `customCatalog.add`:
41
+ * - MUTATING — `streams.add`, `streams.delete`, etc.:
42
42
  * retry only on pre-upload connection failures. DO NOT retry
43
43
  * 5xx (the side effect may have happened).
44
+ * - NONE — `customCatalog.add`: never retry. Custom-catalog upload is
45
+ * metered; auto-retry on transport failure could double-charge
46
+ * for the same audio fingerprinting. Surface a clean error and
47
+ * let the caller decide.
44
48
  */
45
- type RetryClass = "read" | "recognition" | "mutating";
49
+ type RetryClass = "read" | "recognition" | "mutating" | "none";
46
50
  interface RetryPolicy {
47
51
  retryClass: RetryClass;
48
52
  maxAttempts: number;
@@ -228,6 +232,19 @@ interface LongpollOptions {
228
232
  timeout?: number;
229
233
  /** Bypass the default-on `getCallbackUrl` preflight. */
230
234
  skipCallbackCheck?: boolean;
235
+ /**
236
+ * Radio id to subscribe to — the SDK derives the 9-char category locally
237
+ * from `(api_token, radio_id)`. Mutually exclusive with `category`. Only
238
+ * meaningful on the object-form call site (`longpoll({ radioId: 42 })`).
239
+ */
240
+ radioId?: number;
241
+ /**
242
+ * Pre-derived 9-char longpoll category. Mutually exclusive with `radioId`.
243
+ * Only meaningful on the object-form call site
244
+ * (`longpoll({ category: "abc123def" })`); the positional string form
245
+ * `longpoll("abc123def")` is the more common way to pass a category.
246
+ */
247
+ category?: string;
231
248
  }
232
249
  declare class Streams {
233
250
  private readonly http;
@@ -266,6 +283,17 @@ declare class Streams {
266
283
  /**
267
284
  * Long-poll the AudD subscription endpoint.
268
285
  *
286
+ * Two call shapes:
287
+ *
288
+ * - **Common case** — pass an options object with `radioId`; the SDK
289
+ * derives the 9-char category locally from `(api_token, radio_id)`:
290
+ * `longpoll({ radioId: 42 })`.
291
+ * - **Tokenless / pre-derived category** — pass the category as a positional
292
+ * string (`longpoll("abc123def")`) or via the object form
293
+ * (`longpoll({ category: "abc123def" })`). Useful when the category was
294
+ * shared with you (e.g. a browser/mobile client running without the
295
+ * api_token).
296
+ *
269
297
  * Returns a {@link LongpollPoll} handle with three async-iterables —
270
298
  * `matches`, `notifications`, `errors` — that are filled by a background
271
299
  * loop. Iterate them independently or in parallel via `Promise.all([...])`.
@@ -275,8 +303,16 @@ declare class Streams {
275
303
  * callback URL, and the preflight surfaces that misconfiguration as an
276
304
  * actionable {@link AudDInvalidRequestError}. Pass `skipCallbackCheck: true`
277
305
  * to bypass.
306
+ *
307
+ * Throws {@link AudDInvalidRequestError} if the object form supplies both
308
+ * `radioId` and `category`, or neither.
278
309
  */
279
310
  longpoll(category: string, opts?: LongpollOptions): Promise<LongpollPoll>;
311
+ longpoll(opts: LongpollOptions & ({
312
+ radioId: number;
313
+ } | {
314
+ category: string;
315
+ })): Promise<LongpollPoll>;
280
316
  private preflightCallbackUrl;
281
317
  }
282
318
 
@@ -286,8 +322,8 @@ interface CustomCatalogAddOptions {
286
322
  }
287
323
  declare class CustomCatalog {
288
324
  private readonly http;
289
- private readonly mutatingPolicy;
290
- constructor(http: HttpClient, mutatingPolicy: RetryPolicy);
325
+ private readonly noRetryPolicy;
326
+ constructor(http: HttpClient, noRetryPolicy: RetryPolicy);
291
327
  /**
292
328
  * **This is NOT how you submit audio for music recognition.** For
293
329
  * recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
@@ -299,6 +335,11 @@ declare class CustomCatalog {
299
335
  * Calling this again with the same `audioId` re-fingerprints that slot.
300
336
  * There is no public list/delete endpoint; track `audioId` ↔ song
301
337
  * mappings on your side.
338
+ *
339
+ * **No automatic retry.** Custom-catalog upload is metered, and a transport
340
+ * failure could otherwise cause a silent re-upload that double-charges. Any
341
+ * 5xx or pre-upload connection error surfaces as a clean exception — the
342
+ * caller decides whether to retry.
302
343
  */
303
344
  add(opts: CustomCatalogAddOptions): Promise<void>;
304
345
  }
package/dist/index.d.ts CHANGED
@@ -38,11 +38,15 @@ type Source = string | URL | Blob | Uint8Array;
38
38
  * - RECOGNITION — `recognize`, `recognizeEnterprise`, `advanced.findLyrics`:
39
39
  * retry on pre-upload connection failures + 5xx.
40
40
  * DO NOT retry on read-timeout-after-upload (cost protection).
41
- * - MUTATING — `streams.add`, `streams.delete`, etc., `customCatalog.add`:
41
+ * - MUTATING — `streams.add`, `streams.delete`, etc.:
42
42
  * retry only on pre-upload connection failures. DO NOT retry
43
43
  * 5xx (the side effect may have happened).
44
+ * - NONE — `customCatalog.add`: never retry. Custom-catalog upload is
45
+ * metered; auto-retry on transport failure could double-charge
46
+ * for the same audio fingerprinting. Surface a clean error and
47
+ * let the caller decide.
44
48
  */
45
- type RetryClass = "read" | "recognition" | "mutating";
49
+ type RetryClass = "read" | "recognition" | "mutating" | "none";
46
50
  interface RetryPolicy {
47
51
  retryClass: RetryClass;
48
52
  maxAttempts: number;
@@ -228,6 +232,19 @@ interface LongpollOptions {
228
232
  timeout?: number;
229
233
  /** Bypass the default-on `getCallbackUrl` preflight. */
230
234
  skipCallbackCheck?: boolean;
235
+ /**
236
+ * Radio id to subscribe to — the SDK derives the 9-char category locally
237
+ * from `(api_token, radio_id)`. Mutually exclusive with `category`. Only
238
+ * meaningful on the object-form call site (`longpoll({ radioId: 42 })`).
239
+ */
240
+ radioId?: number;
241
+ /**
242
+ * Pre-derived 9-char longpoll category. Mutually exclusive with `radioId`.
243
+ * Only meaningful on the object-form call site
244
+ * (`longpoll({ category: "abc123def" })`); the positional string form
245
+ * `longpoll("abc123def")` is the more common way to pass a category.
246
+ */
247
+ category?: string;
231
248
  }
232
249
  declare class Streams {
233
250
  private readonly http;
@@ -266,6 +283,17 @@ declare class Streams {
266
283
  /**
267
284
  * Long-poll the AudD subscription endpoint.
268
285
  *
286
+ * Two call shapes:
287
+ *
288
+ * - **Common case** — pass an options object with `radioId`; the SDK
289
+ * derives the 9-char category locally from `(api_token, radio_id)`:
290
+ * `longpoll({ radioId: 42 })`.
291
+ * - **Tokenless / pre-derived category** — pass the category as a positional
292
+ * string (`longpoll("abc123def")`) or via the object form
293
+ * (`longpoll({ category: "abc123def" })`). Useful when the category was
294
+ * shared with you (e.g. a browser/mobile client running without the
295
+ * api_token).
296
+ *
269
297
  * Returns a {@link LongpollPoll} handle with three async-iterables —
270
298
  * `matches`, `notifications`, `errors` — that are filled by a background
271
299
  * loop. Iterate them independently or in parallel via `Promise.all([...])`.
@@ -275,8 +303,16 @@ declare class Streams {
275
303
  * callback URL, and the preflight surfaces that misconfiguration as an
276
304
  * actionable {@link AudDInvalidRequestError}. Pass `skipCallbackCheck: true`
277
305
  * to bypass.
306
+ *
307
+ * Throws {@link AudDInvalidRequestError} if the object form supplies both
308
+ * `radioId` and `category`, or neither.
278
309
  */
279
310
  longpoll(category: string, opts?: LongpollOptions): Promise<LongpollPoll>;
311
+ longpoll(opts: LongpollOptions & ({
312
+ radioId: number;
313
+ } | {
314
+ category: string;
315
+ })): Promise<LongpollPoll>;
280
316
  private preflightCallbackUrl;
281
317
  }
282
318
 
@@ -286,8 +322,8 @@ interface CustomCatalogAddOptions {
286
322
  }
287
323
  declare class CustomCatalog {
288
324
  private readonly http;
289
- private readonly mutatingPolicy;
290
- constructor(http: HttpClient, mutatingPolicy: RetryPolicy);
325
+ private readonly noRetryPolicy;
326
+ constructor(http: HttpClient, noRetryPolicy: RetryPolicy);
291
327
  /**
292
328
  * **This is NOT how you submit audio for music recognition.** For
293
329
  * recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
@@ -299,6 +335,11 @@ declare class CustomCatalog {
299
335
  * Calling this again with the same `audioId` re-fingerprints that slot.
300
336
  * There is no public list/delete endpoint; track `audioId` ↔ song
301
337
  * mappings on your side.
338
+ *
339
+ * **No automatic retry.** Custom-catalog upload is metered, and a transport
340
+ * failure could otherwise cause a silent re-upload that double-charges. Any
341
+ * 5xx or pre-upload connection error surfaces as a clean exception — the
342
+ * caller decides whether to retry.
302
343
  */
303
344
  add(opts: CustomCatalogAddOptions): Promise<void>;
304
345
  }
package/dist/index.js CHANGED
@@ -662,6 +662,8 @@ function shouldRetryResponse(resp, retryClass) {
662
662
  return s >= HTTP_SERVER_ERROR_FLOOR;
663
663
  case "mutating":
664
664
  return false;
665
+ case "none":
666
+ return false;
665
667
  }
666
668
  }
667
669
  function shouldRetryError(err, retryClass) {
@@ -672,6 +674,8 @@ function shouldRetryError(err, retryClass) {
672
674
  return isPreUploadConnectionError(err);
673
675
  case "mutating":
674
676
  return isPreUploadConnectionError(err);
677
+ case "none":
678
+ return false;
675
679
  }
676
680
  }
677
681
  function sleep(ms) {
@@ -1147,30 +1151,42 @@ var Streams = class {
1147
1151
  parseCallback(body) {
1148
1152
  return parseCallback(body);
1149
1153
  }
1150
- /**
1151
- * Long-poll the AudD subscription endpoint.
1152
- *
1153
- * Returns a {@link LongpollPoll} handle with three async-iterables —
1154
- * `matches`, `notifications`, `errors` — that are filled by a background
1155
- * loop. Iterate them independently or in parallel via `Promise.all([...])`.
1156
- *
1157
- * Before the first request the SDK runs a one-time `getCallbackUrl`
1158
- * preflight: AudD silently discards events for accounts that haven't set a
1159
- * callback URL, and the preflight surfaces that misconfiguration as an
1160
- * actionable {@link AudDInvalidRequestError}. Pass `skipCallbackCheck: true`
1161
- * to bypass.
1162
- */
1163
- async longpoll(category, opts = {}) {
1164
- if (opts.skipCallbackCheck !== true) {
1154
+ async longpoll(arg1, opts = {}) {
1155
+ let category;
1156
+ let effectiveOpts;
1157
+ if (typeof arg1 === "string") {
1158
+ category = arg1;
1159
+ effectiveOpts = opts;
1160
+ } else {
1161
+ effectiveOpts = arg1;
1162
+ const hasRadioId = arg1.radioId !== void 0;
1163
+ const hasCategory = arg1.category !== void 0;
1164
+ if (hasRadioId && hasCategory) {
1165
+ throw new AudDInvalidRequestError({
1166
+ errorCode: 0,
1167
+ message: "longpoll(): pass exactly one of `radioId` or `category` \u2014 got both.",
1168
+ httpStatus: 0
1169
+ });
1170
+ }
1171
+ if (!hasRadioId && !hasCategory) {
1172
+ throw new AudDInvalidRequestError({
1173
+ errorCode: 0,
1174
+ message: "longpoll(): pass exactly one of `radioId` or `category` \u2014 got neither.",
1175
+ httpStatus: 0
1176
+ });
1177
+ }
1178
+ category = hasRadioId ? this.deriveLongpollCategory(arg1.radioId) : arg1.category;
1179
+ }
1180
+ if (effectiveOpts.skipCallbackCheck !== true) {
1165
1181
  await this.preflightCallbackUrl();
1166
1182
  }
1167
- const timeoutSec = opts.timeout ?? 50;
1183
+ const timeoutSec = effectiveOpts.timeout ?? 50;
1168
1184
  const httpClient = this.http;
1169
1185
  const readPolicy = this.readPolicy;
1170
1186
  return startLongpoll({
1171
1187
  category,
1172
1188
  timeout: timeoutSec,
1173
- sinceTime: opts.sinceTime,
1189
+ sinceTime: effectiveOpts.sinceTime,
1174
1190
  fetchOnce: (params, signal) => runRetried(
1175
1191
  () => httpClient.get(`${API_BASE}/longpoll/`, params, { signal }),
1176
1192
  readPolicy
@@ -1232,12 +1248,12 @@ async function runRetried2(fn, policy) {
1232
1248
  }
1233
1249
  }
1234
1250
  var CustomCatalog = class {
1235
- constructor(http, mutatingPolicy) {
1251
+ constructor(http, noRetryPolicy) {
1236
1252
  this.http = http;
1237
- this.mutatingPolicy = mutatingPolicy;
1253
+ this.noRetryPolicy = noRetryPolicy;
1238
1254
  }
1239
1255
  http;
1240
- mutatingPolicy;
1256
+ noRetryPolicy;
1241
1257
  /**
1242
1258
  * **This is NOT how you submit audio for music recognition.** For
1243
1259
  * recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
@@ -1249,6 +1265,11 @@ var CustomCatalog = class {
1249
1265
  * Calling this again with the same `audioId` re-fingerprints that slot.
1250
1266
  * There is no public list/delete endpoint; track `audioId` ↔ song
1251
1267
  * mappings on your side.
1268
+ *
1269
+ * **No automatic retry.** Custom-catalog upload is metered, and a transport
1270
+ * failure could otherwise cause a silent re-upload that double-charges. Any
1271
+ * 5xx or pre-upload connection error surfaces as a clean exception — the
1272
+ * caller decides whether to retry.
1252
1273
  */
1253
1274
  async add(opts) {
1254
1275
  const reopen = prepareSource(opts.source);
@@ -1257,7 +1278,7 @@ var CustomCatalog = class {
1257
1278
  const prepared = await reopen();
1258
1279
  const fields = { ...prepared.fields, audio_id: audioId };
1259
1280
  return this.http.postForm(UPLOAD_URL, fields);
1260
- }, this.mutatingPolicy);
1281
+ }, this.noRetryPolicy);
1261
1282
  decodeSuccess2(resp.jsonBody, resp.httpStatus, resp.requestId);
1262
1283
  }
1263
1284
  };
@@ -1501,7 +1522,7 @@ var AudD = class {
1501
1522
  /** Sub-namespace for the private fingerprint catalog. NOT for recognition. */
1502
1523
  get customCatalog() {
1503
1524
  if (this._customCatalog === void 0) {
1504
- this._customCatalog = new CustomCatalog(this._http, this.policyFor("mutating"));
1525
+ this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"));
1505
1526
  }
1506
1527
  return this._customCatalog;
1507
1528
  }