@audd/sdk 1.5.9 → 1.5.11
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 +13 -8
- package/dist/index.cjs +34 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +34 -34
- package/dist/index.js.map +1 -1
- package/dist/longpoll.cjs +2 -15
- package/dist/longpoll.cjs.map +1 -1
- package/dist/longpoll.d.cts +1 -1
- package/dist/longpoll.d.ts +1 -1
- package/dist/longpoll.js +2 -15
- package/dist/longpoll.js.map +1 -1
- package/dist/{longpollCore-DMBdcGSM.d.cts → longpollCore-ZtFOyojP.d.cts} +26 -23
- package/dist/{longpollCore-DMBdcGSM.d.ts → longpollCore-ZtFOyojP.d.ts} +26 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ console.log(song.releaseDate, song.label, song.timecode);
|
|
|
85
85
|
// AudD's universal song page — links into every provider
|
|
86
86
|
console.log(song.songLink);
|
|
87
87
|
|
|
88
|
-
// Helpers — driven off songLink, work without any `
|
|
88
|
+
// Helpers — driven off songLink, work without any `returnMetadata` opt-in
|
|
89
89
|
console.log(song.thumbnailUrl); // cover-art image, or null
|
|
90
90
|
console.log(song.streamingUrl("spotify")); // direct or lis.tn redirect
|
|
91
91
|
console.log(song.streamingUrls()); // map of provider -> URL
|
|
@@ -107,21 +107,21 @@ Valid `returnMetadata` values: `apple_music`, `spotify`, `deezer`, `napster`,
|
|
|
107
107
|
`musicbrainz`. Blocks are `undefined` when not requested.
|
|
108
108
|
|
|
109
109
|
`streamingUrl(provider)` prefers the direct provider URL when you
|
|
110
|
-
requested that block via `
|
|
110
|
+
requested that block via `returnMetadata`, then falls back to the lis.tn redirect
|
|
111
111
|
when `songLink` is on `lis.tn`. YouTube has only the redirect path.
|
|
112
112
|
|
|
113
113
|
## Reading additional metadata
|
|
114
114
|
|
|
115
115
|
Every model carries an `extras` map with any server-side fields outside
|
|
116
116
|
the typed surface, plus a `rawResponse` of the full unparsed JSON. Use
|
|
117
|
-
`extras` to read
|
|
117
|
+
`extras` to read fields outside the typed surface:
|
|
118
118
|
|
|
119
119
|
```ts
|
|
120
120
|
console.log(song.extras); // any non-typed top-level fields
|
|
121
121
|
console.log(song.rawResponse); // the whole result object as the server returned it
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
For the **request** side, every call accepts an `extraParameters` map for additional form fields the typed options don't cover
|
|
124
|
+
For the **request** side, every call accepts an `extraParameters` map for additional form fields the typed options don't cover:
|
|
125
125
|
|
|
126
126
|
```ts
|
|
127
127
|
await audd.recognize(url, {
|
|
@@ -145,10 +145,15 @@ for (const m of matches) {
|
|
|
145
145
|
}
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
`EnterpriseMatch` carries the same core tags plus `score`, `
|
|
149
|
-
`
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
`EnterpriseMatch` carries the same core tags plus `score`, `startSeconds`,
|
|
149
|
+
`endSeconds`, `startOffset`, `endOffset`, `isrc`, `upc`. `startSeconds` and
|
|
150
|
+
`endSeconds` are where this song plays in your file, in seconds (e.g. 64.2 to
|
|
151
|
+
71.8) — feed straight to a player or ffmpeg; computed while parsing the chunked
|
|
152
|
+
response, and `recognizeEnterprise` requests accurate offsets by default so
|
|
153
|
+
they're precise. `startOffset`/`endOffset` are the raw millisecond positions
|
|
154
|
+
within AudD's internal 12-second scan fragment behind them. Access to `isrc`,
|
|
155
|
+
`upc`, and `score` requires a Startup plan or higher — [contact
|
|
156
|
+
us](mailto:api@audd.io) for enterprise features.
|
|
152
157
|
|
|
153
158
|
The default per-call timeout is **1 hour** for this endpoint (60s for
|
|
154
159
|
standard recognition); override with `timeoutMs`.
|
package/dist/index.cjs
CHANGED
|
@@ -285,6 +285,20 @@ function asNumber(v) {
|
|
|
285
285
|
function asBoolean(v) {
|
|
286
286
|
return typeof v === "boolean" ? v : void 0;
|
|
287
287
|
}
|
|
288
|
+
function parseOffsetToSeconds(o) {
|
|
289
|
+
if (o === void 0 || o === null) return void 0;
|
|
290
|
+
if (typeof o === "number") return Number.isFinite(o) ? o : void 0;
|
|
291
|
+
const t = String(o).trim();
|
|
292
|
+
if (t === "") return void 0;
|
|
293
|
+
let total = 0;
|
|
294
|
+
for (const part of t.split(":")) {
|
|
295
|
+
if (part.trim() === "") return void 0;
|
|
296
|
+
const n = Number(part);
|
|
297
|
+
if (!Number.isFinite(n)) return void 0;
|
|
298
|
+
total = total * 60 + n;
|
|
299
|
+
}
|
|
300
|
+
return total;
|
|
301
|
+
}
|
|
288
302
|
var STREAMING_PROVIDERS = [
|
|
289
303
|
"spotify",
|
|
290
304
|
"apple_music",
|
|
@@ -366,9 +380,6 @@ var RECOGNITION_KEYS = [
|
|
|
366
380
|
function parseRecognitionResult(raw) {
|
|
367
381
|
const r = requireObject(raw, "RecognitionResult");
|
|
368
382
|
const timecode = asString(r.timecode);
|
|
369
|
-
if (timecode === void 0) {
|
|
370
|
-
throw new TypeError("RecognitionResult: timecode missing or not string");
|
|
371
|
-
}
|
|
372
383
|
const audioId = asNumber(r.audio_id);
|
|
373
384
|
const artist = asString(r.artist);
|
|
374
385
|
const title = asString(r.title);
|
|
@@ -437,14 +448,14 @@ var ENTERPRISE_MATCH_KEYS = [
|
|
|
437
448
|
"upc",
|
|
438
449
|
"song_link",
|
|
439
450
|
"start_offset",
|
|
440
|
-
"end_offset"
|
|
451
|
+
"end_offset",
|
|
452
|
+
"start_seconds",
|
|
453
|
+
"end_seconds"
|
|
441
454
|
];
|
|
442
455
|
function parseEnterpriseMatch(raw) {
|
|
443
456
|
const r = requireObject(raw, "EnterpriseMatch");
|
|
444
457
|
const score = asNumber(r.score);
|
|
445
458
|
const timecode = asString(r.timecode);
|
|
446
|
-
if (score === void 0) throw new TypeError("EnterpriseMatch: score not number");
|
|
447
|
-
if (timecode === void 0) throw new TypeError("EnterpriseMatch: timecode not string");
|
|
448
459
|
const songLink = asString(r.song_link);
|
|
449
460
|
return {
|
|
450
461
|
score,
|
|
@@ -459,6 +470,8 @@ function parseEnterpriseMatch(raw) {
|
|
|
459
470
|
songLink,
|
|
460
471
|
startOffset: asNumber(r.start_offset),
|
|
461
472
|
endOffset: asNumber(r.end_offset),
|
|
473
|
+
startSeconds: void 0,
|
|
474
|
+
endSeconds: void 0,
|
|
462
475
|
extras: pickExtras(r, ENTERPRISE_MATCH_KEYS),
|
|
463
476
|
rawResponse: r,
|
|
464
477
|
streamingUrl(provider) {
|
|
@@ -491,14 +504,19 @@ function parseEnterpriseMatch(raw) {
|
|
|
491
504
|
var ENTERPRISE_CHUNK_KEYS = ["songs", "offset"];
|
|
492
505
|
function parseEnterpriseChunkResult(raw) {
|
|
493
506
|
const r = requireObject(raw, "EnterpriseChunkResult");
|
|
494
|
-
const songsRaw = r.songs;
|
|
495
|
-
if (!Array.isArray(songsRaw)) {
|
|
496
|
-
throw new TypeError("EnterpriseChunkResult: songs not array");
|
|
497
|
-
}
|
|
507
|
+
const songsRaw = Array.isArray(r.songs) ? r.songs : [];
|
|
498
508
|
const offset = asString(r.offset);
|
|
499
|
-
|
|
509
|
+
const base = parseOffsetToSeconds(r.offset);
|
|
510
|
+
const songs = songsRaw.map((s) => {
|
|
511
|
+
const match = parseEnterpriseMatch(s);
|
|
512
|
+
if (base !== void 0) {
|
|
513
|
+
match.startSeconds = base + (match.startOffset ?? 0) / 1e3;
|
|
514
|
+
match.endSeconds = base + (match.endOffset ?? 0) / 1e3;
|
|
515
|
+
}
|
|
516
|
+
return match;
|
|
517
|
+
});
|
|
500
518
|
return {
|
|
501
|
-
songs
|
|
519
|
+
songs,
|
|
502
520
|
offset,
|
|
503
521
|
extras: pickExtras(r, ENTERPRISE_CHUNK_KEYS),
|
|
504
522
|
rawResponse: r
|
|
@@ -510,9 +528,6 @@ function parseStream(raw) {
|
|
|
510
528
|
const radioId = asNumber(r.radio_id);
|
|
511
529
|
const url = asString(r.url);
|
|
512
530
|
const streamRunning = asBoolean(r.stream_running);
|
|
513
|
-
if (radioId === void 0) throw new TypeError("Stream: radio_id not number");
|
|
514
|
-
if (url === void 0) throw new TypeError("Stream: url not string");
|
|
515
|
-
if (streamRunning === void 0) throw new TypeError("Stream: stream_running not boolean");
|
|
516
531
|
return {
|
|
517
532
|
radioId,
|
|
518
533
|
url,
|
|
@@ -543,9 +558,6 @@ function parseStreamCallbackSong(raw) {
|
|
|
543
558
|
const artist = asString(r.artist);
|
|
544
559
|
const title = asString(r.title);
|
|
545
560
|
const score = asNumber(r.score);
|
|
546
|
-
if (artist === void 0) throw new TypeError("StreamCallbackSong: artist not string");
|
|
547
|
-
if (title === void 0) throw new TypeError("StreamCallbackSong: title not string");
|
|
548
|
-
if (score === void 0) throw new TypeError("StreamCallbackSong: score not number");
|
|
549
561
|
const musicbrainz = Array.isArray(r.musicbrainz) ? r.musicbrainz.filter((x) => asObject(x) !== void 0) : void 0;
|
|
550
562
|
return {
|
|
551
563
|
artist,
|
|
@@ -574,13 +586,9 @@ var STREAM_CALLBACK_MATCH_KEYS = [
|
|
|
574
586
|
function parseStreamCallbackMatch(raw) {
|
|
575
587
|
const r = requireObject(raw, "StreamCallbackMatch");
|
|
576
588
|
const radioId = asNumber(r.radio_id);
|
|
577
|
-
|
|
578
|
-
const
|
|
579
|
-
if (!Array.isArray(resultsRaw)) throw new TypeError("StreamCallbackMatch: results not array");
|
|
580
|
-
if (resultsRaw.length === 0) throw new TypeError("StreamCallbackMatch: results is empty");
|
|
581
|
-
const songs = resultsRaw.map(parseStreamCallbackSong);
|
|
589
|
+
const resultsRaw = Array.isArray(r.results) ? r.results : [];
|
|
590
|
+
const songs = resultsRaw.filter((x) => asObject(x) !== void 0).map(parseStreamCallbackSong);
|
|
582
591
|
const [first, ...rest] = songs;
|
|
583
|
-
if (first === void 0) throw new TypeError("StreamCallbackMatch: results is empty");
|
|
584
592
|
return {
|
|
585
593
|
radioId,
|
|
586
594
|
timestamp: asString(r.timestamp),
|
|
@@ -600,14 +608,8 @@ var STREAM_CALLBACK_NOTIFICATION_KEYS = [
|
|
|
600
608
|
function parseStreamCallbackNotification(raw) {
|
|
601
609
|
const r = requireObject(raw, "StreamCallbackNotification");
|
|
602
610
|
const radioId = asNumber(r.radio_id);
|
|
603
|
-
if (radioId === void 0)
|
|
604
|
-
throw new TypeError("StreamCallbackNotification: radio_id not number");
|
|
605
611
|
const code = asNumber(r.notification_code);
|
|
606
|
-
if (code === void 0)
|
|
607
|
-
throw new TypeError("StreamCallbackNotification: notification_code not number");
|
|
608
612
|
const message = asString(r.notification_message);
|
|
609
|
-
if (message === void 0)
|
|
610
|
-
throw new TypeError("StreamCallbackNotification: notification_message not string");
|
|
611
613
|
return {
|
|
612
614
|
radioId,
|
|
613
615
|
streamRunning: asBoolean(r.stream_running),
|
|
@@ -631,8 +633,6 @@ function parseLyricsResult(raw) {
|
|
|
631
633
|
const r = requireObject(raw, "LyricsResult");
|
|
632
634
|
const artist = asString(r.artist);
|
|
633
635
|
const title = asString(r.title);
|
|
634
|
-
if (artist === void 0) throw new TypeError("LyricsResult: artist not string");
|
|
635
|
-
if (title === void 0) throw new TypeError("LyricsResult: title not string");
|
|
636
636
|
return {
|
|
637
637
|
artist,
|
|
638
638
|
title,
|
|
@@ -1403,8 +1403,8 @@ function buildEnterpriseFields(opts) {
|
|
|
1403
1403
|
if (opts.skipFirstSeconds !== void 0)
|
|
1404
1404
|
fields["skip_first_seconds"] = String(opts.skipFirstSeconds);
|
|
1405
1405
|
if (opts.useTimecode !== void 0) fields["use_timecode"] = opts.useTimecode ? "true" : "false";
|
|
1406
|
-
|
|
1407
|
-
|
|
1406
|
+
const accurate = opts.accurateOffsets ?? true;
|
|
1407
|
+
fields["accurate_offsets"] = accurate ? "true" : "false";
|
|
1408
1408
|
return fields;
|
|
1409
1409
|
}
|
|
1410
1410
|
function isDeprecationPassThrough(body) {
|