@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/README.md +18 -12
- package/dist/index.cjs +196 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -27
- package/dist/index.d.ts +37 -27
- package/dist/index.js +196 -147
- package/dist/index.js.map +1 -1
- package/dist/longpoll.cjs +39 -18
- package/dist/longpoll.cjs.map +1 -1
- package/dist/longpoll.d.cts +2 -2
- package/dist/longpoll.d.ts +2 -2
- package/dist/longpoll.js +39 -18
- package/dist/longpoll.js.map +1 -1
- package/dist/{longpollCore-BRyps2P4.d.cts → longpollCore-BXx8PRmC.d.cts} +8 -5
- package/dist/{longpollCore-BRyps2P4.d.ts → longpollCore-BXx8PRmC.d.ts} +8 -5
- package/package.json +19 -10
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as path from 'path';
|
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
|
|
5
5
|
// src/version.ts
|
|
6
|
-
var VERSION = "1.5.
|
|
6
|
+
var VERSION = "1.5.12";
|
|
7
7
|
|
|
8
8
|
// src/errors.ts
|
|
9
9
|
var AudDError = class extends Error {
|
|
@@ -76,6 +76,16 @@ var AudDConnectionError = class extends AudDError {
|
|
|
76
76
|
this.cause = cause;
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
|
+
var USER_ABORT_ERROR_NAME = "UserAbortError";
|
|
80
|
+
function userAbortError(cause) {
|
|
81
|
+
const err = new Error("Request was cancelled by the caller's AbortSignal");
|
|
82
|
+
err.name = USER_ABORT_ERROR_NAME;
|
|
83
|
+
err.cause = cause;
|
|
84
|
+
return err;
|
|
85
|
+
}
|
|
86
|
+
function isUserAbortError(err) {
|
|
87
|
+
return err !== null && typeof err === "object" && err.name === USER_ABORT_ERROR_NAME;
|
|
88
|
+
}
|
|
79
89
|
var AudDSerializationError = class extends AudDError {
|
|
80
90
|
name = "AudDSerializationError";
|
|
81
91
|
rawText;
|
|
@@ -148,6 +158,55 @@ function raiseFromErrorResponse(body, opts) {
|
|
|
148
158
|
throw new Cls(fields);
|
|
149
159
|
}
|
|
150
160
|
|
|
161
|
+
// src/events.ts
|
|
162
|
+
function safeEmit(hook, event) {
|
|
163
|
+
if (hook === void 0) return;
|
|
164
|
+
try {
|
|
165
|
+
hook(event);
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
async function emitAround(hook, method, url, fn) {
|
|
170
|
+
const startedAt = Date.now();
|
|
171
|
+
safeEmit(hook, {
|
|
172
|
+
kind: "request",
|
|
173
|
+
method,
|
|
174
|
+
url,
|
|
175
|
+
requestId: null,
|
|
176
|
+
httpStatus: null,
|
|
177
|
+
elapsedMs: null,
|
|
178
|
+
errorCode: null,
|
|
179
|
+
extras: {}
|
|
180
|
+
});
|
|
181
|
+
let resp;
|
|
182
|
+
try {
|
|
183
|
+
resp = await fn();
|
|
184
|
+
} catch (err) {
|
|
185
|
+
safeEmit(hook, {
|
|
186
|
+
kind: "exception",
|
|
187
|
+
method,
|
|
188
|
+
url,
|
|
189
|
+
requestId: null,
|
|
190
|
+
httpStatus: null,
|
|
191
|
+
elapsedMs: Date.now() - startedAt,
|
|
192
|
+
errorCode: null,
|
|
193
|
+
extras: { name: err instanceof Error ? err.name : String(err) }
|
|
194
|
+
});
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
safeEmit(hook, {
|
|
198
|
+
kind: "response",
|
|
199
|
+
method,
|
|
200
|
+
url,
|
|
201
|
+
requestId: resp.requestId,
|
|
202
|
+
httpStatus: resp.httpStatus,
|
|
203
|
+
elapsedMs: Date.now() - startedAt,
|
|
204
|
+
errorCode: null,
|
|
205
|
+
extras: {}
|
|
206
|
+
});
|
|
207
|
+
return resp;
|
|
208
|
+
}
|
|
209
|
+
|
|
151
210
|
// src/userAgent.ts
|
|
152
211
|
function userAgent() {
|
|
153
212
|
const node = typeof process !== "undefined" && process.version ? process.version.replace(/^v/, "") : "unknown";
|
|
@@ -225,6 +284,11 @@ var HttpClient = class {
|
|
|
225
284
|
requestId: response.headers.get("x-request-id"),
|
|
226
285
|
rawText: text
|
|
227
286
|
};
|
|
287
|
+
} catch (err) {
|
|
288
|
+
if (userSignal?.aborted === true && err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
289
|
+
throw userAbortError(err);
|
|
290
|
+
}
|
|
291
|
+
throw err;
|
|
228
292
|
} finally {
|
|
229
293
|
clearTimeout(timer);
|
|
230
294
|
if (userSignal !== void 0) {
|
|
@@ -249,10 +313,8 @@ function asObject(raw) {
|
|
|
249
313
|
}
|
|
250
314
|
return void 0;
|
|
251
315
|
}
|
|
252
|
-
function
|
|
253
|
-
|
|
254
|
-
if (o === void 0) throw new TypeError(`${what}: expected object`);
|
|
255
|
-
return o;
|
|
316
|
+
function asObjectLenient(raw) {
|
|
317
|
+
return asObject(raw) ?? {};
|
|
256
318
|
}
|
|
257
319
|
function asString(v) {
|
|
258
320
|
return typeof v === "string" ? v : void 0;
|
|
@@ -263,6 +325,20 @@ function asNumber(v) {
|
|
|
263
325
|
function asBoolean(v) {
|
|
264
326
|
return typeof v === "boolean" ? v : void 0;
|
|
265
327
|
}
|
|
328
|
+
function parseOffsetToSeconds(o) {
|
|
329
|
+
if (o === void 0 || o === null) return void 0;
|
|
330
|
+
if (typeof o === "number") return Number.isFinite(o) ? o : void 0;
|
|
331
|
+
const t = String(o).trim();
|
|
332
|
+
if (t === "") return void 0;
|
|
333
|
+
let total = 0;
|
|
334
|
+
for (const part of t.split(":")) {
|
|
335
|
+
if (part.trim() === "") return void 0;
|
|
336
|
+
const n = Number(part);
|
|
337
|
+
if (!Number.isFinite(n)) return void 0;
|
|
338
|
+
total = total * 60 + n;
|
|
339
|
+
}
|
|
340
|
+
return total;
|
|
341
|
+
}
|
|
266
342
|
var STREAMING_PROVIDERS = [
|
|
267
343
|
"spotify",
|
|
268
344
|
"apple_music",
|
|
@@ -342,7 +418,7 @@ var RECOGNITION_KEYS = [
|
|
|
342
418
|
"musicbrainz"
|
|
343
419
|
];
|
|
344
420
|
function parseRecognitionResult(raw) {
|
|
345
|
-
const r =
|
|
421
|
+
const r = asObjectLenient(raw);
|
|
346
422
|
const timecode = asString(r.timecode);
|
|
347
423
|
const audioId = asNumber(r.audio_id);
|
|
348
424
|
const artist = asString(r.artist);
|
|
@@ -412,10 +488,12 @@ var ENTERPRISE_MATCH_KEYS = [
|
|
|
412
488
|
"upc",
|
|
413
489
|
"song_link",
|
|
414
490
|
"start_offset",
|
|
415
|
-
"end_offset"
|
|
491
|
+
"end_offset",
|
|
492
|
+
"start_seconds",
|
|
493
|
+
"end_seconds"
|
|
416
494
|
];
|
|
417
495
|
function parseEnterpriseMatch(raw) {
|
|
418
|
-
const r =
|
|
496
|
+
const r = asObjectLenient(raw);
|
|
419
497
|
const score = asNumber(r.score);
|
|
420
498
|
const timecode = asString(r.timecode);
|
|
421
499
|
const songLink = asString(r.song_link);
|
|
@@ -432,6 +510,8 @@ function parseEnterpriseMatch(raw) {
|
|
|
432
510
|
songLink,
|
|
433
511
|
startOffset: asNumber(r.start_offset),
|
|
434
512
|
endOffset: asNumber(r.end_offset),
|
|
513
|
+
startSeconds: void 0,
|
|
514
|
+
endSeconds: void 0,
|
|
435
515
|
extras: pickExtras(r, ENTERPRISE_MATCH_KEYS),
|
|
436
516
|
rawResponse: r,
|
|
437
517
|
streamingUrl(provider) {
|
|
@@ -463,11 +543,20 @@ function parseEnterpriseMatch(raw) {
|
|
|
463
543
|
}
|
|
464
544
|
var ENTERPRISE_CHUNK_KEYS = ["songs", "offset"];
|
|
465
545
|
function parseEnterpriseChunkResult(raw) {
|
|
466
|
-
const r =
|
|
467
|
-
const songsRaw = Array.isArray(r.songs) ? r.songs : [];
|
|
546
|
+
const r = asObjectLenient(raw);
|
|
547
|
+
const songsRaw = Array.isArray(r.songs) ? r.songs.filter((s) => asObject(s) !== void 0) : [];
|
|
468
548
|
const offset = asString(r.offset);
|
|
549
|
+
const base = parseOffsetToSeconds(r.offset);
|
|
550
|
+
const songs = songsRaw.map((s) => {
|
|
551
|
+
const match = parseEnterpriseMatch(s);
|
|
552
|
+
if (base !== void 0) {
|
|
553
|
+
match.startSeconds = base + (match.startOffset ?? 0) / 1e3;
|
|
554
|
+
match.endSeconds = base + (match.endOffset ?? 0) / 1e3;
|
|
555
|
+
}
|
|
556
|
+
return match;
|
|
557
|
+
});
|
|
469
558
|
return {
|
|
470
|
-
songs
|
|
559
|
+
songs,
|
|
471
560
|
offset,
|
|
472
561
|
extras: pickExtras(r, ENTERPRISE_CHUNK_KEYS),
|
|
473
562
|
rawResponse: r
|
|
@@ -475,7 +564,7 @@ function parseEnterpriseChunkResult(raw) {
|
|
|
475
564
|
}
|
|
476
565
|
var STREAM_KEYS = ["radio_id", "url", "stream_running", "longpoll_category"];
|
|
477
566
|
function parseStream(raw) {
|
|
478
|
-
const r =
|
|
567
|
+
const r = asObjectLenient(raw);
|
|
479
568
|
const radioId = asNumber(r.radio_id);
|
|
480
569
|
const url = asString(r.url);
|
|
481
570
|
const streamRunning = asBoolean(r.stream_running);
|
|
@@ -505,7 +594,7 @@ var STREAM_CALLBACK_SONG_KEYS = [
|
|
|
505
594
|
"musicbrainz"
|
|
506
595
|
];
|
|
507
596
|
function parseStreamCallbackSong(raw) {
|
|
508
|
-
const r =
|
|
597
|
+
const r = asObjectLenient(raw);
|
|
509
598
|
const artist = asString(r.artist);
|
|
510
599
|
const title = asString(r.title);
|
|
511
600
|
const score = asNumber(r.score);
|
|
@@ -535,7 +624,7 @@ var STREAM_CALLBACK_MATCH_KEYS = [
|
|
|
535
624
|
"results"
|
|
536
625
|
];
|
|
537
626
|
function parseStreamCallbackMatch(raw) {
|
|
538
|
-
const r =
|
|
627
|
+
const r = asObjectLenient(raw);
|
|
539
628
|
const radioId = asNumber(r.radio_id);
|
|
540
629
|
const resultsRaw = Array.isArray(r.results) ? r.results : [];
|
|
541
630
|
const songs = resultsRaw.filter((x) => asObject(x) !== void 0).map(parseStreamCallbackSong);
|
|
@@ -557,7 +646,7 @@ var STREAM_CALLBACK_NOTIFICATION_KEYS = [
|
|
|
557
646
|
"notification_message"
|
|
558
647
|
];
|
|
559
648
|
function parseStreamCallbackNotification(raw) {
|
|
560
|
-
const r =
|
|
649
|
+
const r = asObjectLenient(raw);
|
|
561
650
|
const radioId = asNumber(r.radio_id);
|
|
562
651
|
const code = asNumber(r.notification_code);
|
|
563
652
|
const message = asString(r.notification_message);
|
|
@@ -581,7 +670,7 @@ var LYRICS_KEYS = [
|
|
|
581
670
|
"media"
|
|
582
671
|
];
|
|
583
672
|
function parseLyricsResult(raw) {
|
|
584
|
-
const r =
|
|
673
|
+
const r = asObjectLenient(raw);
|
|
585
674
|
const artist = asString(r.artist);
|
|
586
675
|
const title = asString(r.title);
|
|
587
676
|
return {
|
|
@@ -677,6 +766,22 @@ async function retry(fn, policy) {
|
|
|
677
766
|
if (haveResp && lastResp !== void 0) return lastResp;
|
|
678
767
|
throw lastError;
|
|
679
768
|
}
|
|
769
|
+
async function runRetried(fn, policy) {
|
|
770
|
+
try {
|
|
771
|
+
return await retry(fn, policy);
|
|
772
|
+
} catch (err) {
|
|
773
|
+
if (isUserAbortError(err)) {
|
|
774
|
+
throw new AudDConnectionError("Request was cancelled by the caller's AbortSignal", err);
|
|
775
|
+
}
|
|
776
|
+
if (err instanceof TypeError) {
|
|
777
|
+
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
778
|
+
}
|
|
779
|
+
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
780
|
+
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
781
|
+
}
|
|
782
|
+
throw err;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
680
785
|
function looksLikeUrl(s) {
|
|
681
786
|
return s.startsWith("http://") || s.startsWith("https://");
|
|
682
787
|
}
|
|
@@ -928,13 +1033,13 @@ function decodeOne(resp) {
|
|
|
928
1033
|
);
|
|
929
1034
|
}
|
|
930
1035
|
const dict = body;
|
|
931
|
-
if (
|
|
1036
|
+
if (asObject(dict.notification) !== void 0) {
|
|
932
1037
|
const notification = parseStreamCallbackNotification(dict.notification);
|
|
933
1038
|
const t = dict["time"];
|
|
934
1039
|
if (typeof t === "number") notification.time = t;
|
|
935
1040
|
return { kind: "notification", notification };
|
|
936
1041
|
}
|
|
937
|
-
if (
|
|
1042
|
+
if (asObject(dict.result) !== void 0) {
|
|
938
1043
|
return { kind: "match", match: parseStreamCallbackMatch(dict.result) };
|
|
939
1044
|
}
|
|
940
1045
|
return { kind: "keepalive" };
|
|
@@ -1013,6 +1118,7 @@ function startLongpoll(cfg) {
|
|
|
1013
1118
|
|
|
1014
1119
|
// src/streams.ts
|
|
1015
1120
|
var API_BASE = "https://api.audd.io";
|
|
1121
|
+
var LONGPOLL_TIMEOUT_MARGIN_SEC = 10;
|
|
1016
1122
|
var NO_CALLBACK_ERROR_CODE = 19;
|
|
1017
1123
|
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.";
|
|
1018
1124
|
function decodeSuccess(body, httpStatus, requestId) {
|
|
@@ -1037,34 +1143,26 @@ function decodeSuccess(body, httpStatus, requestId) {
|
|
|
1037
1143
|
rawResponse: obj
|
|
1038
1144
|
});
|
|
1039
1145
|
}
|
|
1040
|
-
async function runRetried(fn, policy) {
|
|
1041
|
-
try {
|
|
1042
|
-
return await retry(fn, policy);
|
|
1043
|
-
} catch (err) {
|
|
1044
|
-
if (err instanceof TypeError) {
|
|
1045
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1046
|
-
}
|
|
1047
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1048
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1049
|
-
}
|
|
1050
|
-
throw err;
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
1146
|
var Streams = class {
|
|
1054
|
-
constructor(http, readPolicy, mutatingPolicy, apiToken) {
|
|
1147
|
+
constructor(http, readPolicy, mutatingPolicy, apiToken, onEvent) {
|
|
1055
1148
|
this.http = http;
|
|
1056
1149
|
this.readPolicy = readPolicy;
|
|
1057
1150
|
this.mutatingPolicy = mutatingPolicy;
|
|
1058
1151
|
this.apiToken = apiToken;
|
|
1152
|
+
this.onEvent = onEvent;
|
|
1059
1153
|
}
|
|
1060
1154
|
http;
|
|
1061
1155
|
readPolicy;
|
|
1062
1156
|
mutatingPolicy;
|
|
1063
1157
|
apiToken;
|
|
1158
|
+
onEvent;
|
|
1064
1159
|
async post(path2, fields, policy) {
|
|
1065
|
-
const
|
|
1066
|
-
|
|
1067
|
-
|
|
1160
|
+
const url = `${API_BASE}/${path2}/`;
|
|
1161
|
+
const resp = await emitAround(
|
|
1162
|
+
this.onEvent,
|
|
1163
|
+
path2,
|
|
1164
|
+
url,
|
|
1165
|
+
() => runRetried(() => this.http.postForm(url, fields), policy)
|
|
1068
1166
|
);
|
|
1069
1167
|
return decodeSuccess(resp.jsonBody, resp.httpStatus, resp.requestId);
|
|
1070
1168
|
}
|
|
@@ -1111,11 +1209,11 @@ var Streams = class {
|
|
|
1111
1209
|
async list() {
|
|
1112
1210
|
const result = await this.post("getStreams", {}, this.readPolicy);
|
|
1113
1211
|
if (!Array.isArray(result)) return [];
|
|
1114
|
-
return result.map(parseStream);
|
|
1212
|
+
return result.filter((x) => asObject(x) !== void 0).map(parseStream);
|
|
1115
1213
|
}
|
|
1116
1214
|
/** Compute the 9-char longpoll category locally — pure, no network. */
|
|
1117
1215
|
deriveLongpollCategory(radioId) {
|
|
1118
|
-
return deriveLongpollCategory(this.apiToken, radioId);
|
|
1216
|
+
return deriveLongpollCategory(this.apiToken(), radioId);
|
|
1119
1217
|
}
|
|
1120
1218
|
/**
|
|
1121
1219
|
* Parse a callback POST body into `{ match, notification }`. Pass an
|
|
@@ -1157,13 +1255,21 @@ var Streams = class {
|
|
|
1157
1255
|
const timeoutSec = effectiveOpts.timeout ?? 50;
|
|
1158
1256
|
const httpClient = this.http;
|
|
1159
1257
|
const readPolicy = this.readPolicy;
|
|
1258
|
+
const onEvent = this.onEvent;
|
|
1259
|
+
const longpollUrl = `${API_BASE}/longpoll/`;
|
|
1260
|
+
const timeoutMs = (timeoutSec + LONGPOLL_TIMEOUT_MARGIN_SEC) * 1e3;
|
|
1160
1261
|
return startLongpoll({
|
|
1161
1262
|
category,
|
|
1162
1263
|
timeout: timeoutSec,
|
|
1163
1264
|
sinceTime: effectiveOpts.sinceTime,
|
|
1164
|
-
fetchOnce: (params, signal) =>
|
|
1165
|
-
|
|
1166
|
-
|
|
1265
|
+
fetchOnce: (params, signal) => emitAround(
|
|
1266
|
+
onEvent,
|
|
1267
|
+
"longpoll",
|
|
1268
|
+
longpollUrl,
|
|
1269
|
+
() => runRetried(
|
|
1270
|
+
() => httpClient.get(longpollUrl, params, { signal, timeoutMs }),
|
|
1271
|
+
readPolicy
|
|
1272
|
+
)
|
|
1167
1273
|
)
|
|
1168
1274
|
});
|
|
1169
1275
|
}
|
|
@@ -1208,26 +1314,15 @@ function decodeSuccess2(body, httpStatus, requestId) {
|
|
|
1208
1314
|
});
|
|
1209
1315
|
}
|
|
1210
1316
|
}
|
|
1211
|
-
async function runRetried2(fn, policy) {
|
|
1212
|
-
try {
|
|
1213
|
-
return await retry(fn, policy);
|
|
1214
|
-
} catch (err) {
|
|
1215
|
-
if (err instanceof TypeError) {
|
|
1216
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1217
|
-
}
|
|
1218
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1219
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1220
|
-
}
|
|
1221
|
-
throw err;
|
|
1222
|
-
}
|
|
1223
|
-
}
|
|
1224
1317
|
var CustomCatalog = class {
|
|
1225
|
-
constructor(http, noRetryPolicy) {
|
|
1318
|
+
constructor(http, noRetryPolicy, onEvent) {
|
|
1226
1319
|
this.http = http;
|
|
1227
1320
|
this.noRetryPolicy = noRetryPolicy;
|
|
1321
|
+
this.onEvent = onEvent;
|
|
1228
1322
|
}
|
|
1229
1323
|
http;
|
|
1230
1324
|
noRetryPolicy;
|
|
1325
|
+
onEvent;
|
|
1231
1326
|
/**
|
|
1232
1327
|
* **This is NOT how you submit audio for music recognition.** For
|
|
1233
1328
|
* recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
|
|
@@ -1248,42 +1343,36 @@ var CustomCatalog = class {
|
|
|
1248
1343
|
async add(opts) {
|
|
1249
1344
|
const reopen = prepareSource(opts.source);
|
|
1250
1345
|
const audioId = String(opts.audioId);
|
|
1251
|
-
const resp = await
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1346
|
+
const resp = await emitAround(
|
|
1347
|
+
this.onEvent,
|
|
1348
|
+
"customCatalogAdd",
|
|
1349
|
+
UPLOAD_URL,
|
|
1350
|
+
() => runRetried(async () => {
|
|
1351
|
+
const prepared = await reopen();
|
|
1352
|
+
const fields = { ...prepared.fields, audio_id: audioId };
|
|
1353
|
+
return this.http.postForm(UPLOAD_URL, fields);
|
|
1354
|
+
}, this.noRetryPolicy)
|
|
1355
|
+
);
|
|
1256
1356
|
decodeSuccess2(resp.jsonBody, resp.httpStatus, resp.requestId);
|
|
1257
1357
|
}
|
|
1258
1358
|
};
|
|
1259
1359
|
|
|
1260
1360
|
// src/advanced.ts
|
|
1261
1361
|
var API_BASE2 = "https://api.audd.io";
|
|
1262
|
-
async function runRetried3(fn, policy) {
|
|
1263
|
-
try {
|
|
1264
|
-
return await retry(fn, policy);
|
|
1265
|
-
} catch (err) {
|
|
1266
|
-
if (err instanceof TypeError) {
|
|
1267
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1268
|
-
}
|
|
1269
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1270
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1271
|
-
}
|
|
1272
|
-
throw err;
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
1362
|
var Advanced = class {
|
|
1276
1363
|
/**
|
|
1277
1364
|
* @param recognitionPolicy The findLyrics endpoint is metered and shouldn't
|
|
1278
1365
|
* double-bill on post-upload read timeout: Advanced uses RECOGNITION retry
|
|
1279
1366
|
* policy, not READ.
|
|
1280
1367
|
*/
|
|
1281
|
-
constructor(http, recognitionPolicy) {
|
|
1368
|
+
constructor(http, recognitionPolicy, onEvent) {
|
|
1282
1369
|
this.http = http;
|
|
1283
1370
|
this.recognitionPolicy = recognitionPolicy;
|
|
1371
|
+
this.onEvent = onEvent;
|
|
1284
1372
|
}
|
|
1285
1373
|
http;
|
|
1286
1374
|
recognitionPolicy;
|
|
1375
|
+
onEvent;
|
|
1287
1376
|
/** Find lyrics by free-text query. Returns a list of matches (possibly empty). */
|
|
1288
1377
|
async findLyrics(query) {
|
|
1289
1378
|
const body = await this.rawRequest("findLyrics", { q: query });
|
|
@@ -1307,9 +1396,12 @@ var Advanced = class {
|
|
|
1307
1396
|
if (v === void 0) continue;
|
|
1308
1397
|
fields[k] = String(v);
|
|
1309
1398
|
}
|
|
1310
|
-
const
|
|
1311
|
-
|
|
1312
|
-
this.
|
|
1399
|
+
const url = `${API_BASE2}/${method}/`;
|
|
1400
|
+
const resp = await emitAround(
|
|
1401
|
+
this.onEvent,
|
|
1402
|
+
method,
|
|
1403
|
+
url,
|
|
1404
|
+
() => runRetried(() => this.http.postForm(url, fields), this.recognitionPolicy)
|
|
1313
1405
|
);
|
|
1314
1406
|
const body = resp.jsonBody;
|
|
1315
1407
|
if (typeof body !== "object" || body === null || Array.isArray(body)) {
|
|
@@ -1325,13 +1417,6 @@ var ENTERPRISE_BASE = "https://enterprise.audd.io";
|
|
|
1325
1417
|
var DEPRECATED_PARAMS_CODE = 51;
|
|
1326
1418
|
var HTTP_CLIENT_ERROR_FLOOR2 = 400;
|
|
1327
1419
|
var TOKEN_ENV_VAR = "AUDD_API_TOKEN";
|
|
1328
|
-
function safeEmit(hook, event) {
|
|
1329
|
-
if (hook === void 0) return;
|
|
1330
|
-
try {
|
|
1331
|
-
hook(event);
|
|
1332
|
-
} catch {
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
1420
|
function resolveToken(apiToken) {
|
|
1336
1421
|
if (apiToken !== void 0 && apiToken !== "") return apiToken;
|
|
1337
1422
|
const env = (typeof process !== "undefined" ? process.env[TOKEN_ENV_VAR] : void 0) ?? "";
|
|
@@ -1354,8 +1439,8 @@ function buildEnterpriseFields(opts) {
|
|
|
1354
1439
|
if (opts.skipFirstSeconds !== void 0)
|
|
1355
1440
|
fields["skip_first_seconds"] = String(opts.skipFirstSeconds);
|
|
1356
1441
|
if (opts.useTimecode !== void 0) fields["use_timecode"] = opts.useTimecode ? "true" : "false";
|
|
1357
|
-
|
|
1358
|
-
|
|
1442
|
+
const accurate = opts.accurateOffsets ?? true;
|
|
1443
|
+
fields["accurate_offsets"] = accurate ? "true" : "false";
|
|
1359
1444
|
return fields;
|
|
1360
1445
|
}
|
|
1361
1446
|
function isDeprecationPassThrough(body) {
|
|
@@ -1404,7 +1489,7 @@ function decodeOrRaise(resp) {
|
|
|
1404
1489
|
function decodeRecognize(resp) {
|
|
1405
1490
|
const body = decodeOrRaise(resp);
|
|
1406
1491
|
const result = body["result"];
|
|
1407
|
-
if (result
|
|
1492
|
+
if (asObject(result) === void 0) return null;
|
|
1408
1493
|
return parseRecognitionResult(result);
|
|
1409
1494
|
}
|
|
1410
1495
|
function decodeEnterprise(resp) {
|
|
@@ -1413,24 +1498,12 @@ function decodeEnterprise(resp) {
|
|
|
1413
1498
|
if (!Array.isArray(chunks)) return [];
|
|
1414
1499
|
const out = [];
|
|
1415
1500
|
for (const c of chunks) {
|
|
1501
|
+
if (asObject(c) === void 0) continue;
|
|
1416
1502
|
const chunk = parseEnterpriseChunkResult(c);
|
|
1417
1503
|
out.push(...chunk.songs);
|
|
1418
1504
|
}
|
|
1419
1505
|
return out;
|
|
1420
1506
|
}
|
|
1421
|
-
async function runRetried4(fn, policy) {
|
|
1422
|
-
try {
|
|
1423
|
-
return await retry(fn, policy);
|
|
1424
|
-
} catch (err) {
|
|
1425
|
-
if (err instanceof TypeError) {
|
|
1426
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1427
|
-
}
|
|
1428
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1429
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1430
|
-
}
|
|
1431
|
-
throw err;
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
1507
|
var AudD = class {
|
|
1435
1508
|
_http;
|
|
1436
1509
|
_enterpriseHttp;
|
|
@@ -1488,7 +1561,8 @@ var AudD = class {
|
|
|
1488
1561
|
this._http,
|
|
1489
1562
|
this.policyFor("read"),
|
|
1490
1563
|
this.policyFor("mutating"),
|
|
1491
|
-
this._apiToken
|
|
1564
|
+
() => this._apiToken,
|
|
1565
|
+
this._onEvent
|
|
1492
1566
|
);
|
|
1493
1567
|
}
|
|
1494
1568
|
return this._streams;
|
|
@@ -1496,14 +1570,14 @@ var AudD = class {
|
|
|
1496
1570
|
/** Sub-namespace for the private fingerprint catalog. NOT for recognition. */
|
|
1497
1571
|
get customCatalog() {
|
|
1498
1572
|
if (this._customCatalog === void 0) {
|
|
1499
|
-
this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"));
|
|
1573
|
+
this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"), this._onEvent);
|
|
1500
1574
|
}
|
|
1501
1575
|
return this._customCatalog;
|
|
1502
1576
|
}
|
|
1503
1577
|
/** Lyrics search + raw-request escape hatch. */
|
|
1504
1578
|
get advanced() {
|
|
1505
1579
|
if (this._advanced === void 0) {
|
|
1506
|
-
this._advanced = new Advanced(this._http, this.policyFor("recognition"));
|
|
1580
|
+
this._advanced = new Advanced(this._http, this.policyFor("recognition"), this._onEvent);
|
|
1507
1581
|
}
|
|
1508
1582
|
return this._advanced;
|
|
1509
1583
|
}
|
|
@@ -1519,20 +1593,11 @@ var AudD = class {
|
|
|
1519
1593
|
const market = opts.market;
|
|
1520
1594
|
const policy = this.policyFor("recognition");
|
|
1521
1595
|
const url = `${API_BASE3}/`;
|
|
1522
|
-
const
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
method: "recognize",
|
|
1596
|
+
const resp = await emitAround(
|
|
1597
|
+
this._onEvent,
|
|
1598
|
+
"recognize",
|
|
1526
1599
|
url,
|
|
1527
|
-
|
|
1528
|
-
httpStatus: null,
|
|
1529
|
-
elapsedMs: null,
|
|
1530
|
-
errorCode: null,
|
|
1531
|
-
extras: {}
|
|
1532
|
-
});
|
|
1533
|
-
let resp;
|
|
1534
|
-
try {
|
|
1535
|
-
resp = await runRetried4(async () => {
|
|
1600
|
+
() => runRetried(async () => {
|
|
1536
1601
|
const prepared = await reopen();
|
|
1537
1602
|
const fields = { ...prepared.fields };
|
|
1538
1603
|
if (opts.extraParameters) Object.assign(fields, opts.extraParameters);
|
|
@@ -1542,30 +1607,8 @@ var AudD = class {
|
|
|
1542
1607
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
1543
1608
|
...opts.signal !== void 0 ? { signal: opts.signal } : {}
|
|
1544
1609
|
});
|
|
1545
|
-
}, policy)
|
|
1546
|
-
|
|
1547
|
-
safeEmit(this._onEvent, {
|
|
1548
|
-
kind: "exception",
|
|
1549
|
-
method: "recognize",
|
|
1550
|
-
url,
|
|
1551
|
-
requestId: null,
|
|
1552
|
-
httpStatus: null,
|
|
1553
|
-
elapsedMs: Date.now() - startedAt,
|
|
1554
|
-
errorCode: null,
|
|
1555
|
-
extras: { name: err instanceof Error ? err.name : String(err) }
|
|
1556
|
-
});
|
|
1557
|
-
throw err;
|
|
1558
|
-
}
|
|
1559
|
-
safeEmit(this._onEvent, {
|
|
1560
|
-
kind: "response",
|
|
1561
|
-
method: "recognize",
|
|
1562
|
-
url,
|
|
1563
|
-
requestId: resp.requestId,
|
|
1564
|
-
httpStatus: resp.httpStatus,
|
|
1565
|
-
elapsedMs: Date.now() - startedAt,
|
|
1566
|
-
errorCode: null,
|
|
1567
|
-
extras: {}
|
|
1568
|
-
});
|
|
1610
|
+
}, policy)
|
|
1611
|
+
);
|
|
1569
1612
|
return decodeRecognize(resp);
|
|
1570
1613
|
}
|
|
1571
1614
|
/**
|
|
@@ -1578,14 +1621,20 @@ var AudD = class {
|
|
|
1578
1621
|
const reopen = prepareSource(source);
|
|
1579
1622
|
const extra = buildEnterpriseFields(opts);
|
|
1580
1623
|
const policy = this.policyFor("recognition");
|
|
1581
|
-
const
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1624
|
+
const url = `${ENTERPRISE_BASE}/`;
|
|
1625
|
+
const resp = await emitAround(
|
|
1626
|
+
this._onEvent,
|
|
1627
|
+
"recognizeEnterprise",
|
|
1628
|
+
url,
|
|
1629
|
+
() => runRetried(async () => {
|
|
1630
|
+
const prepared = await reopen();
|
|
1631
|
+
const fields = { ...prepared.fields, ...extra };
|
|
1632
|
+
return this._enterpriseHttp.postForm(url, fields, {
|
|
1633
|
+
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
1634
|
+
...opts.signal !== void 0 ? { signal: opts.signal } : {}
|
|
1635
|
+
});
|
|
1636
|
+
}, policy)
|
|
1637
|
+
);
|
|
1589
1638
|
return decodeEnterprise(resp);
|
|
1590
1639
|
}
|
|
1591
1640
|
/** Release any underlying resources. (Currently a no-op for `fetch`-based transport.) */
|