@audd/sdk 1.5.11 → 1.5.13
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 +9 -8
- package/dist/index.cjs +203 -156
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -27
- package/dist/index.d.ts +36 -27
- package/dist/index.js +203 -156
- package/dist/index.js.map +1 -1
- package/dist/longpoll.cjs +71 -25
- 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 +71 -25
- package/dist/longpoll.js.map +1 -1
- package/dist/{longpollCore-ZtFOyojP.d.cts → longpollCore-BXx8PRmC.d.cts} +1 -1
- package/dist/{longpollCore-ZtFOyojP.d.ts → longpollCore-BXx8PRmC.d.ts} +1 -1
- 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.13";
|
|
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,19 +313,42 @@ 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
|
+
if (typeof v === "string") return v;
|
|
321
|
+
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
322
|
+
return void 0;
|
|
323
|
+
}
|
|
324
|
+
function toFiniteNumber(v) {
|
|
325
|
+
if (typeof v === "number") return Number.isFinite(v) ? v : void 0;
|
|
326
|
+
if (typeof v === "boolean") return v ? 1 : 0;
|
|
327
|
+
if (typeof v === "string") {
|
|
328
|
+
const t = v.trim();
|
|
329
|
+
if (t === "") return void 0;
|
|
330
|
+
const n = Number(t);
|
|
331
|
+
return Number.isFinite(n) ? n : void 0;
|
|
332
|
+
}
|
|
333
|
+
return void 0;
|
|
259
334
|
}
|
|
260
335
|
function asNumber(v) {
|
|
261
|
-
return
|
|
336
|
+
return toFiniteNumber(v);
|
|
337
|
+
}
|
|
338
|
+
function asInteger(v) {
|
|
339
|
+
const n = toFiniteNumber(v);
|
|
340
|
+
return n === void 0 ? void 0 : Math.trunc(n);
|
|
262
341
|
}
|
|
263
342
|
function asBoolean(v) {
|
|
264
|
-
|
|
343
|
+
if (typeof v === "boolean") return v;
|
|
344
|
+
if (typeof v === "number") return v !== 0;
|
|
345
|
+
if (typeof v === "string") {
|
|
346
|
+
const t = v.trim().toLowerCase();
|
|
347
|
+
if (t === "true" || t === "1" || t === "yes" || t === "on") return true;
|
|
348
|
+
if (t === "false" || t === "0" || t === "no" || t === "off" || t === "") return false;
|
|
349
|
+
return void 0;
|
|
350
|
+
}
|
|
351
|
+
return void 0;
|
|
265
352
|
}
|
|
266
353
|
function parseOffsetToSeconds(o) {
|
|
267
354
|
if (o === void 0 || o === null) return void 0;
|
|
@@ -356,9 +443,9 @@ var RECOGNITION_KEYS = [
|
|
|
356
443
|
"musicbrainz"
|
|
357
444
|
];
|
|
358
445
|
function parseRecognitionResult(raw) {
|
|
359
|
-
const r =
|
|
446
|
+
const r = asObjectLenient(raw);
|
|
360
447
|
const timecode = asString(r.timecode);
|
|
361
|
-
const audioId =
|
|
448
|
+
const audioId = asInteger(r.audio_id);
|
|
362
449
|
const artist = asString(r.artist);
|
|
363
450
|
const title = asString(r.title);
|
|
364
451
|
const songLink = asString(r.song_link);
|
|
@@ -431,7 +518,7 @@ var ENTERPRISE_MATCH_KEYS = [
|
|
|
431
518
|
"end_seconds"
|
|
432
519
|
];
|
|
433
520
|
function parseEnterpriseMatch(raw) {
|
|
434
|
-
const r =
|
|
521
|
+
const r = asObjectLenient(raw);
|
|
435
522
|
const score = asNumber(r.score);
|
|
436
523
|
const timecode = asString(r.timecode);
|
|
437
524
|
const songLink = asString(r.song_link);
|
|
@@ -446,8 +533,8 @@ function parseEnterpriseMatch(raw) {
|
|
|
446
533
|
isrc: asString(r.isrc),
|
|
447
534
|
upc: asString(r.upc),
|
|
448
535
|
songLink,
|
|
449
|
-
startOffset:
|
|
450
|
-
endOffset:
|
|
536
|
+
startOffset: asInteger(r.start_offset),
|
|
537
|
+
endOffset: asInteger(r.end_offset),
|
|
451
538
|
startSeconds: void 0,
|
|
452
539
|
endSeconds: void 0,
|
|
453
540
|
extras: pickExtras(r, ENTERPRISE_MATCH_KEYS),
|
|
@@ -481,8 +568,8 @@ function parseEnterpriseMatch(raw) {
|
|
|
481
568
|
}
|
|
482
569
|
var ENTERPRISE_CHUNK_KEYS = ["songs", "offset"];
|
|
483
570
|
function parseEnterpriseChunkResult(raw) {
|
|
484
|
-
const r =
|
|
485
|
-
const songsRaw = Array.isArray(r.songs) ? r.songs : [];
|
|
571
|
+
const r = asObjectLenient(raw);
|
|
572
|
+
const songsRaw = Array.isArray(r.songs) ? r.songs.filter((s) => asObject(s) !== void 0) : [];
|
|
486
573
|
const offset = asString(r.offset);
|
|
487
574
|
const base = parseOffsetToSeconds(r.offset);
|
|
488
575
|
const songs = songsRaw.map((s) => {
|
|
@@ -502,8 +589,8 @@ function parseEnterpriseChunkResult(raw) {
|
|
|
502
589
|
}
|
|
503
590
|
var STREAM_KEYS = ["radio_id", "url", "stream_running", "longpoll_category"];
|
|
504
591
|
function parseStream(raw) {
|
|
505
|
-
const r =
|
|
506
|
-
const radioId =
|
|
592
|
+
const r = asObjectLenient(raw);
|
|
593
|
+
const radioId = asInteger(r.radio_id);
|
|
507
594
|
const url = asString(r.url);
|
|
508
595
|
const streamRunning = asBoolean(r.stream_running);
|
|
509
596
|
return {
|
|
@@ -532,7 +619,7 @@ var STREAM_CALLBACK_SONG_KEYS = [
|
|
|
532
619
|
"musicbrainz"
|
|
533
620
|
];
|
|
534
621
|
function parseStreamCallbackSong(raw) {
|
|
535
|
-
const r =
|
|
622
|
+
const r = asObjectLenient(raw);
|
|
536
623
|
const artist = asString(r.artist);
|
|
537
624
|
const title = asString(r.title);
|
|
538
625
|
const score = asNumber(r.score);
|
|
@@ -562,15 +649,15 @@ var STREAM_CALLBACK_MATCH_KEYS = [
|
|
|
562
649
|
"results"
|
|
563
650
|
];
|
|
564
651
|
function parseStreamCallbackMatch(raw) {
|
|
565
|
-
const r =
|
|
566
|
-
const radioId =
|
|
652
|
+
const r = asObjectLenient(raw);
|
|
653
|
+
const radioId = asInteger(r.radio_id);
|
|
567
654
|
const resultsRaw = Array.isArray(r.results) ? r.results : [];
|
|
568
655
|
const songs = resultsRaw.filter((x) => asObject(x) !== void 0).map(parseStreamCallbackSong);
|
|
569
656
|
const [first, ...rest] = songs;
|
|
570
657
|
return {
|
|
571
658
|
radioId,
|
|
572
659
|
timestamp: asString(r.timestamp),
|
|
573
|
-
playLength:
|
|
660
|
+
playLength: asInteger(r.play_length),
|
|
574
661
|
song: first,
|
|
575
662
|
alternatives: rest,
|
|
576
663
|
extras: pickExtras(r, STREAM_CALLBACK_MATCH_KEYS),
|
|
@@ -584,9 +671,9 @@ var STREAM_CALLBACK_NOTIFICATION_KEYS = [
|
|
|
584
671
|
"notification_message"
|
|
585
672
|
];
|
|
586
673
|
function parseStreamCallbackNotification(raw) {
|
|
587
|
-
const r =
|
|
588
|
-
const radioId =
|
|
589
|
-
const code =
|
|
674
|
+
const r = asObjectLenient(raw);
|
|
675
|
+
const radioId = asInteger(r.radio_id);
|
|
676
|
+
const code = asInteger(r.notification_code);
|
|
590
677
|
const message = asString(r.notification_message);
|
|
591
678
|
return {
|
|
592
679
|
radioId,
|
|
@@ -608,16 +695,16 @@ var LYRICS_KEYS = [
|
|
|
608
695
|
"media"
|
|
609
696
|
];
|
|
610
697
|
function parseLyricsResult(raw) {
|
|
611
|
-
const r =
|
|
698
|
+
const r = asObjectLenient(raw);
|
|
612
699
|
const artist = asString(r.artist);
|
|
613
700
|
const title = asString(r.title);
|
|
614
701
|
return {
|
|
615
702
|
artist,
|
|
616
703
|
title,
|
|
617
704
|
lyrics: asString(r.lyrics),
|
|
618
|
-
songId:
|
|
705
|
+
songId: asInteger(r.song_id),
|
|
619
706
|
fullTitle: asString(r.full_title),
|
|
620
|
-
artistId:
|
|
707
|
+
artistId: asInteger(r.artist_id),
|
|
621
708
|
songLink: asString(r.song_link),
|
|
622
709
|
media: asString(r.media),
|
|
623
710
|
extras: pickExtras(r, LYRICS_KEYS),
|
|
@@ -704,6 +791,22 @@ async function retry(fn, policy) {
|
|
|
704
791
|
if (haveResp && lastResp !== void 0) return lastResp;
|
|
705
792
|
throw lastError;
|
|
706
793
|
}
|
|
794
|
+
async function runRetried(fn, policy) {
|
|
795
|
+
try {
|
|
796
|
+
return await retry(fn, policy);
|
|
797
|
+
} catch (err) {
|
|
798
|
+
if (isUserAbortError(err)) {
|
|
799
|
+
throw new AudDConnectionError("Request was cancelled by the caller's AbortSignal", err);
|
|
800
|
+
}
|
|
801
|
+
if (err instanceof TypeError) {
|
|
802
|
+
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
803
|
+
}
|
|
804
|
+
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
805
|
+
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
806
|
+
}
|
|
807
|
+
throw err;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
707
810
|
function looksLikeUrl(s) {
|
|
708
811
|
return s.startsWith("http://") || s.startsWith("https://");
|
|
709
812
|
}
|
|
@@ -955,13 +1058,13 @@ function decodeOne(resp) {
|
|
|
955
1058
|
);
|
|
956
1059
|
}
|
|
957
1060
|
const dict = body;
|
|
958
|
-
if (
|
|
1061
|
+
if (asObject(dict.notification) !== void 0) {
|
|
959
1062
|
const notification = parseStreamCallbackNotification(dict.notification);
|
|
960
1063
|
const t = dict["time"];
|
|
961
1064
|
if (typeof t === "number") notification.time = t;
|
|
962
1065
|
return { kind: "notification", notification };
|
|
963
1066
|
}
|
|
964
|
-
if (
|
|
1067
|
+
if (asObject(dict.result) !== void 0) {
|
|
965
1068
|
return { kind: "match", match: parseStreamCallbackMatch(dict.result) };
|
|
966
1069
|
}
|
|
967
1070
|
return { kind: "keepalive" };
|
|
@@ -1040,6 +1143,7 @@ function startLongpoll(cfg) {
|
|
|
1040
1143
|
|
|
1041
1144
|
// src/streams.ts
|
|
1042
1145
|
var API_BASE = "https://api.audd.io";
|
|
1146
|
+
var LONGPOLL_TIMEOUT_MARGIN_SEC = 10;
|
|
1043
1147
|
var NO_CALLBACK_ERROR_CODE = 19;
|
|
1044
1148
|
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.";
|
|
1045
1149
|
function decodeSuccess(body, httpStatus, requestId) {
|
|
@@ -1064,34 +1168,26 @@ function decodeSuccess(body, httpStatus, requestId) {
|
|
|
1064
1168
|
rawResponse: obj
|
|
1065
1169
|
});
|
|
1066
1170
|
}
|
|
1067
|
-
async function runRetried(fn, policy) {
|
|
1068
|
-
try {
|
|
1069
|
-
return await retry(fn, policy);
|
|
1070
|
-
} catch (err) {
|
|
1071
|
-
if (err instanceof TypeError) {
|
|
1072
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1073
|
-
}
|
|
1074
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1075
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1076
|
-
}
|
|
1077
|
-
throw err;
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
1171
|
var Streams = class {
|
|
1081
|
-
constructor(http, readPolicy, mutatingPolicy, apiToken) {
|
|
1172
|
+
constructor(http, readPolicy, mutatingPolicy, apiToken, onEvent) {
|
|
1082
1173
|
this.http = http;
|
|
1083
1174
|
this.readPolicy = readPolicy;
|
|
1084
1175
|
this.mutatingPolicy = mutatingPolicy;
|
|
1085
1176
|
this.apiToken = apiToken;
|
|
1177
|
+
this.onEvent = onEvent;
|
|
1086
1178
|
}
|
|
1087
1179
|
http;
|
|
1088
1180
|
readPolicy;
|
|
1089
1181
|
mutatingPolicy;
|
|
1090
1182
|
apiToken;
|
|
1183
|
+
onEvent;
|
|
1091
1184
|
async post(path2, fields, policy) {
|
|
1092
|
-
const
|
|
1093
|
-
|
|
1094
|
-
|
|
1185
|
+
const url = `${API_BASE}/${path2}/`;
|
|
1186
|
+
const resp = await emitAround(
|
|
1187
|
+
this.onEvent,
|
|
1188
|
+
path2,
|
|
1189
|
+
url,
|
|
1190
|
+
() => runRetried(() => this.http.postForm(url, fields), policy)
|
|
1095
1191
|
);
|
|
1096
1192
|
return decodeSuccess(resp.jsonBody, resp.httpStatus, resp.requestId);
|
|
1097
1193
|
}
|
|
@@ -1138,11 +1234,11 @@ var Streams = class {
|
|
|
1138
1234
|
async list() {
|
|
1139
1235
|
const result = await this.post("getStreams", {}, this.readPolicy);
|
|
1140
1236
|
if (!Array.isArray(result)) return [];
|
|
1141
|
-
return result.map(parseStream);
|
|
1237
|
+
return result.filter((x) => asObject(x) !== void 0).map(parseStream);
|
|
1142
1238
|
}
|
|
1143
1239
|
/** Compute the 9-char longpoll category locally — pure, no network. */
|
|
1144
1240
|
deriveLongpollCategory(radioId) {
|
|
1145
|
-
return deriveLongpollCategory(this.apiToken, radioId);
|
|
1241
|
+
return deriveLongpollCategory(this.apiToken(), radioId);
|
|
1146
1242
|
}
|
|
1147
1243
|
/**
|
|
1148
1244
|
* Parse a callback POST body into `{ match, notification }`. Pass an
|
|
@@ -1184,13 +1280,21 @@ var Streams = class {
|
|
|
1184
1280
|
const timeoutSec = effectiveOpts.timeout ?? 50;
|
|
1185
1281
|
const httpClient = this.http;
|
|
1186
1282
|
const readPolicy = this.readPolicy;
|
|
1283
|
+
const onEvent = this.onEvent;
|
|
1284
|
+
const longpollUrl = `${API_BASE}/longpoll/`;
|
|
1285
|
+
const timeoutMs = (timeoutSec + LONGPOLL_TIMEOUT_MARGIN_SEC) * 1e3;
|
|
1187
1286
|
return startLongpoll({
|
|
1188
1287
|
category,
|
|
1189
1288
|
timeout: timeoutSec,
|
|
1190
1289
|
sinceTime: effectiveOpts.sinceTime,
|
|
1191
|
-
fetchOnce: (params, signal) =>
|
|
1192
|
-
|
|
1193
|
-
|
|
1290
|
+
fetchOnce: (params, signal) => emitAround(
|
|
1291
|
+
onEvent,
|
|
1292
|
+
"longpoll",
|
|
1293
|
+
longpollUrl,
|
|
1294
|
+
() => runRetried(
|
|
1295
|
+
() => httpClient.get(longpollUrl, params, { signal, timeoutMs }),
|
|
1296
|
+
readPolicy
|
|
1297
|
+
)
|
|
1194
1298
|
)
|
|
1195
1299
|
});
|
|
1196
1300
|
}
|
|
@@ -1235,26 +1339,15 @@ function decodeSuccess2(body, httpStatus, requestId) {
|
|
|
1235
1339
|
});
|
|
1236
1340
|
}
|
|
1237
1341
|
}
|
|
1238
|
-
async function runRetried2(fn, policy) {
|
|
1239
|
-
try {
|
|
1240
|
-
return await retry(fn, policy);
|
|
1241
|
-
} catch (err) {
|
|
1242
|
-
if (err instanceof TypeError) {
|
|
1243
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1244
|
-
}
|
|
1245
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1246
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1247
|
-
}
|
|
1248
|
-
throw err;
|
|
1249
|
-
}
|
|
1250
|
-
}
|
|
1251
1342
|
var CustomCatalog = class {
|
|
1252
|
-
constructor(http, noRetryPolicy) {
|
|
1343
|
+
constructor(http, noRetryPolicy, onEvent) {
|
|
1253
1344
|
this.http = http;
|
|
1254
1345
|
this.noRetryPolicy = noRetryPolicy;
|
|
1346
|
+
this.onEvent = onEvent;
|
|
1255
1347
|
}
|
|
1256
1348
|
http;
|
|
1257
1349
|
noRetryPolicy;
|
|
1350
|
+
onEvent;
|
|
1258
1351
|
/**
|
|
1259
1352
|
* **This is NOT how you submit audio for music recognition.** For
|
|
1260
1353
|
* recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
|
|
@@ -1275,42 +1368,36 @@ var CustomCatalog = class {
|
|
|
1275
1368
|
async add(opts) {
|
|
1276
1369
|
const reopen = prepareSource(opts.source);
|
|
1277
1370
|
const audioId = String(opts.audioId);
|
|
1278
|
-
const resp = await
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1371
|
+
const resp = await emitAround(
|
|
1372
|
+
this.onEvent,
|
|
1373
|
+
"customCatalogAdd",
|
|
1374
|
+
UPLOAD_URL,
|
|
1375
|
+
() => runRetried(async () => {
|
|
1376
|
+
const prepared = await reopen();
|
|
1377
|
+
const fields = { ...prepared.fields, audio_id: audioId };
|
|
1378
|
+
return this.http.postForm(UPLOAD_URL, fields);
|
|
1379
|
+
}, this.noRetryPolicy)
|
|
1380
|
+
);
|
|
1283
1381
|
decodeSuccess2(resp.jsonBody, resp.httpStatus, resp.requestId);
|
|
1284
1382
|
}
|
|
1285
1383
|
};
|
|
1286
1384
|
|
|
1287
1385
|
// src/advanced.ts
|
|
1288
1386
|
var API_BASE2 = "https://api.audd.io";
|
|
1289
|
-
async function runRetried3(fn, policy) {
|
|
1290
|
-
try {
|
|
1291
|
-
return await retry(fn, policy);
|
|
1292
|
-
} catch (err) {
|
|
1293
|
-
if (err instanceof TypeError) {
|
|
1294
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1295
|
-
}
|
|
1296
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1297
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1298
|
-
}
|
|
1299
|
-
throw err;
|
|
1300
|
-
}
|
|
1301
|
-
}
|
|
1302
1387
|
var Advanced = class {
|
|
1303
1388
|
/**
|
|
1304
1389
|
* @param recognitionPolicy The findLyrics endpoint is metered and shouldn't
|
|
1305
1390
|
* double-bill on post-upload read timeout: Advanced uses RECOGNITION retry
|
|
1306
1391
|
* policy, not READ.
|
|
1307
1392
|
*/
|
|
1308
|
-
constructor(http, recognitionPolicy) {
|
|
1393
|
+
constructor(http, recognitionPolicy, onEvent) {
|
|
1309
1394
|
this.http = http;
|
|
1310
1395
|
this.recognitionPolicy = recognitionPolicy;
|
|
1396
|
+
this.onEvent = onEvent;
|
|
1311
1397
|
}
|
|
1312
1398
|
http;
|
|
1313
1399
|
recognitionPolicy;
|
|
1400
|
+
onEvent;
|
|
1314
1401
|
/** Find lyrics by free-text query. Returns a list of matches (possibly empty). */
|
|
1315
1402
|
async findLyrics(query) {
|
|
1316
1403
|
const body = await this.rawRequest("findLyrics", { q: query });
|
|
@@ -1334,9 +1421,12 @@ var Advanced = class {
|
|
|
1334
1421
|
if (v === void 0) continue;
|
|
1335
1422
|
fields[k] = String(v);
|
|
1336
1423
|
}
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
this.
|
|
1424
|
+
const url = `${API_BASE2}/${method}/`;
|
|
1425
|
+
const resp = await emitAround(
|
|
1426
|
+
this.onEvent,
|
|
1427
|
+
method,
|
|
1428
|
+
url,
|
|
1429
|
+
() => runRetried(() => this.http.postForm(url, fields), this.recognitionPolicy)
|
|
1340
1430
|
);
|
|
1341
1431
|
const body = resp.jsonBody;
|
|
1342
1432
|
if (typeof body !== "object" || body === null || Array.isArray(body)) {
|
|
@@ -1352,13 +1442,6 @@ var ENTERPRISE_BASE = "https://enterprise.audd.io";
|
|
|
1352
1442
|
var DEPRECATED_PARAMS_CODE = 51;
|
|
1353
1443
|
var HTTP_CLIENT_ERROR_FLOOR2 = 400;
|
|
1354
1444
|
var TOKEN_ENV_VAR = "AUDD_API_TOKEN";
|
|
1355
|
-
function safeEmit(hook, event) {
|
|
1356
|
-
if (hook === void 0) return;
|
|
1357
|
-
try {
|
|
1358
|
-
hook(event);
|
|
1359
|
-
} catch {
|
|
1360
|
-
}
|
|
1361
|
-
}
|
|
1362
1445
|
function resolveToken(apiToken) {
|
|
1363
1446
|
if (apiToken !== void 0 && apiToken !== "") return apiToken;
|
|
1364
1447
|
const env = (typeof process !== "undefined" ? process.env[TOKEN_ENV_VAR] : void 0) ?? "";
|
|
@@ -1431,7 +1514,7 @@ function decodeOrRaise(resp) {
|
|
|
1431
1514
|
function decodeRecognize(resp) {
|
|
1432
1515
|
const body = decodeOrRaise(resp);
|
|
1433
1516
|
const result = body["result"];
|
|
1434
|
-
if (result
|
|
1517
|
+
if (asObject(result) === void 0) return null;
|
|
1435
1518
|
return parseRecognitionResult(result);
|
|
1436
1519
|
}
|
|
1437
1520
|
function decodeEnterprise(resp) {
|
|
@@ -1440,24 +1523,12 @@ function decodeEnterprise(resp) {
|
|
|
1440
1523
|
if (!Array.isArray(chunks)) return [];
|
|
1441
1524
|
const out = [];
|
|
1442
1525
|
for (const c of chunks) {
|
|
1526
|
+
if (asObject(c) === void 0) continue;
|
|
1443
1527
|
const chunk = parseEnterpriseChunkResult(c);
|
|
1444
1528
|
out.push(...chunk.songs);
|
|
1445
1529
|
}
|
|
1446
1530
|
return out;
|
|
1447
1531
|
}
|
|
1448
|
-
async function runRetried4(fn, policy) {
|
|
1449
|
-
try {
|
|
1450
|
-
return await retry(fn, policy);
|
|
1451
|
-
} catch (err) {
|
|
1452
|
-
if (err instanceof TypeError) {
|
|
1453
|
-
throw new AudDConnectionError(`Network error: ${err.message}`, err);
|
|
1454
|
-
}
|
|
1455
|
-
if (err !== null && typeof err === "object" && err.name === "AbortError") {
|
|
1456
|
-
throw new AudDConnectionError("Request was aborted (timeout)", err);
|
|
1457
|
-
}
|
|
1458
|
-
throw err;
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
1532
|
var AudD = class {
|
|
1462
1533
|
_http;
|
|
1463
1534
|
_enterpriseHttp;
|
|
@@ -1515,7 +1586,8 @@ var AudD = class {
|
|
|
1515
1586
|
this._http,
|
|
1516
1587
|
this.policyFor("read"),
|
|
1517
1588
|
this.policyFor("mutating"),
|
|
1518
|
-
this._apiToken
|
|
1589
|
+
() => this._apiToken,
|
|
1590
|
+
this._onEvent
|
|
1519
1591
|
);
|
|
1520
1592
|
}
|
|
1521
1593
|
return this._streams;
|
|
@@ -1523,14 +1595,14 @@ var AudD = class {
|
|
|
1523
1595
|
/** Sub-namespace for the private fingerprint catalog. NOT for recognition. */
|
|
1524
1596
|
get customCatalog() {
|
|
1525
1597
|
if (this._customCatalog === void 0) {
|
|
1526
|
-
this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"));
|
|
1598
|
+
this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"), this._onEvent);
|
|
1527
1599
|
}
|
|
1528
1600
|
return this._customCatalog;
|
|
1529
1601
|
}
|
|
1530
1602
|
/** Lyrics search + raw-request escape hatch. */
|
|
1531
1603
|
get advanced() {
|
|
1532
1604
|
if (this._advanced === void 0) {
|
|
1533
|
-
this._advanced = new Advanced(this._http, this.policyFor("recognition"));
|
|
1605
|
+
this._advanced = new Advanced(this._http, this.policyFor("recognition"), this._onEvent);
|
|
1534
1606
|
}
|
|
1535
1607
|
return this._advanced;
|
|
1536
1608
|
}
|
|
@@ -1546,20 +1618,11 @@ var AudD = class {
|
|
|
1546
1618
|
const market = opts.market;
|
|
1547
1619
|
const policy = this.policyFor("recognition");
|
|
1548
1620
|
const url = `${API_BASE3}/`;
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
method: "recognize",
|
|
1621
|
+
const resp = await emitAround(
|
|
1622
|
+
this._onEvent,
|
|
1623
|
+
"recognize",
|
|
1553
1624
|
url,
|
|
1554
|
-
|
|
1555
|
-
httpStatus: null,
|
|
1556
|
-
elapsedMs: null,
|
|
1557
|
-
errorCode: null,
|
|
1558
|
-
extras: {}
|
|
1559
|
-
});
|
|
1560
|
-
let resp;
|
|
1561
|
-
try {
|
|
1562
|
-
resp = await runRetried4(async () => {
|
|
1625
|
+
() => runRetried(async () => {
|
|
1563
1626
|
const prepared = await reopen();
|
|
1564
1627
|
const fields = { ...prepared.fields };
|
|
1565
1628
|
if (opts.extraParameters) Object.assign(fields, opts.extraParameters);
|
|
@@ -1569,30 +1632,8 @@ var AudD = class {
|
|
|
1569
1632
|
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
1570
1633
|
...opts.signal !== void 0 ? { signal: opts.signal } : {}
|
|
1571
1634
|
});
|
|
1572
|
-
}, policy)
|
|
1573
|
-
|
|
1574
|
-
safeEmit(this._onEvent, {
|
|
1575
|
-
kind: "exception",
|
|
1576
|
-
method: "recognize",
|
|
1577
|
-
url,
|
|
1578
|
-
requestId: null,
|
|
1579
|
-
httpStatus: null,
|
|
1580
|
-
elapsedMs: Date.now() - startedAt,
|
|
1581
|
-
errorCode: null,
|
|
1582
|
-
extras: { name: err instanceof Error ? err.name : String(err) }
|
|
1583
|
-
});
|
|
1584
|
-
throw err;
|
|
1585
|
-
}
|
|
1586
|
-
safeEmit(this._onEvent, {
|
|
1587
|
-
kind: "response",
|
|
1588
|
-
method: "recognize",
|
|
1589
|
-
url,
|
|
1590
|
-
requestId: resp.requestId,
|
|
1591
|
-
httpStatus: resp.httpStatus,
|
|
1592
|
-
elapsedMs: Date.now() - startedAt,
|
|
1593
|
-
errorCode: null,
|
|
1594
|
-
extras: {}
|
|
1595
|
-
});
|
|
1635
|
+
}, policy)
|
|
1636
|
+
);
|
|
1596
1637
|
return decodeRecognize(resp);
|
|
1597
1638
|
}
|
|
1598
1639
|
/**
|
|
@@ -1605,14 +1646,20 @@ var AudD = class {
|
|
|
1605
1646
|
const reopen = prepareSource(source);
|
|
1606
1647
|
const extra = buildEnterpriseFields(opts);
|
|
1607
1648
|
const policy = this.policyFor("recognition");
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1649
|
+
const url = `${ENTERPRISE_BASE}/`;
|
|
1650
|
+
const resp = await emitAround(
|
|
1651
|
+
this._onEvent,
|
|
1652
|
+
"recognizeEnterprise",
|
|
1653
|
+
url,
|
|
1654
|
+
() => runRetried(async () => {
|
|
1655
|
+
const prepared = await reopen();
|
|
1656
|
+
const fields = { ...prepared.fields, ...extra };
|
|
1657
|
+
return this._enterpriseHttp.postForm(url, fields, {
|
|
1658
|
+
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
1659
|
+
...opts.signal !== void 0 ? { signal: opts.signal } : {}
|
|
1660
|
+
});
|
|
1661
|
+
}, policy)
|
|
1662
|
+
);
|
|
1616
1663
|
return decodeEnterprise(resp);
|
|
1617
1664
|
}
|
|
1618
1665
|
/** Release any underlying resources. (Currently a no-op for `fetch`-based transport.) */
|