@transcribe-api/sdk 0.1.6 → 0.1.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 +52 -1
- package/index.js +164 -10
- package/package.json +1 -1
- package/worker.js +164 -10
package/README.md
CHANGED
|
@@ -68,6 +68,10 @@ const client = new TranscribeAPI({
|
|
|
68
68
|
apiKey: "YOUR_API_KEY",
|
|
69
69
|
baseUrl: "https://api.transcribeapi.com/v1",
|
|
70
70
|
uploadConcurrency: 4,
|
|
71
|
+
autoReload: {
|
|
72
|
+
amount: 10,
|
|
73
|
+
ifBalanceBelow: 20,
|
|
74
|
+
},
|
|
71
75
|
showLogs: true,
|
|
72
76
|
logger: console,
|
|
73
77
|
polling: {
|
|
@@ -82,6 +86,7 @@ const client = new TranscribeAPI({
|
|
|
82
86
|
| `apiKey` | Required. Your Transcribe API key. |
|
|
83
87
|
| `baseUrl` | Optional. Defaults to `https://api.transcribeapi.com/v1`. |
|
|
84
88
|
| `uploadConcurrency` | Optional. Number of in-flight upload PUTs. Defaults to `1` and is capped at `32`. |
|
|
89
|
+
| `autoReload` | Optional. Automatically calls `POST /v1/add-funds` after a transcription result when `remaining_balance` is below `ifBalanceBelow`. |
|
|
85
90
|
| `showLogs` | Optional. Prints upload progress, upload completion, polling status, and final result info. |
|
|
86
91
|
| `logger` | Optional. Logger object with a `log` method. Defaults to `console`. |
|
|
87
92
|
| `polling` | Optional. `{ interval, timeout }` in seconds. `interval` must be at least `10`. Omit or set to `null`/`false` to disable automatic polling. |
|
|
@@ -185,7 +190,53 @@ You can also pass a comma-separated string:
|
|
|
185
190
|
exclude: "metadata,billing"
|
|
186
191
|
```
|
|
187
192
|
|
|
188
|
-
|
|
193
|
+
For single-file outputs, if `exclude` leaves only one transcript field and removes metadata, billing, and detected language, the API returns that remaining value directly instead of a JSON object. Text-only responses return a plain string, VTT-only responses return a plain string, and segments-only responses return an array. Multi-file outputs still return JSON so each result stays associated with its `reference_id`.
|
|
194
|
+
|
|
195
|
+
If `autoReload` is enabled, the SDK always keeps billing in the request even if you included `billing` in `exclude`. It needs `remaining_balance` to decide whether to call `POST /v1/add-funds`.
|
|
196
|
+
|
|
197
|
+
## Auto reload
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
const client = new TranscribeAPI({
|
|
201
|
+
apiKey: process.env.TRANSCRIBE_API_KEY,
|
|
202
|
+
autoReload: {
|
|
203
|
+
amount: 10,
|
|
204
|
+
ifBalanceBelow: 20,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
When `autoReload` is enabled, the SDK:
|
|
210
|
+
|
|
211
|
+
- checks `remaining_balance` after a transcription result
|
|
212
|
+
- only calls `POST /v1/add-funds` when the balance is below `ifBalanceBelow`
|
|
213
|
+
- sends `{ amount, if_balance_below }` to the API for backend-side protection too
|
|
214
|
+
- ignores `billing` inside `exclude` so the SDK can always read `remaining_balance`
|
|
215
|
+
|
|
216
|
+
Auto reload only runs when the transcription result returned to the SDK includes `remaining_balance`. If you disable polling for async jobs, immediate non-terminal async responses do not have enough billing data for the SDK to auto reload yet.
|
|
217
|
+
|
|
218
|
+
### Manual addFunds for webhooks
|
|
219
|
+
|
|
220
|
+
When using webhooks with batch jobs, the transcription result goes to your webhook URL instead of the SDK. The `autoReload` config won't fire because the SDK never sees the result. Call `client.addFunds()` inside your webhook handler instead:
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
// Webhook handler (Express example)
|
|
224
|
+
app.post("/webhook", async (req, res) => {
|
|
225
|
+
const { remaining_balance } = req.body;
|
|
226
|
+
|
|
227
|
+
if (remaining_balance < 20) {
|
|
228
|
+
const client = new TranscribeAPI({ apiKey: process.env.TRANSCRIBE_API_KEY });
|
|
229
|
+
await client.addFunds(10, { ifBalanceBelow: 20 });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
res.sendStatus(200);
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`addFunds(amount, { ifBalanceBelow })` calls `POST /v1/add-funds`:
|
|
237
|
+
|
|
238
|
+
- `amount` — whole dollar amount from 5 to 100.
|
|
239
|
+
- `ifBalanceBelow` — server-side guard. The API only charges if your current balance is actually below this threshold, making it safe against duplicate webhook deliveries.
|
|
189
240
|
|
|
190
241
|
## Webhooks
|
|
191
242
|
|
package/index.js
CHANGED
|
@@ -143,6 +143,54 @@ function normalizePollingConfig(polling) {
|
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
function normalizeAutoReloadConfig(autoReload) {
|
|
147
|
+
if (autoReload === undefined || autoReload === null || autoReload === false) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (typeof autoReload !== "object" || Array.isArray(autoReload)) {
|
|
151
|
+
throw new TranscribeAPIError("`autoReload` must be an object with `amount` and `ifBalanceBelow`.", {
|
|
152
|
+
code: "invalid_auto_reload_config",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const amount = Number.parseInt(String(autoReload.amount ?? ""), 10);
|
|
157
|
+
if (!Number.isInteger(amount) || amount < 5 || amount > 100) {
|
|
158
|
+
throw new TranscribeAPIError("`autoReload.amount` must be a whole dollar value from 5 to 100.", {
|
|
159
|
+
code: "invalid_auto_reload_amount",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const rawIfBalanceBelow = autoReload.ifBalanceBelow ?? autoReload.if_balance_below;
|
|
164
|
+
const ifBalanceBelow = Number(rawIfBalanceBelow);
|
|
165
|
+
if (!Number.isFinite(ifBalanceBelow) || ifBalanceBelow < 0) {
|
|
166
|
+
throw new TranscribeAPIError("`autoReload.ifBalanceBelow` must be a number >= 0.", {
|
|
167
|
+
code: "invalid_auto_reload_if_balance_below",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
amount,
|
|
173
|
+
ifBalanceBelow,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function normalizeExcludeList(exclude) {
|
|
178
|
+
if (exclude === undefined || exclude === null || exclude === "") {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
if (Array.isArray(exclude)) {
|
|
182
|
+
return exclude.map((item) => String(item || "").trim()).filter(Boolean);
|
|
183
|
+
}
|
|
184
|
+
return String(exclude)
|
|
185
|
+
.split(",")
|
|
186
|
+
.map((item) => item.trim())
|
|
187
|
+
.filter(Boolean);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function stringifyExclude(excludeList) {
|
|
191
|
+
return excludeList.length ? excludeList.join(",") : null;
|
|
192
|
+
}
|
|
193
|
+
|
|
146
194
|
function contentTypeFromName(name = "") {
|
|
147
195
|
const lower = String(name).toLowerCase();
|
|
148
196
|
if (lower.endsWith(".mp3")) return "audio/mpeg";
|
|
@@ -1229,6 +1277,7 @@ export class TranscribeAPI {
|
|
|
1229
1277
|
showLogs = false,
|
|
1230
1278
|
logger = console,
|
|
1231
1279
|
polling = null,
|
|
1280
|
+
autoReload = null,
|
|
1232
1281
|
} = {}) {
|
|
1233
1282
|
if (!apiKey) {
|
|
1234
1283
|
throw new Error("Missing API key");
|
|
@@ -1240,6 +1289,7 @@ export class TranscribeAPI {
|
|
|
1240
1289
|
this.showLogs = Boolean(showLogs);
|
|
1241
1290
|
this.logger = logger || console;
|
|
1242
1291
|
this.polling = normalizePollingConfig(polling);
|
|
1292
|
+
this.autoReload = normalizeAutoReloadConfig(autoReload);
|
|
1243
1293
|
this.jobs = {
|
|
1244
1294
|
createBigFile: (options) => this.createBigFileJob(options),
|
|
1245
1295
|
createBatch: (options) => this.createBatchJob(options),
|
|
@@ -1265,6 +1315,54 @@ export class TranscribeAPI {
|
|
|
1265
1315
|
return retryable ? retry(run) : run();
|
|
1266
1316
|
}
|
|
1267
1317
|
|
|
1318
|
+
getEffectiveExclude(exclude) {
|
|
1319
|
+
const excludeList = normalizeExcludeList(exclude);
|
|
1320
|
+
if (!this.autoReload) {
|
|
1321
|
+
return excludeList;
|
|
1322
|
+
}
|
|
1323
|
+
return excludeList.filter((field) => field !== "billing");
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
async addFunds(amount, { ifBalanceBelow } = {}) {
|
|
1327
|
+
const body = { amount };
|
|
1328
|
+
if (ifBalanceBelow !== undefined && ifBalanceBelow !== null) {
|
|
1329
|
+
body.if_balance_below = ifBalanceBelow;
|
|
1330
|
+
}
|
|
1331
|
+
return this.requestJson("/add-funds", {
|
|
1332
|
+
method: "POST",
|
|
1333
|
+
body,
|
|
1334
|
+
retryable: true,
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
async maybeAutoReload(result, { showLogs = this.showLogs, logger = this.logger } = {}) {
|
|
1339
|
+
if (!this.autoReload || !result || typeof result !== "object" || Array.isArray(result)) {
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
const remainingBalance = Number(result.remaining_balance);
|
|
1344
|
+
if (!Number.isFinite(remainingBalance) || remainingBalance >= this.autoReload.ifBalanceBelow) {
|
|
1345
|
+
return result;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
try {
|
|
1349
|
+
const autoReloadResult = await this.addFunds(this.autoReload.amount, {
|
|
1350
|
+
ifBalanceBelow: this.autoReload.ifBalanceBelow,
|
|
1351
|
+
});
|
|
1352
|
+
if (showLogs && typeof logger?.log === "function") {
|
|
1353
|
+
logger.log(JSON.stringify(autoReloadResult, null, 2));
|
|
1354
|
+
}
|
|
1355
|
+
} catch (error) {
|
|
1356
|
+
if (showLogs && typeof logger?.log === "function") {
|
|
1357
|
+
logger.log(JSON.stringify({
|
|
1358
|
+
auto_reload_error: extractErrorInfo(error),
|
|
1359
|
+
}, null, 2));
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
return result;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1268
1366
|
async waitForJobCompletion(jobId, {
|
|
1269
1367
|
polling = this.polling,
|
|
1270
1368
|
showLogs = this.showLogs,
|
|
@@ -1325,6 +1423,7 @@ export class TranscribeAPI {
|
|
|
1325
1423
|
exclude,
|
|
1326
1424
|
uploadConcurrency,
|
|
1327
1425
|
} = {}) {
|
|
1426
|
+
const effectiveExclude = this.getEffectiveExclude(exclude);
|
|
1328
1427
|
const progress = composeProgressHandler({
|
|
1329
1428
|
onProgress,
|
|
1330
1429
|
showLogs: showLogs ?? this.showLogs,
|
|
@@ -1353,7 +1452,7 @@ export class TranscribeAPI {
|
|
|
1353
1452
|
logger: logger ?? this.logger,
|
|
1354
1453
|
uploadConcurrency,
|
|
1355
1454
|
language,
|
|
1356
|
-
exclude,
|
|
1455
|
+
exclude: effectiveExclude,
|
|
1357
1456
|
});
|
|
1358
1457
|
}
|
|
1359
1458
|
|
|
@@ -1364,8 +1463,6 @@ export class TranscribeAPI {
|
|
|
1364
1463
|
: (normalizedItem.durationEstimateSec || await estimateDurationFromFile(normalizedSingleFile));
|
|
1365
1464
|
const effectiveLanguage = normalizedItem.hasLanguage ? normalizedItem.language : normalizeLanguageCode(language);
|
|
1366
1465
|
const isAsync = files.length > 1
|
|
1367
|
-
|| Boolean(normalizedItem.url)
|
|
1368
|
-
|| Boolean(webhookUrl)
|
|
1369
1466
|
|| (
|
|
1370
1467
|
normalizedSingleFile
|
|
1371
1468
|
&& (
|
|
@@ -1383,7 +1480,19 @@ export class TranscribeAPI {
|
|
|
1383
1480
|
logger: logger ?? this.logger,
|
|
1384
1481
|
uploadConcurrency,
|
|
1385
1482
|
language,
|
|
1386
|
-
exclude,
|
|
1483
|
+
exclude: effectiveExclude,
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
if (normalizedItem.url) {
|
|
1488
|
+
return this.transcribeUrlDirect({
|
|
1489
|
+
url: normalizedItem.url,
|
|
1490
|
+
referenceId: normalizedItem.referenceId,
|
|
1491
|
+
language: effectiveLanguage,
|
|
1492
|
+
exclude: effectiveExclude,
|
|
1493
|
+
webhookUrl,
|
|
1494
|
+
showLogs: showLogs ?? this.showLogs,
|
|
1495
|
+
logger: logger ?? this.logger,
|
|
1387
1496
|
});
|
|
1388
1497
|
}
|
|
1389
1498
|
|
|
@@ -1391,7 +1500,7 @@ export class TranscribeAPI {
|
|
|
1391
1500
|
file: normalizedSingleFile,
|
|
1392
1501
|
referenceId: normalizedItem.referenceId,
|
|
1393
1502
|
language: effectiveLanguage,
|
|
1394
|
-
exclude,
|
|
1503
|
+
exclude: effectiveExclude,
|
|
1395
1504
|
webhookUrl,
|
|
1396
1505
|
showLogs: showLogs ?? this.showLogs,
|
|
1397
1506
|
logger: logger ?? this.logger,
|
|
@@ -1440,7 +1549,10 @@ export class TranscribeAPI {
|
|
|
1440
1549
|
if ((showLogs ?? this.showLogs) && TERMINAL_JOB_STATUSES.has(String(result?.job_status || ""))) {
|
|
1441
1550
|
logTerminalAsyncResult(result, logger ?? this.logger);
|
|
1442
1551
|
}
|
|
1443
|
-
return result
|
|
1552
|
+
return this.maybeAutoReload(result, {
|
|
1553
|
+
showLogs: showLogs ?? this.showLogs,
|
|
1554
|
+
logger: logger ?? this.logger,
|
|
1555
|
+
});
|
|
1444
1556
|
}
|
|
1445
1557
|
|
|
1446
1558
|
async transcribeDirect({
|
|
@@ -1460,7 +1572,8 @@ export class TranscribeAPI {
|
|
|
1460
1572
|
if (String(referenceId || "").trim()) form.set("reference_id", String(referenceId).trim());
|
|
1461
1573
|
form.set("file", directFile, directFile.name);
|
|
1462
1574
|
if (language) form.set("language", language);
|
|
1463
|
-
|
|
1575
|
+
const excludeValue = stringifyExclude(this.getEffectiveExclude(exclude));
|
|
1576
|
+
if (excludeValue) form.set("exclude", excludeValue);
|
|
1464
1577
|
if (webhookUrl) form.set("webhook_url", webhookUrl);
|
|
1465
1578
|
|
|
1466
1579
|
const response = await fetch(`${this.baseUrl}/transcribe`, {
|
|
@@ -1479,10 +1592,51 @@ export class TranscribeAPI {
|
|
|
1479
1592
|
initialJob: result,
|
|
1480
1593
|
});
|
|
1481
1594
|
}
|
|
1595
|
+
const finalResult = await this.maybeAutoReload(result, { showLogs, logger });
|
|
1482
1596
|
if (showLogs && typeof logger?.log === "function") {
|
|
1483
|
-
logger.log(typeof
|
|
1597
|
+
logger.log(typeof finalResult === "string" ? finalResult : JSON.stringify(finalResult, null, 2));
|
|
1484
1598
|
}
|
|
1485
|
-
return
|
|
1599
|
+
return finalResult;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
async transcribeUrlDirect({
|
|
1603
|
+
url,
|
|
1604
|
+
referenceId,
|
|
1605
|
+
language,
|
|
1606
|
+
exclude,
|
|
1607
|
+
webhookUrl,
|
|
1608
|
+
showLogs = false,
|
|
1609
|
+
logger = console,
|
|
1610
|
+
} = {}) {
|
|
1611
|
+
const form = new FormData();
|
|
1612
|
+
form.set("url", String(url || "").trim());
|
|
1613
|
+
if (String(referenceId || "").trim()) form.set("reference_id", String(referenceId).trim());
|
|
1614
|
+
if (language) form.set("language", language);
|
|
1615
|
+
const excludeValue = stringifyExclude(this.getEffectiveExclude(exclude));
|
|
1616
|
+
if (excludeValue) form.set("exclude", excludeValue);
|
|
1617
|
+
if (webhookUrl) form.set("webhook_url", webhookUrl);
|
|
1618
|
+
|
|
1619
|
+
const response = await fetch(`${this.baseUrl}/transcribe`, {
|
|
1620
|
+
method: "POST",
|
|
1621
|
+
headers: {
|
|
1622
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
1623
|
+
},
|
|
1624
|
+
body: form,
|
|
1625
|
+
});
|
|
1626
|
+
const result = await parseApiResponse(response);
|
|
1627
|
+
if (this.polling && result && typeof result === "object" && result.job_status && !TERMINAL_JOB_STATUSES.has(String(result.job_status))) {
|
|
1628
|
+
return this.waitForJobCompletion(result.job_id, {
|
|
1629
|
+
polling: this.polling,
|
|
1630
|
+
showLogs,
|
|
1631
|
+
logger,
|
|
1632
|
+
initialJob: result,
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
const finalResult = await this.maybeAutoReload(result, { showLogs, logger });
|
|
1636
|
+
if (showLogs && typeof logger?.log === "function") {
|
|
1637
|
+
logger.log(typeof finalResult === "string" ? finalResult : JSON.stringify(finalResult, null, 2));
|
|
1638
|
+
}
|
|
1639
|
+
return finalResult;
|
|
1486
1640
|
}
|
|
1487
1641
|
|
|
1488
1642
|
async transcribeMany({
|
|
@@ -1602,7 +1756,7 @@ export class TranscribeAPI {
|
|
|
1602
1756
|
)),
|
|
1603
1757
|
...(normalizedLanguage ? { language: normalizedLanguage } : {}),
|
|
1604
1758
|
...(webhookUrl ? { webhook_url: webhookUrl } : {}),
|
|
1605
|
-
...(exclude ? { exclude:
|
|
1759
|
+
...(stringifyExclude(this.getEffectiveExclude(exclude)) ? { exclude: stringifyExclude(this.getEffectiveExclude(exclude)) } : {}),
|
|
1606
1760
|
},
|
|
1607
1761
|
retryable: true,
|
|
1608
1762
|
});
|
package/package.json
CHANGED
package/worker.js
CHANGED
|
@@ -143,6 +143,54 @@ function normalizePollingConfig(polling) {
|
|
|
143
143
|
};
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
function normalizeAutoReloadConfig(autoReload) {
|
|
147
|
+
if (autoReload === undefined || autoReload === null || autoReload === false) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (typeof autoReload !== "object" || Array.isArray(autoReload)) {
|
|
151
|
+
throw new TranscribeAPIError("`autoReload` must be an object with `amount` and `ifBalanceBelow`.", {
|
|
152
|
+
code: "invalid_auto_reload_config",
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const amount = Number.parseInt(String(autoReload.amount ?? ""), 10);
|
|
157
|
+
if (!Number.isInteger(amount) || amount < 5 || amount > 100) {
|
|
158
|
+
throw new TranscribeAPIError("`autoReload.amount` must be a whole dollar value from 5 to 100.", {
|
|
159
|
+
code: "invalid_auto_reload_amount",
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const rawIfBalanceBelow = autoReload.ifBalanceBelow ?? autoReload.if_balance_below;
|
|
164
|
+
const ifBalanceBelow = Number(rawIfBalanceBelow);
|
|
165
|
+
if (!Number.isFinite(ifBalanceBelow) || ifBalanceBelow < 0) {
|
|
166
|
+
throw new TranscribeAPIError("`autoReload.ifBalanceBelow` must be a number >= 0.", {
|
|
167
|
+
code: "invalid_auto_reload_if_balance_below",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
amount,
|
|
173
|
+
ifBalanceBelow,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function normalizeExcludeList(exclude) {
|
|
178
|
+
if (exclude === undefined || exclude === null || exclude === "") {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
if (Array.isArray(exclude)) {
|
|
182
|
+
return exclude.map((item) => String(item || "").trim()).filter(Boolean);
|
|
183
|
+
}
|
|
184
|
+
return String(exclude)
|
|
185
|
+
.split(",")
|
|
186
|
+
.map((item) => item.trim())
|
|
187
|
+
.filter(Boolean);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function stringifyExclude(excludeList) {
|
|
191
|
+
return excludeList.length ? excludeList.join(",") : null;
|
|
192
|
+
}
|
|
193
|
+
|
|
146
194
|
function contentTypeFromName(name = "") {
|
|
147
195
|
const lower = String(name).toLowerCase();
|
|
148
196
|
if (lower.endsWith(".mp3")) return "audio/mpeg";
|
|
@@ -1229,6 +1277,7 @@ export class TranscribeAPI {
|
|
|
1229
1277
|
showLogs = false,
|
|
1230
1278
|
logger = console,
|
|
1231
1279
|
polling = null,
|
|
1280
|
+
autoReload = null,
|
|
1232
1281
|
} = {}) {
|
|
1233
1282
|
if (!apiKey) {
|
|
1234
1283
|
throw new Error("Missing API key");
|
|
@@ -1240,6 +1289,7 @@ export class TranscribeAPI {
|
|
|
1240
1289
|
this.showLogs = Boolean(showLogs);
|
|
1241
1290
|
this.logger = logger || console;
|
|
1242
1291
|
this.polling = normalizePollingConfig(polling);
|
|
1292
|
+
this.autoReload = normalizeAutoReloadConfig(autoReload);
|
|
1243
1293
|
this.jobs = {
|
|
1244
1294
|
createBigFile: (options) => this.createBigFileJob(options),
|
|
1245
1295
|
createBatch: (options) => this.createBatchJob(options),
|
|
@@ -1265,6 +1315,54 @@ export class TranscribeAPI {
|
|
|
1265
1315
|
return retryable ? retry(run) : run();
|
|
1266
1316
|
}
|
|
1267
1317
|
|
|
1318
|
+
getEffectiveExclude(exclude) {
|
|
1319
|
+
const excludeList = normalizeExcludeList(exclude);
|
|
1320
|
+
if (!this.autoReload) {
|
|
1321
|
+
return excludeList;
|
|
1322
|
+
}
|
|
1323
|
+
return excludeList.filter((field) => field !== "billing");
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
async addFunds(amount, { ifBalanceBelow } = {}) {
|
|
1327
|
+
const body = { amount };
|
|
1328
|
+
if (ifBalanceBelow !== undefined && ifBalanceBelow !== null) {
|
|
1329
|
+
body.if_balance_below = ifBalanceBelow;
|
|
1330
|
+
}
|
|
1331
|
+
return this.requestJson("/add-funds", {
|
|
1332
|
+
method: "POST",
|
|
1333
|
+
body,
|
|
1334
|
+
retryable: true,
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
async maybeAutoReload(result, { showLogs = this.showLogs, logger = this.logger } = {}) {
|
|
1339
|
+
if (!this.autoReload || !result || typeof result !== "object" || Array.isArray(result)) {
|
|
1340
|
+
return result;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
const remainingBalance = Number(result.remaining_balance);
|
|
1344
|
+
if (!Number.isFinite(remainingBalance) || remainingBalance >= this.autoReload.ifBalanceBelow) {
|
|
1345
|
+
return result;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
try {
|
|
1349
|
+
const autoReloadResult = await this.addFunds(this.autoReload.amount, {
|
|
1350
|
+
ifBalanceBelow: this.autoReload.ifBalanceBelow,
|
|
1351
|
+
});
|
|
1352
|
+
if (showLogs && typeof logger?.log === "function") {
|
|
1353
|
+
logger.log(JSON.stringify(autoReloadResult, null, 2));
|
|
1354
|
+
}
|
|
1355
|
+
} catch (error) {
|
|
1356
|
+
if (showLogs && typeof logger?.log === "function") {
|
|
1357
|
+
logger.log(JSON.stringify({
|
|
1358
|
+
auto_reload_error: extractErrorInfo(error),
|
|
1359
|
+
}, null, 2));
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
return result;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1268
1366
|
async waitForJobCompletion(jobId, {
|
|
1269
1367
|
polling = this.polling,
|
|
1270
1368
|
showLogs = this.showLogs,
|
|
@@ -1325,6 +1423,7 @@ export class TranscribeAPI {
|
|
|
1325
1423
|
exclude,
|
|
1326
1424
|
uploadConcurrency,
|
|
1327
1425
|
} = {}) {
|
|
1426
|
+
const effectiveExclude = this.getEffectiveExclude(exclude);
|
|
1328
1427
|
const progress = composeProgressHandler({
|
|
1329
1428
|
onProgress,
|
|
1330
1429
|
showLogs: showLogs ?? this.showLogs,
|
|
@@ -1353,7 +1452,7 @@ export class TranscribeAPI {
|
|
|
1353
1452
|
logger: logger ?? this.logger,
|
|
1354
1453
|
uploadConcurrency,
|
|
1355
1454
|
language,
|
|
1356
|
-
exclude,
|
|
1455
|
+
exclude: effectiveExclude,
|
|
1357
1456
|
});
|
|
1358
1457
|
}
|
|
1359
1458
|
|
|
@@ -1364,8 +1463,6 @@ export class TranscribeAPI {
|
|
|
1364
1463
|
: (normalizedItem.durationEstimateSec || await estimateDurationFromFile(normalizedSingleFile));
|
|
1365
1464
|
const effectiveLanguage = normalizedItem.hasLanguage ? normalizedItem.language : normalizeLanguageCode(language);
|
|
1366
1465
|
const isAsync = files.length > 1
|
|
1367
|
-
|| Boolean(normalizedItem.url)
|
|
1368
|
-
|| Boolean(webhookUrl)
|
|
1369
1466
|
|| (
|
|
1370
1467
|
normalizedSingleFile
|
|
1371
1468
|
&& (
|
|
@@ -1383,7 +1480,19 @@ export class TranscribeAPI {
|
|
|
1383
1480
|
logger: logger ?? this.logger,
|
|
1384
1481
|
uploadConcurrency,
|
|
1385
1482
|
language,
|
|
1386
|
-
exclude,
|
|
1483
|
+
exclude: effectiveExclude,
|
|
1484
|
+
});
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
if (normalizedItem.url) {
|
|
1488
|
+
return this.transcribeUrlDirect({
|
|
1489
|
+
url: normalizedItem.url,
|
|
1490
|
+
referenceId: normalizedItem.referenceId,
|
|
1491
|
+
language: effectiveLanguage,
|
|
1492
|
+
exclude: effectiveExclude,
|
|
1493
|
+
webhookUrl,
|
|
1494
|
+
showLogs: showLogs ?? this.showLogs,
|
|
1495
|
+
logger: logger ?? this.logger,
|
|
1387
1496
|
});
|
|
1388
1497
|
}
|
|
1389
1498
|
|
|
@@ -1391,7 +1500,7 @@ export class TranscribeAPI {
|
|
|
1391
1500
|
file: normalizedSingleFile,
|
|
1392
1501
|
referenceId: normalizedItem.referenceId,
|
|
1393
1502
|
language: effectiveLanguage,
|
|
1394
|
-
exclude,
|
|
1503
|
+
exclude: effectiveExclude,
|
|
1395
1504
|
webhookUrl,
|
|
1396
1505
|
showLogs: showLogs ?? this.showLogs,
|
|
1397
1506
|
logger: logger ?? this.logger,
|
|
@@ -1440,7 +1549,10 @@ export class TranscribeAPI {
|
|
|
1440
1549
|
if ((showLogs ?? this.showLogs) && TERMINAL_JOB_STATUSES.has(String(result?.job_status || ""))) {
|
|
1441
1550
|
logTerminalAsyncResult(result, logger ?? this.logger);
|
|
1442
1551
|
}
|
|
1443
|
-
return result
|
|
1552
|
+
return this.maybeAutoReload(result, {
|
|
1553
|
+
showLogs: showLogs ?? this.showLogs,
|
|
1554
|
+
logger: logger ?? this.logger,
|
|
1555
|
+
});
|
|
1444
1556
|
}
|
|
1445
1557
|
|
|
1446
1558
|
async transcribeDirect({
|
|
@@ -1460,7 +1572,8 @@ export class TranscribeAPI {
|
|
|
1460
1572
|
if (String(referenceId || "").trim()) form.set("reference_id", String(referenceId).trim());
|
|
1461
1573
|
form.set("file", directFile, directFile.name);
|
|
1462
1574
|
if (language) form.set("language", language);
|
|
1463
|
-
|
|
1575
|
+
const excludeValue = stringifyExclude(this.getEffectiveExclude(exclude));
|
|
1576
|
+
if (excludeValue) form.set("exclude", excludeValue);
|
|
1464
1577
|
if (webhookUrl) form.set("webhook_url", webhookUrl);
|
|
1465
1578
|
|
|
1466
1579
|
const response = await fetch(`${this.baseUrl}/transcribe`, {
|
|
@@ -1479,10 +1592,51 @@ export class TranscribeAPI {
|
|
|
1479
1592
|
initialJob: result,
|
|
1480
1593
|
});
|
|
1481
1594
|
}
|
|
1595
|
+
const finalResult = await this.maybeAutoReload(result, { showLogs, logger });
|
|
1482
1596
|
if (showLogs && typeof logger?.log === "function") {
|
|
1483
|
-
logger.log(typeof
|
|
1597
|
+
logger.log(typeof finalResult === "string" ? finalResult : JSON.stringify(finalResult, null, 2));
|
|
1484
1598
|
}
|
|
1485
|
-
return
|
|
1599
|
+
return finalResult;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
async transcribeUrlDirect({
|
|
1603
|
+
url,
|
|
1604
|
+
referenceId,
|
|
1605
|
+
language,
|
|
1606
|
+
exclude,
|
|
1607
|
+
webhookUrl,
|
|
1608
|
+
showLogs = false,
|
|
1609
|
+
logger = console,
|
|
1610
|
+
} = {}) {
|
|
1611
|
+
const form = new FormData();
|
|
1612
|
+
form.set("url", String(url || "").trim());
|
|
1613
|
+
if (String(referenceId || "").trim()) form.set("reference_id", String(referenceId).trim());
|
|
1614
|
+
if (language) form.set("language", language);
|
|
1615
|
+
const excludeValue = stringifyExclude(this.getEffectiveExclude(exclude));
|
|
1616
|
+
if (excludeValue) form.set("exclude", excludeValue);
|
|
1617
|
+
if (webhookUrl) form.set("webhook_url", webhookUrl);
|
|
1618
|
+
|
|
1619
|
+
const response = await fetch(`${this.baseUrl}/transcribe`, {
|
|
1620
|
+
method: "POST",
|
|
1621
|
+
headers: {
|
|
1622
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
1623
|
+
},
|
|
1624
|
+
body: form,
|
|
1625
|
+
});
|
|
1626
|
+
const result = await parseApiResponse(response);
|
|
1627
|
+
if (this.polling && result && typeof result === "object" && result.job_status && !TERMINAL_JOB_STATUSES.has(String(result.job_status))) {
|
|
1628
|
+
return this.waitForJobCompletion(result.job_id, {
|
|
1629
|
+
polling: this.polling,
|
|
1630
|
+
showLogs,
|
|
1631
|
+
logger,
|
|
1632
|
+
initialJob: result,
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
const finalResult = await this.maybeAutoReload(result, { showLogs, logger });
|
|
1636
|
+
if (showLogs && typeof logger?.log === "function") {
|
|
1637
|
+
logger.log(typeof finalResult === "string" ? finalResult : JSON.stringify(finalResult, null, 2));
|
|
1638
|
+
}
|
|
1639
|
+
return finalResult;
|
|
1486
1640
|
}
|
|
1487
1641
|
|
|
1488
1642
|
async transcribeMany({
|
|
@@ -1602,7 +1756,7 @@ export class TranscribeAPI {
|
|
|
1602
1756
|
)),
|
|
1603
1757
|
...(normalizedLanguage ? { language: normalizedLanguage } : {}),
|
|
1604
1758
|
...(webhookUrl ? { webhook_url: webhookUrl } : {}),
|
|
1605
|
-
...(exclude ? { exclude:
|
|
1759
|
+
...(stringifyExclude(this.getEffectiveExclude(exclude)) ? { exclude: stringifyExclude(this.getEffectiveExclude(exclude)) } : {}),
|
|
1606
1760
|
},
|
|
1607
1761
|
retryable: true,
|
|
1608
1762
|
});
|