@audd/sdk 1.5.10 → 1.5.12

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.cjs CHANGED
@@ -25,7 +25,7 @@ function _interopNamespace(e) {
25
25
  var path__namespace = /*#__PURE__*/_interopNamespace(path);
26
26
 
27
27
  // src/version.ts
28
- var VERSION = "1.5.8";
28
+ var VERSION = "1.5.12";
29
29
 
30
30
  // src/errors.ts
31
31
  var AudDError = class extends Error {
@@ -98,6 +98,16 @@ var AudDConnectionError = class extends AudDError {
98
98
  this.cause = cause;
99
99
  }
100
100
  };
101
+ var USER_ABORT_ERROR_NAME = "UserAbortError";
102
+ function userAbortError(cause) {
103
+ const err = new Error("Request was cancelled by the caller's AbortSignal");
104
+ err.name = USER_ABORT_ERROR_NAME;
105
+ err.cause = cause;
106
+ return err;
107
+ }
108
+ function isUserAbortError(err) {
109
+ return err !== null && typeof err === "object" && err.name === USER_ABORT_ERROR_NAME;
110
+ }
101
111
  var AudDSerializationError = class extends AudDError {
102
112
  name = "AudDSerializationError";
103
113
  rawText;
@@ -170,6 +180,55 @@ function raiseFromErrorResponse(body, opts) {
170
180
  throw new Cls(fields);
171
181
  }
172
182
 
183
+ // src/events.ts
184
+ function safeEmit(hook, event) {
185
+ if (hook === void 0) return;
186
+ try {
187
+ hook(event);
188
+ } catch {
189
+ }
190
+ }
191
+ async function emitAround(hook, method, url, fn) {
192
+ const startedAt = Date.now();
193
+ safeEmit(hook, {
194
+ kind: "request",
195
+ method,
196
+ url,
197
+ requestId: null,
198
+ httpStatus: null,
199
+ elapsedMs: null,
200
+ errorCode: null,
201
+ extras: {}
202
+ });
203
+ let resp;
204
+ try {
205
+ resp = await fn();
206
+ } catch (err) {
207
+ safeEmit(hook, {
208
+ kind: "exception",
209
+ method,
210
+ url,
211
+ requestId: null,
212
+ httpStatus: null,
213
+ elapsedMs: Date.now() - startedAt,
214
+ errorCode: null,
215
+ extras: { name: err instanceof Error ? err.name : String(err) }
216
+ });
217
+ throw err;
218
+ }
219
+ safeEmit(hook, {
220
+ kind: "response",
221
+ method,
222
+ url,
223
+ requestId: resp.requestId,
224
+ httpStatus: resp.httpStatus,
225
+ elapsedMs: Date.now() - startedAt,
226
+ errorCode: null,
227
+ extras: {}
228
+ });
229
+ return resp;
230
+ }
231
+
173
232
  // src/userAgent.ts
174
233
  function userAgent() {
175
234
  const node = typeof process !== "undefined" && process.version ? process.version.replace(/^v/, "") : "unknown";
@@ -247,6 +306,11 @@ var HttpClient = class {
247
306
  requestId: response.headers.get("x-request-id"),
248
307
  rawText: text
249
308
  };
309
+ } catch (err) {
310
+ if (userSignal?.aborted === true && err !== null && typeof err === "object" && err.name === "AbortError") {
311
+ throw userAbortError(err);
312
+ }
313
+ throw err;
250
314
  } finally {
251
315
  clearTimeout(timer);
252
316
  if (userSignal !== void 0) {
@@ -271,10 +335,8 @@ function asObject(raw) {
271
335
  }
272
336
  return void 0;
273
337
  }
274
- function requireObject(raw, what) {
275
- const o = asObject(raw);
276
- if (o === void 0) throw new TypeError(`${what}: expected object`);
277
- return o;
338
+ function asObjectLenient(raw) {
339
+ return asObject(raw) ?? {};
278
340
  }
279
341
  function asString(v) {
280
342
  return typeof v === "string" ? v : void 0;
@@ -285,6 +347,20 @@ function asNumber(v) {
285
347
  function asBoolean(v) {
286
348
  return typeof v === "boolean" ? v : void 0;
287
349
  }
350
+ function parseOffsetToSeconds(o) {
351
+ if (o === void 0 || o === null) return void 0;
352
+ if (typeof o === "number") return Number.isFinite(o) ? o : void 0;
353
+ const t = String(o).trim();
354
+ if (t === "") return void 0;
355
+ let total = 0;
356
+ for (const part of t.split(":")) {
357
+ if (part.trim() === "") return void 0;
358
+ const n = Number(part);
359
+ if (!Number.isFinite(n)) return void 0;
360
+ total = total * 60 + n;
361
+ }
362
+ return total;
363
+ }
288
364
  var STREAMING_PROVIDERS = [
289
365
  "spotify",
290
366
  "apple_music",
@@ -364,7 +440,7 @@ var RECOGNITION_KEYS = [
364
440
  "musicbrainz"
365
441
  ];
366
442
  function parseRecognitionResult(raw) {
367
- const r = requireObject(raw, "RecognitionResult");
443
+ const r = asObjectLenient(raw);
368
444
  const timecode = asString(r.timecode);
369
445
  const audioId = asNumber(r.audio_id);
370
446
  const artist = asString(r.artist);
@@ -434,10 +510,12 @@ var ENTERPRISE_MATCH_KEYS = [
434
510
  "upc",
435
511
  "song_link",
436
512
  "start_offset",
437
- "end_offset"
513
+ "end_offset",
514
+ "start_seconds",
515
+ "end_seconds"
438
516
  ];
439
517
  function parseEnterpriseMatch(raw) {
440
- const r = requireObject(raw, "EnterpriseMatch");
518
+ const r = asObjectLenient(raw);
441
519
  const score = asNumber(r.score);
442
520
  const timecode = asString(r.timecode);
443
521
  const songLink = asString(r.song_link);
@@ -454,6 +532,8 @@ function parseEnterpriseMatch(raw) {
454
532
  songLink,
455
533
  startOffset: asNumber(r.start_offset),
456
534
  endOffset: asNumber(r.end_offset),
535
+ startSeconds: void 0,
536
+ endSeconds: void 0,
457
537
  extras: pickExtras(r, ENTERPRISE_MATCH_KEYS),
458
538
  rawResponse: r,
459
539
  streamingUrl(provider) {
@@ -485,11 +565,20 @@ function parseEnterpriseMatch(raw) {
485
565
  }
486
566
  var ENTERPRISE_CHUNK_KEYS = ["songs", "offset"];
487
567
  function parseEnterpriseChunkResult(raw) {
488
- const r = requireObject(raw, "EnterpriseChunkResult");
489
- const songsRaw = Array.isArray(r.songs) ? r.songs : [];
568
+ const r = asObjectLenient(raw);
569
+ const songsRaw = Array.isArray(r.songs) ? r.songs.filter((s) => asObject(s) !== void 0) : [];
490
570
  const offset = asString(r.offset);
571
+ const base = parseOffsetToSeconds(r.offset);
572
+ const songs = songsRaw.map((s) => {
573
+ const match = parseEnterpriseMatch(s);
574
+ if (base !== void 0) {
575
+ match.startSeconds = base + (match.startOffset ?? 0) / 1e3;
576
+ match.endSeconds = base + (match.endOffset ?? 0) / 1e3;
577
+ }
578
+ return match;
579
+ });
491
580
  return {
492
- songs: songsRaw.map(parseEnterpriseMatch),
581
+ songs,
493
582
  offset,
494
583
  extras: pickExtras(r, ENTERPRISE_CHUNK_KEYS),
495
584
  rawResponse: r
@@ -497,7 +586,7 @@ function parseEnterpriseChunkResult(raw) {
497
586
  }
498
587
  var STREAM_KEYS = ["radio_id", "url", "stream_running", "longpoll_category"];
499
588
  function parseStream(raw) {
500
- const r = requireObject(raw, "Stream");
589
+ const r = asObjectLenient(raw);
501
590
  const radioId = asNumber(r.radio_id);
502
591
  const url = asString(r.url);
503
592
  const streamRunning = asBoolean(r.stream_running);
@@ -527,7 +616,7 @@ var STREAM_CALLBACK_SONG_KEYS = [
527
616
  "musicbrainz"
528
617
  ];
529
618
  function parseStreamCallbackSong(raw) {
530
- const r = requireObject(raw, "StreamCallbackSong");
619
+ const r = asObjectLenient(raw);
531
620
  const artist = asString(r.artist);
532
621
  const title = asString(r.title);
533
622
  const score = asNumber(r.score);
@@ -557,7 +646,7 @@ var STREAM_CALLBACK_MATCH_KEYS = [
557
646
  "results"
558
647
  ];
559
648
  function parseStreamCallbackMatch(raw) {
560
- const r = requireObject(raw, "StreamCallbackMatch");
649
+ const r = asObjectLenient(raw);
561
650
  const radioId = asNumber(r.radio_id);
562
651
  const resultsRaw = Array.isArray(r.results) ? r.results : [];
563
652
  const songs = resultsRaw.filter((x) => asObject(x) !== void 0).map(parseStreamCallbackSong);
@@ -579,7 +668,7 @@ var STREAM_CALLBACK_NOTIFICATION_KEYS = [
579
668
  "notification_message"
580
669
  ];
581
670
  function parseStreamCallbackNotification(raw) {
582
- const r = requireObject(raw, "StreamCallbackNotification");
671
+ const r = asObjectLenient(raw);
583
672
  const radioId = asNumber(r.radio_id);
584
673
  const code = asNumber(r.notification_code);
585
674
  const message = asString(r.notification_message);
@@ -603,7 +692,7 @@ var LYRICS_KEYS = [
603
692
  "media"
604
693
  ];
605
694
  function parseLyricsResult(raw) {
606
- const r = requireObject(raw, "LyricsResult");
695
+ const r = asObjectLenient(raw);
607
696
  const artist = asString(r.artist);
608
697
  const title = asString(r.title);
609
698
  return {
@@ -699,6 +788,22 @@ async function retry(fn, policy) {
699
788
  if (haveResp && lastResp !== void 0) return lastResp;
700
789
  throw lastError;
701
790
  }
791
+ async function runRetried(fn, policy) {
792
+ try {
793
+ return await retry(fn, policy);
794
+ } catch (err) {
795
+ if (isUserAbortError(err)) {
796
+ throw new AudDConnectionError("Request was cancelled by the caller's AbortSignal", err);
797
+ }
798
+ if (err instanceof TypeError) {
799
+ throw new AudDConnectionError(`Network error: ${err.message}`, err);
800
+ }
801
+ if (err !== null && typeof err === "object" && err.name === "AbortError") {
802
+ throw new AudDConnectionError("Request was aborted (timeout)", err);
803
+ }
804
+ throw err;
805
+ }
806
+ }
702
807
  function looksLikeUrl(s) {
703
808
  return s.startsWith("http://") || s.startsWith("https://");
704
809
  }
@@ -950,13 +1055,13 @@ function decodeOne(resp) {
950
1055
  );
951
1056
  }
952
1057
  const dict = body;
953
- if ("notification" in dict) {
1058
+ if (asObject(dict.notification) !== void 0) {
954
1059
  const notification = parseStreamCallbackNotification(dict.notification);
955
1060
  const t = dict["time"];
956
1061
  if (typeof t === "number") notification.time = t;
957
1062
  return { kind: "notification", notification };
958
1063
  }
959
- if ("result" in dict) {
1064
+ if (asObject(dict.result) !== void 0) {
960
1065
  return { kind: "match", match: parseStreamCallbackMatch(dict.result) };
961
1066
  }
962
1067
  return { kind: "keepalive" };
@@ -1035,6 +1140,7 @@ function startLongpoll(cfg) {
1035
1140
 
1036
1141
  // src/streams.ts
1037
1142
  var API_BASE = "https://api.audd.io";
1143
+ var LONGPOLL_TIMEOUT_MARGIN_SEC = 10;
1038
1144
  var NO_CALLBACK_ERROR_CODE = 19;
1039
1145
  var PREFLIGHT_NO_CALLBACK_HINT = "Longpoll won't deliver events because no callback URL is configured for this account. Set one first via streams.setCallbackUrl(...) \u2014 `https://audd.tech/empty/` is fine if you only want longpolling and don't need a real receiver. To skip this check, pass skipCallbackCheck: true.";
1040
1146
  function decodeSuccess(body, httpStatus, requestId) {
@@ -1059,34 +1165,26 @@ function decodeSuccess(body, httpStatus, requestId) {
1059
1165
  rawResponse: obj
1060
1166
  });
1061
1167
  }
1062
- async function runRetried(fn, policy) {
1063
- try {
1064
- return await retry(fn, policy);
1065
- } catch (err) {
1066
- if (err instanceof TypeError) {
1067
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1068
- }
1069
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1070
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1071
- }
1072
- throw err;
1073
- }
1074
- }
1075
1168
  var Streams = class {
1076
- constructor(http, readPolicy, mutatingPolicy, apiToken) {
1169
+ constructor(http, readPolicy, mutatingPolicy, apiToken, onEvent) {
1077
1170
  this.http = http;
1078
1171
  this.readPolicy = readPolicy;
1079
1172
  this.mutatingPolicy = mutatingPolicy;
1080
1173
  this.apiToken = apiToken;
1174
+ this.onEvent = onEvent;
1081
1175
  }
1082
1176
  http;
1083
1177
  readPolicy;
1084
1178
  mutatingPolicy;
1085
1179
  apiToken;
1180
+ onEvent;
1086
1181
  async post(path2, fields, policy) {
1087
- const resp = await runRetried(
1088
- () => this.http.postForm(`${API_BASE}/${path2}/`, fields),
1089
- policy
1182
+ const url = `${API_BASE}/${path2}/`;
1183
+ const resp = await emitAround(
1184
+ this.onEvent,
1185
+ path2,
1186
+ url,
1187
+ () => runRetried(() => this.http.postForm(url, fields), policy)
1090
1188
  );
1091
1189
  return decodeSuccess(resp.jsonBody, resp.httpStatus, resp.requestId);
1092
1190
  }
@@ -1133,11 +1231,11 @@ var Streams = class {
1133
1231
  async list() {
1134
1232
  const result = await this.post("getStreams", {}, this.readPolicy);
1135
1233
  if (!Array.isArray(result)) return [];
1136
- return result.map(parseStream);
1234
+ return result.filter((x) => asObject(x) !== void 0).map(parseStream);
1137
1235
  }
1138
1236
  /** Compute the 9-char longpoll category locally — pure, no network. */
1139
1237
  deriveLongpollCategory(radioId) {
1140
- return deriveLongpollCategory(this.apiToken, radioId);
1238
+ return deriveLongpollCategory(this.apiToken(), radioId);
1141
1239
  }
1142
1240
  /**
1143
1241
  * Parse a callback POST body into `{ match, notification }`. Pass an
@@ -1179,13 +1277,21 @@ var Streams = class {
1179
1277
  const timeoutSec = effectiveOpts.timeout ?? 50;
1180
1278
  const httpClient = this.http;
1181
1279
  const readPolicy = this.readPolicy;
1280
+ const onEvent = this.onEvent;
1281
+ const longpollUrl = `${API_BASE}/longpoll/`;
1282
+ const timeoutMs = (timeoutSec + LONGPOLL_TIMEOUT_MARGIN_SEC) * 1e3;
1182
1283
  return startLongpoll({
1183
1284
  category,
1184
1285
  timeout: timeoutSec,
1185
1286
  sinceTime: effectiveOpts.sinceTime,
1186
- fetchOnce: (params, signal) => runRetried(
1187
- () => httpClient.get(`${API_BASE}/longpoll/`, params, { signal }),
1188
- readPolicy
1287
+ fetchOnce: (params, signal) => emitAround(
1288
+ onEvent,
1289
+ "longpoll",
1290
+ longpollUrl,
1291
+ () => runRetried(
1292
+ () => httpClient.get(longpollUrl, params, { signal, timeoutMs }),
1293
+ readPolicy
1294
+ )
1189
1295
  )
1190
1296
  });
1191
1297
  }
@@ -1230,26 +1336,15 @@ function decodeSuccess2(body, httpStatus, requestId) {
1230
1336
  });
1231
1337
  }
1232
1338
  }
1233
- async function runRetried2(fn, policy) {
1234
- try {
1235
- return await retry(fn, policy);
1236
- } catch (err) {
1237
- if (err instanceof TypeError) {
1238
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1239
- }
1240
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1241
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1242
- }
1243
- throw err;
1244
- }
1245
- }
1246
1339
  var CustomCatalog = class {
1247
- constructor(http, noRetryPolicy) {
1340
+ constructor(http, noRetryPolicy, onEvent) {
1248
1341
  this.http = http;
1249
1342
  this.noRetryPolicy = noRetryPolicy;
1343
+ this.onEvent = onEvent;
1250
1344
  }
1251
1345
  http;
1252
1346
  noRetryPolicy;
1347
+ onEvent;
1253
1348
  /**
1254
1349
  * **This is NOT how you submit audio for music recognition.** For
1255
1350
  * recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
@@ -1270,42 +1365,36 @@ var CustomCatalog = class {
1270
1365
  async add(opts) {
1271
1366
  const reopen = prepareSource(opts.source);
1272
1367
  const audioId = String(opts.audioId);
1273
- const resp = await runRetried2(async () => {
1274
- const prepared = await reopen();
1275
- const fields = { ...prepared.fields, audio_id: audioId };
1276
- return this.http.postForm(UPLOAD_URL, fields);
1277
- }, this.noRetryPolicy);
1368
+ const resp = await emitAround(
1369
+ this.onEvent,
1370
+ "customCatalogAdd",
1371
+ UPLOAD_URL,
1372
+ () => runRetried(async () => {
1373
+ const prepared = await reopen();
1374
+ const fields = { ...prepared.fields, audio_id: audioId };
1375
+ return this.http.postForm(UPLOAD_URL, fields);
1376
+ }, this.noRetryPolicy)
1377
+ );
1278
1378
  decodeSuccess2(resp.jsonBody, resp.httpStatus, resp.requestId);
1279
1379
  }
1280
1380
  };
1281
1381
 
1282
1382
  // src/advanced.ts
1283
1383
  var API_BASE2 = "https://api.audd.io";
1284
- async function runRetried3(fn, policy) {
1285
- try {
1286
- return await retry(fn, policy);
1287
- } catch (err) {
1288
- if (err instanceof TypeError) {
1289
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1290
- }
1291
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1292
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1293
- }
1294
- throw err;
1295
- }
1296
- }
1297
1384
  var Advanced = class {
1298
1385
  /**
1299
1386
  * @param recognitionPolicy The findLyrics endpoint is metered and shouldn't
1300
1387
  * double-bill on post-upload read timeout: Advanced uses RECOGNITION retry
1301
1388
  * policy, not READ.
1302
1389
  */
1303
- constructor(http, recognitionPolicy) {
1390
+ constructor(http, recognitionPolicy, onEvent) {
1304
1391
  this.http = http;
1305
1392
  this.recognitionPolicy = recognitionPolicy;
1393
+ this.onEvent = onEvent;
1306
1394
  }
1307
1395
  http;
1308
1396
  recognitionPolicy;
1397
+ onEvent;
1309
1398
  /** Find lyrics by free-text query. Returns a list of matches (possibly empty). */
1310
1399
  async findLyrics(query) {
1311
1400
  const body = await this.rawRequest("findLyrics", { q: query });
@@ -1329,9 +1418,12 @@ var Advanced = class {
1329
1418
  if (v === void 0) continue;
1330
1419
  fields[k] = String(v);
1331
1420
  }
1332
- const resp = await runRetried3(
1333
- () => this.http.postForm(`${API_BASE2}/${method}/`, fields),
1334
- this.recognitionPolicy
1421
+ const url = `${API_BASE2}/${method}/`;
1422
+ const resp = await emitAround(
1423
+ this.onEvent,
1424
+ method,
1425
+ url,
1426
+ () => runRetried(() => this.http.postForm(url, fields), this.recognitionPolicy)
1335
1427
  );
1336
1428
  const body = resp.jsonBody;
1337
1429
  if (typeof body !== "object" || body === null || Array.isArray(body)) {
@@ -1347,13 +1439,6 @@ var ENTERPRISE_BASE = "https://enterprise.audd.io";
1347
1439
  var DEPRECATED_PARAMS_CODE = 51;
1348
1440
  var HTTP_CLIENT_ERROR_FLOOR2 = 400;
1349
1441
  var TOKEN_ENV_VAR = "AUDD_API_TOKEN";
1350
- function safeEmit(hook, event) {
1351
- if (hook === void 0) return;
1352
- try {
1353
- hook(event);
1354
- } catch {
1355
- }
1356
- }
1357
1442
  function resolveToken(apiToken) {
1358
1443
  if (apiToken !== void 0 && apiToken !== "") return apiToken;
1359
1444
  const env = (typeof process !== "undefined" ? process.env[TOKEN_ENV_VAR] : void 0) ?? "";
@@ -1376,8 +1461,8 @@ function buildEnterpriseFields(opts) {
1376
1461
  if (opts.skipFirstSeconds !== void 0)
1377
1462
  fields["skip_first_seconds"] = String(opts.skipFirstSeconds);
1378
1463
  if (opts.useTimecode !== void 0) fields["use_timecode"] = opts.useTimecode ? "true" : "false";
1379
- if (opts.accurateOffsets !== void 0)
1380
- fields["accurate_offsets"] = opts.accurateOffsets ? "true" : "false";
1464
+ const accurate = opts.accurateOffsets ?? true;
1465
+ fields["accurate_offsets"] = accurate ? "true" : "false";
1381
1466
  return fields;
1382
1467
  }
1383
1468
  function isDeprecationPassThrough(body) {
@@ -1426,7 +1511,7 @@ function decodeOrRaise(resp) {
1426
1511
  function decodeRecognize(resp) {
1427
1512
  const body = decodeOrRaise(resp);
1428
1513
  const result = body["result"];
1429
- if (result == null) return null;
1514
+ if (asObject(result) === void 0) return null;
1430
1515
  return parseRecognitionResult(result);
1431
1516
  }
1432
1517
  function decodeEnterprise(resp) {
@@ -1435,24 +1520,12 @@ function decodeEnterprise(resp) {
1435
1520
  if (!Array.isArray(chunks)) return [];
1436
1521
  const out = [];
1437
1522
  for (const c of chunks) {
1523
+ if (asObject(c) === void 0) continue;
1438
1524
  const chunk = parseEnterpriseChunkResult(c);
1439
1525
  out.push(...chunk.songs);
1440
1526
  }
1441
1527
  return out;
1442
1528
  }
1443
- async function runRetried4(fn, policy) {
1444
- try {
1445
- return await retry(fn, policy);
1446
- } catch (err) {
1447
- if (err instanceof TypeError) {
1448
- throw new AudDConnectionError(`Network error: ${err.message}`, err);
1449
- }
1450
- if (err !== null && typeof err === "object" && err.name === "AbortError") {
1451
- throw new AudDConnectionError("Request was aborted (timeout)", err);
1452
- }
1453
- throw err;
1454
- }
1455
- }
1456
1529
  var AudD = class {
1457
1530
  _http;
1458
1531
  _enterpriseHttp;
@@ -1510,7 +1583,8 @@ var AudD = class {
1510
1583
  this._http,
1511
1584
  this.policyFor("read"),
1512
1585
  this.policyFor("mutating"),
1513
- this._apiToken
1586
+ () => this._apiToken,
1587
+ this._onEvent
1514
1588
  );
1515
1589
  }
1516
1590
  return this._streams;
@@ -1518,14 +1592,14 @@ var AudD = class {
1518
1592
  /** Sub-namespace for the private fingerprint catalog. NOT for recognition. */
1519
1593
  get customCatalog() {
1520
1594
  if (this._customCatalog === void 0) {
1521
- this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"));
1595
+ this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"), this._onEvent);
1522
1596
  }
1523
1597
  return this._customCatalog;
1524
1598
  }
1525
1599
  /** Lyrics search + raw-request escape hatch. */
1526
1600
  get advanced() {
1527
1601
  if (this._advanced === void 0) {
1528
- this._advanced = new Advanced(this._http, this.policyFor("recognition"));
1602
+ this._advanced = new Advanced(this._http, this.policyFor("recognition"), this._onEvent);
1529
1603
  }
1530
1604
  return this._advanced;
1531
1605
  }
@@ -1541,20 +1615,11 @@ var AudD = class {
1541
1615
  const market = opts.market;
1542
1616
  const policy = this.policyFor("recognition");
1543
1617
  const url = `${API_BASE3}/`;
1544
- const startedAt = Date.now();
1545
- safeEmit(this._onEvent, {
1546
- kind: "request",
1547
- method: "recognize",
1618
+ const resp = await emitAround(
1619
+ this._onEvent,
1620
+ "recognize",
1548
1621
  url,
1549
- requestId: null,
1550
- httpStatus: null,
1551
- elapsedMs: null,
1552
- errorCode: null,
1553
- extras: {}
1554
- });
1555
- let resp;
1556
- try {
1557
- resp = await runRetried4(async () => {
1622
+ () => runRetried(async () => {
1558
1623
  const prepared = await reopen();
1559
1624
  const fields = { ...prepared.fields };
1560
1625
  if (opts.extraParameters) Object.assign(fields, opts.extraParameters);
@@ -1564,30 +1629,8 @@ var AudD = class {
1564
1629
  ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
1565
1630
  ...opts.signal !== void 0 ? { signal: opts.signal } : {}
1566
1631
  });
1567
- }, policy);
1568
- } catch (err) {
1569
- safeEmit(this._onEvent, {
1570
- kind: "exception",
1571
- method: "recognize",
1572
- url,
1573
- requestId: null,
1574
- httpStatus: null,
1575
- elapsedMs: Date.now() - startedAt,
1576
- errorCode: null,
1577
- extras: { name: err instanceof Error ? err.name : String(err) }
1578
- });
1579
- throw err;
1580
- }
1581
- safeEmit(this._onEvent, {
1582
- kind: "response",
1583
- method: "recognize",
1584
- url,
1585
- requestId: resp.requestId,
1586
- httpStatus: resp.httpStatus,
1587
- elapsedMs: Date.now() - startedAt,
1588
- errorCode: null,
1589
- extras: {}
1590
- });
1632
+ }, policy)
1633
+ );
1591
1634
  return decodeRecognize(resp);
1592
1635
  }
1593
1636
  /**
@@ -1600,14 +1643,20 @@ var AudD = class {
1600
1643
  const reopen = prepareSource(source);
1601
1644
  const extra = buildEnterpriseFields(opts);
1602
1645
  const policy = this.policyFor("recognition");
1603
- const resp = await runRetried4(async () => {
1604
- const prepared = await reopen();
1605
- const fields = { ...prepared.fields, ...extra };
1606
- return this._enterpriseHttp.postForm(`${ENTERPRISE_BASE}/`, fields, {
1607
- ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
1608
- ...opts.signal !== void 0 ? { signal: opts.signal } : {}
1609
- });
1610
- }, policy);
1646
+ const url = `${ENTERPRISE_BASE}/`;
1647
+ const resp = await emitAround(
1648
+ this._onEvent,
1649
+ "recognizeEnterprise",
1650
+ url,
1651
+ () => runRetried(async () => {
1652
+ const prepared = await reopen();
1653
+ const fields = { ...prepared.fields, ...extra };
1654
+ return this._enterpriseHttp.postForm(url, fields, {
1655
+ ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
1656
+ ...opts.signal !== void 0 ? { signal: opts.signal } : {}
1657
+ });
1658
+ }, policy)
1659
+ );
1611
1660
  return decodeEnterprise(resp);
1612
1661
  }
1613
1662
  /** Release any underlying resources. (Currently a no-op for `fetch`-based transport.) */