@audd/sdk 1.5.6 → 1.5.8
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 +67 -4
- package/dist/index.cjs +25 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -7
- package/dist/index.d.ts +36 -7
- package/dist/index.js +25 -14
- package/dist/index.js.map +1 -1
- package/dist/longpoll.cjs +5 -1
- package/dist/longpoll.cjs.map +1 -1
- package/dist/longpoll.js +5 -1
- package/dist/longpoll.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,14 +96,14 @@ only what you need — each provider you ask for adds latency:
|
|
|
96
96
|
|
|
97
97
|
```ts
|
|
98
98
|
const song = await audd.recognize("https://audd.tech/example.mp3", {
|
|
99
|
-
|
|
99
|
+
returnMetadata: ["apple_music", "spotify"],
|
|
100
100
|
});
|
|
101
101
|
console.log(song?.appleMusic?.url); // direct Apple Music link
|
|
102
102
|
console.log(song?.spotify?.uri); // spotify:track:...
|
|
103
103
|
console.log(song?.previewUrl()); // first preview across requested providers, or null
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
Valid `
|
|
106
|
+
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
|
|
@@ -121,6 +121,17 @@ 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 — undocumented parameters or beta features:
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
await audd.recognize(url, {
|
|
128
|
+
returnMetadata: "apple_music",
|
|
129
|
+
extraParameters: { some_beta_flag: "true" },
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The same `extraParameters` field is on `RecognizeEnterpriseOptions`, `SetCallbackUrlOptions`, and `AddStreamOptions`. Typed options win on collision.
|
|
134
|
+
|
|
124
135
|
## Long files (enterprise)
|
|
125
136
|
|
|
126
137
|
`recognizeEnterprise` accepts files up to several hours and returns a
|
|
@@ -273,6 +284,58 @@ If you already have the body bytes (queue consumer, replay tool), call
|
|
|
273
284
|
`parseCallback(body)` directly — it accepts a parsed JSON object or a
|
|
274
285
|
JSON string and returns the same `{ match, notification }` shape.
|
|
275
286
|
|
|
287
|
+
#### Per-framework wiring
|
|
288
|
+
|
|
289
|
+
The same `handleCallback(req)` works across Node web frameworks — register
|
|
290
|
+
a POST route and pass the request object in.
|
|
291
|
+
|
|
292
|
+
`Fastify`:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
import Fastify from "fastify";
|
|
296
|
+
import { handleCallback } from "@audd/sdk";
|
|
297
|
+
|
|
298
|
+
const app = Fastify();
|
|
299
|
+
app.post("/audd-callback", async (req, reply) => {
|
|
300
|
+
const { match } = await handleCallback(req);
|
|
301
|
+
if (match) console.log(`${match.song.artist} — ${match.song.title}`);
|
|
302
|
+
return { ok: true };
|
|
303
|
+
});
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
`Koa`:
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
import Koa from "koa";
|
|
310
|
+
import Router from "@koa/router";
|
|
311
|
+
import bodyParser from "koa-bodyparser";
|
|
312
|
+
import { handleCallback } from "@audd/sdk";
|
|
313
|
+
|
|
314
|
+
const app = new Koa();
|
|
315
|
+
const router = new Router();
|
|
316
|
+
|
|
317
|
+
app.use(bodyParser());
|
|
318
|
+
router.post("/audd-callback", async (ctx) => {
|
|
319
|
+
const { match } = await handleCallback(ctx.request);
|
|
320
|
+
if (match) console.log(`${match.song.artist} — ${match.song.title}`);
|
|
321
|
+
ctx.body = { ok: true };
|
|
322
|
+
});
|
|
323
|
+
app.use(router.routes());
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
`Next.js` (App Router, `app/api/audd-callback/route.ts`):
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
330
|
+
import { handleCallback } from "@audd/sdk";
|
|
331
|
+
|
|
332
|
+
export async function POST(req: NextRequest) {
|
|
333
|
+
const { match } = await handleCallback(req);
|
|
334
|
+
if (match) console.log(`${match.song.artist} — ${match.song.title}`);
|
|
335
|
+
return NextResponse.json({ ok: true });
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
276
339
|
### Receiving events without a callback URL (longpoll)
|
|
277
340
|
|
|
278
341
|
Useful when you can't expose a public HTTPS receiver. The poll handle
|
|
@@ -286,9 +349,9 @@ callback URL set, and the preflight surfaces that as an actionable
|
|
|
286
349
|
error. Pass `skipCallbackCheck: true` to bypass.
|
|
287
350
|
|
|
288
351
|
```ts
|
|
289
|
-
const
|
|
290
|
-
const poll = await audd.streams.longpoll(category, { timeout: 30 });
|
|
352
|
+
const radioId = 1; // any integer you choose — your handle for this stream
|
|
291
353
|
|
|
354
|
+
const poll = await audd.streams.longpoll({ radioId, timeout: 30 });
|
|
292
355
|
for await (const m of poll.matches) {
|
|
293
356
|
console.log(m.song.artist, m.song.title);
|
|
294
357
|
}
|
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.
|
|
28
|
+
var VERSION = "1.5.8";
|
|
29
29
|
|
|
30
30
|
// src/errors.ts
|
|
31
31
|
var AudDError = class extends Error {
|
|
@@ -684,6 +684,8 @@ function shouldRetryResponse(resp, retryClass) {
|
|
|
684
684
|
return s >= HTTP_SERVER_ERROR_FLOOR;
|
|
685
685
|
case "mutating":
|
|
686
686
|
return false;
|
|
687
|
+
case "none":
|
|
688
|
+
return false;
|
|
687
689
|
}
|
|
688
690
|
}
|
|
689
691
|
function shouldRetryError(err, retryClass) {
|
|
@@ -694,6 +696,8 @@ function shouldRetryError(err, retryClass) {
|
|
|
694
696
|
return isPreUploadConnectionError(err);
|
|
695
697
|
case "mutating":
|
|
696
698
|
return isPreUploadConnectionError(err);
|
|
699
|
+
case "none":
|
|
700
|
+
return false;
|
|
697
701
|
}
|
|
698
702
|
}
|
|
699
703
|
function sleep(ms) {
|
|
@@ -1123,7 +1127,9 @@ var Streams = class {
|
|
|
1123
1127
|
*/
|
|
1124
1128
|
async setCallbackUrl(url, opts = {}) {
|
|
1125
1129
|
const finalUrl = addReturnToUrl(url, opts.returnMetadata);
|
|
1126
|
-
|
|
1130
|
+
const fields = opts.extraParameters ? { ...opts.extraParameters } : {};
|
|
1131
|
+
fields["url"] = finalUrl;
|
|
1132
|
+
await this.post("setCallbackUrl", fields, this.mutatingPolicy);
|
|
1127
1133
|
}
|
|
1128
1134
|
/** Get the currently registered callback URL. */
|
|
1129
1135
|
async getCallbackUrl() {
|
|
@@ -1132,10 +1138,9 @@ var Streams = class {
|
|
|
1132
1138
|
}
|
|
1133
1139
|
/** Register a new stream for real-time recognition. */
|
|
1134
1140
|
async add(opts) {
|
|
1135
|
-
const fields = {
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
};
|
|
1141
|
+
const fields = opts.extraParameters ? { ...opts.extraParameters } : {};
|
|
1142
|
+
fields["url"] = opts.url;
|
|
1143
|
+
fields["radio_id"] = String(opts.radioId);
|
|
1139
1144
|
if (opts.callbacks !== void 0) fields["callbacks"] = opts.callbacks;
|
|
1140
1145
|
await this.post("addStream", fields, this.mutatingPolicy);
|
|
1141
1146
|
}
|
|
@@ -1266,12 +1271,12 @@ async function runRetried2(fn, policy) {
|
|
|
1266
1271
|
}
|
|
1267
1272
|
}
|
|
1268
1273
|
var CustomCatalog = class {
|
|
1269
|
-
constructor(http,
|
|
1274
|
+
constructor(http, noRetryPolicy) {
|
|
1270
1275
|
this.http = http;
|
|
1271
|
-
this.
|
|
1276
|
+
this.noRetryPolicy = noRetryPolicy;
|
|
1272
1277
|
}
|
|
1273
1278
|
http;
|
|
1274
|
-
|
|
1279
|
+
noRetryPolicy;
|
|
1275
1280
|
/**
|
|
1276
1281
|
* **This is NOT how you submit audio for music recognition.** For
|
|
1277
1282
|
* recognition, use `audd.recognize()` (or `audd.recognizeEnterprise()` for
|
|
@@ -1283,6 +1288,11 @@ var CustomCatalog = class {
|
|
|
1283
1288
|
* Calling this again with the same `audioId` re-fingerprints that slot.
|
|
1284
1289
|
* There is no public list/delete endpoint; track `audioId` ↔ song
|
|
1285
1290
|
* mappings on your side.
|
|
1291
|
+
*
|
|
1292
|
+
* **No automatic retry.** Custom-catalog upload is metered, and a transport
|
|
1293
|
+
* failure could otherwise cause a silent re-upload that double-charges. Any
|
|
1294
|
+
* 5xx or pre-upload connection error surfaces as a clean exception — the
|
|
1295
|
+
* caller decides whether to retry.
|
|
1286
1296
|
*/
|
|
1287
1297
|
async add(opts) {
|
|
1288
1298
|
const reopen = prepareSource(opts.source);
|
|
@@ -1291,7 +1301,7 @@ var CustomCatalog = class {
|
|
|
1291
1301
|
const prepared = await reopen();
|
|
1292
1302
|
const fields = { ...prepared.fields, audio_id: audioId };
|
|
1293
1303
|
return this.http.postForm(UPLOAD_URL, fields);
|
|
1294
|
-
}, this.
|
|
1304
|
+
}, this.noRetryPolicy);
|
|
1295
1305
|
decodeSuccess2(resp.jsonBody, resp.httpStatus, resp.requestId);
|
|
1296
1306
|
}
|
|
1297
1307
|
};
|
|
@@ -1384,8 +1394,8 @@ function formatReturn(value) {
|
|
|
1384
1394
|
return Array.isArray(value) ? value.join(",") : value;
|
|
1385
1395
|
}
|
|
1386
1396
|
function buildEnterpriseFields(opts) {
|
|
1387
|
-
const fields = {};
|
|
1388
|
-
const ret = formatReturn(opts.
|
|
1397
|
+
const fields = opts.extraParameters ? { ...opts.extraParameters } : {};
|
|
1398
|
+
const ret = formatReturn(opts.returnMetadata);
|
|
1389
1399
|
if (ret !== void 0) fields["return"] = ret;
|
|
1390
1400
|
if (opts.skip !== void 0) fields["skip"] = String(opts.skip);
|
|
1391
1401
|
if (opts.every !== void 0) fields["every"] = String(opts.every);
|
|
@@ -1535,7 +1545,7 @@ var AudD = class {
|
|
|
1535
1545
|
/** Sub-namespace for the private fingerprint catalog. NOT for recognition. */
|
|
1536
1546
|
get customCatalog() {
|
|
1537
1547
|
if (this._customCatalog === void 0) {
|
|
1538
|
-
this._customCatalog = new CustomCatalog(this._http, this.policyFor("
|
|
1548
|
+
this._customCatalog = new CustomCatalog(this._http, this.policyFor("none"));
|
|
1539
1549
|
}
|
|
1540
1550
|
return this._customCatalog;
|
|
1541
1551
|
}
|
|
@@ -1554,7 +1564,7 @@ var AudD = class {
|
|
|
1554
1564
|
*/
|
|
1555
1565
|
async recognize(source, opts = {}) {
|
|
1556
1566
|
const reopen = prepareSource(source);
|
|
1557
|
-
const ret = formatReturn(opts.
|
|
1567
|
+
const ret = formatReturn(opts.returnMetadata);
|
|
1558
1568
|
const market = opts.market;
|
|
1559
1569
|
const policy = this.policyFor("recognition");
|
|
1560
1570
|
const url = `${API_BASE3}/`;
|
|
@@ -1574,6 +1584,7 @@ var AudD = class {
|
|
|
1574
1584
|
resp = await runRetried4(async () => {
|
|
1575
1585
|
const prepared = await reopen();
|
|
1576
1586
|
const fields = { ...prepared.fields };
|
|
1587
|
+
if (opts.extraParameters) Object.assign(fields, opts.extraParameters);
|
|
1577
1588
|
if (ret !== void 0) fields["return"] = ret;
|
|
1578
1589
|
if (market !== void 0) fields["market"] = market;
|
|
1579
1590
|
return this._http.postForm(url, fields, {
|