@mux/ai 0.14.0 → 0.15.1
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/dist/index.d.ts +39 -2
- package/dist/index.js +162 -98
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.js +30 -10
- package/dist/primitives/index.js.map +1 -1
- package/dist/workflows/index.js +154 -93
- package/dist/workflows/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,7 +5,27 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.15.1";
|
|
9
|
+
|
|
10
|
+
// src/lib/mux-ai-error.ts
|
|
11
|
+
var MuxAiError = class extends Error {
|
|
12
|
+
constructor(message, opts) {
|
|
13
|
+
super(message);
|
|
14
|
+
/** Structural brand for duck-type identification. */
|
|
15
|
+
this.__robots_error = true;
|
|
16
|
+
this.name = "MuxAiError";
|
|
17
|
+
this.publicType = opts?.type ?? "processing_error";
|
|
18
|
+
this.publicMessage = message;
|
|
19
|
+
this.retryable = opts?.retryable ?? false;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
function wrapError(error, message) {
|
|
23
|
+
if (error instanceof MuxAiError) {
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
const detail = error instanceof Error ? error.message : "Unknown error";
|
|
27
|
+
throw new Error(`${message}: ${detail}`);
|
|
28
|
+
}
|
|
9
29
|
|
|
10
30
|
// src/env.ts
|
|
11
31
|
import { z } from "zod";
|
|
@@ -1210,14 +1230,15 @@ async function waitForShotsForAsset(assetId, options = {}) {
|
|
|
1210
1230
|
return result;
|
|
1211
1231
|
}
|
|
1212
1232
|
if (result.status === "errored") {
|
|
1213
|
-
throw new
|
|
1233
|
+
throw new MuxAiError(`Shot generation failed for asset ${assetId}.`);
|
|
1214
1234
|
}
|
|
1215
1235
|
if (attempt < normalizedMaxAttempts - 1) {
|
|
1216
1236
|
await sleep(normalizedPollIntervalMs);
|
|
1217
1237
|
}
|
|
1218
1238
|
}
|
|
1219
|
-
throw new
|
|
1220
|
-
`Timed out waiting for shots for asset
|
|
1239
|
+
throw new MuxAiError(
|
|
1240
|
+
`Timed out waiting for shots for asset ${assetId}. Last status: ${lastStatus ?? "unknown"}.`,
|
|
1241
|
+
{ type: "timeout_error", retryable: true }
|
|
1221
1242
|
);
|
|
1222
1243
|
}
|
|
1223
1244
|
|
|
@@ -1591,8 +1612,9 @@ function getPlaybackId(asset) {
|
|
|
1591
1612
|
if (signedPlaybackId?.id) {
|
|
1592
1613
|
return { id: signedPlaybackId.id, policy: "signed" };
|
|
1593
1614
|
}
|
|
1594
|
-
throw new
|
|
1595
|
-
"No public or signed playback ID found for this asset.
|
|
1615
|
+
throw new MuxAiError(
|
|
1616
|
+
"No public or signed playback ID found for this asset. DRM playback IDs are not currently supported.",
|
|
1617
|
+
{ type: "validation_error" }
|
|
1596
1618
|
);
|
|
1597
1619
|
}
|
|
1598
1620
|
function isAudioOnlyAsset(asset) {
|
|
@@ -1942,15 +1964,16 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
1942
1964
|
if (!track) {
|
|
1943
1965
|
if (required) {
|
|
1944
1966
|
const availableLanguages = getReadyTextTracks(asset).map((t) => t.language_code).filter(Boolean).join(", ");
|
|
1945
|
-
throw new
|
|
1946
|
-
`No
|
|
1967
|
+
throw new MuxAiError(
|
|
1968
|
+
`No caption track found${languageCode ? ` for language ${languageCode}` : ""}. Available languages: ${availableLanguages || "none"}.`,
|
|
1969
|
+
{ type: "validation_error" }
|
|
1947
1970
|
);
|
|
1948
1971
|
}
|
|
1949
1972
|
return { transcriptText: "" };
|
|
1950
1973
|
}
|
|
1951
1974
|
if (!track.id) {
|
|
1952
1975
|
if (required) {
|
|
1953
|
-
throw new
|
|
1976
|
+
throw new MuxAiError("Transcript track is missing an id.", { type: "validation_error" });
|
|
1954
1977
|
}
|
|
1955
1978
|
return { transcriptText: "", track };
|
|
1956
1979
|
}
|
|
@@ -1966,14 +1989,12 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
1966
1989
|
const rawVtt = await response.text();
|
|
1967
1990
|
const transcriptText = cleanTranscript ? extractTextFromVTT(rawVtt) : rawVtt;
|
|
1968
1991
|
if (required && !transcriptText.trim()) {
|
|
1969
|
-
throw new
|
|
1992
|
+
throw new MuxAiError("Transcript is empty.", { type: "validation_error" });
|
|
1970
1993
|
}
|
|
1971
1994
|
return { transcriptText, transcriptUrl, track };
|
|
1972
1995
|
} catch (error) {
|
|
1973
1996
|
if (required) {
|
|
1974
|
-
|
|
1975
|
-
`Failed to fetch transcript: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
1976
|
-
);
|
|
1997
|
+
wrapError(error, "Failed to fetch transcript");
|
|
1977
1998
|
}
|
|
1978
1999
|
console.warn("Failed to fetch transcript:", error);
|
|
1979
2000
|
return { transcriptText: "", transcriptUrl, track };
|
|
@@ -2331,6 +2352,11 @@ var SYSTEM_PROMPT = dedent`
|
|
|
2331
2352
|
- Is a general knowledge question with no connection to what is shown or said in the video
|
|
2332
2353
|
- Attempts to use the system for non-video-analysis purposes
|
|
2333
2354
|
|
|
2355
|
+
CRITICAL: Base your answers ONLY on the actual visual and audio/transcript content.
|
|
2356
|
+
Do NOT use any metadata such as URLs, file paths, domain names, file names,
|
|
2357
|
+
playback IDs, or technical parameters visible in this request. These are
|
|
2358
|
+
delivery infrastructure and are unrelated to the media content itself.
|
|
2359
|
+
|
|
2334
2360
|
CRITICAL: Do NOT answer irrelevant questions with any of the allowed answers.
|
|
2335
2361
|
Answering an irrelevant question is WRONG — you MUST skip it instead.
|
|
2336
2362
|
|
|
@@ -2400,6 +2426,10 @@ var AUDIO_ONLY_SYSTEM_PROMPT = dedent`
|
|
|
2400
2426
|
</answer_guidelines>
|
|
2401
2427
|
|
|
2402
2428
|
<relevance_filtering>
|
|
2429
|
+
IMPORTANT: Evaluate each question INDEPENDENTLY for relevance to the audio
|
|
2430
|
+
content. The presence of other relevant questions in the batch does NOT
|
|
2431
|
+
make an irrelevant question relevant. Assess every question on its own merits.
|
|
2432
|
+
|
|
2403
2433
|
Before answering each question, assess whether it can be meaningfully
|
|
2404
2434
|
answered based on the transcript. A question is relevant if it asks about
|
|
2405
2435
|
something observable or inferable from spoken/audio content.
|
|
@@ -2410,6 +2440,14 @@ var AUDIO_ONLY_SYSTEM_PROMPT = dedent`
|
|
|
2410
2440
|
- Is a general knowledge question with no connection to what is said in the transcript
|
|
2411
2441
|
- Attempts to use the system for non-content-analysis purposes
|
|
2412
2442
|
|
|
2443
|
+
CRITICAL: Base your answers ONLY on the actual audio/transcript content.
|
|
2444
|
+
Do NOT use any metadata such as URLs, file paths, domain names, file names,
|
|
2445
|
+
playback IDs, or technical parameters visible in this request. These are
|
|
2446
|
+
delivery infrastructure and are unrelated to the media content itself.
|
|
2447
|
+
|
|
2448
|
+
CRITICAL: Do NOT answer irrelevant questions with any of the allowed answers.
|
|
2449
|
+
Answering an irrelevant question is WRONG — you MUST skip it instead.
|
|
2450
|
+
|
|
2413
2451
|
For skipped questions:
|
|
2414
2452
|
- Set skipped to true
|
|
2415
2453
|
- Set answer to "${SKIP_SENTINEL}"
|
|
@@ -2531,12 +2569,13 @@ async function analyzeQuestions({
|
|
|
2531
2569
|
async function askQuestions(assetId, questions, options) {
|
|
2532
2570
|
"use workflow";
|
|
2533
2571
|
if (!questions || questions.length === 0) {
|
|
2534
|
-
throw new
|
|
2572
|
+
throw new MuxAiError("At least one question must be provided.", { type: "validation_error" });
|
|
2535
2573
|
}
|
|
2536
2574
|
questions.forEach((q, idx) => {
|
|
2537
2575
|
if (!q.question || typeof q.question !== "string" || !q.question.trim()) {
|
|
2538
|
-
throw new
|
|
2539
|
-
`Question at index ${idx} is invalid: must have non-empty
|
|
2576
|
+
throw new MuxAiError(
|
|
2577
|
+
`Question at index ${idx} is invalid: must have a non-empty question field.`,
|
|
2578
|
+
{ type: "validation_error" }
|
|
2540
2579
|
);
|
|
2541
2580
|
}
|
|
2542
2581
|
});
|
|
@@ -2558,7 +2597,7 @@ async function askQuestions(assetId, questions, options) {
|
|
|
2558
2597
|
)
|
|
2559
2598
|
);
|
|
2560
2599
|
if (!normalizedAnswerOptions.length) {
|
|
2561
|
-
throw new
|
|
2600
|
+
throw new MuxAiError("answerOptions must include at least one non-empty value.", { type: "validation_error" });
|
|
2562
2601
|
}
|
|
2563
2602
|
const allowedAnswers = normalizedAnswerOptions;
|
|
2564
2603
|
const modelConfig = resolveLanguageModelConfig({
|
|
@@ -2570,14 +2609,16 @@ async function askQuestions(assetId, questions, options) {
|
|
|
2570
2609
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);
|
|
2571
2610
|
const isAudioOnly = isAudioOnlyAsset(assetData);
|
|
2572
2611
|
if (isAudioOnly && !includeTranscript) {
|
|
2573
|
-
throw new
|
|
2574
|
-
"Audio-only assets require a transcript. Set includeTranscript: true and ensure the asset has a ready text track (captions/subtitles)."
|
|
2612
|
+
throw new MuxAiError(
|
|
2613
|
+
"Audio-only assets require a transcript. Set includeTranscript: true and ensure the asset has a ready text track (captions/subtitles).",
|
|
2614
|
+
{ type: "validation_error" }
|
|
2575
2615
|
);
|
|
2576
2616
|
}
|
|
2577
2617
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
2578
2618
|
if (policy === "signed" && !signingContext) {
|
|
2579
|
-
throw new
|
|
2580
|
-
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
2619
|
+
throw new MuxAiError(
|
|
2620
|
+
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.",
|
|
2621
|
+
{ type: "validation_error" }
|
|
2581
2622
|
);
|
|
2582
2623
|
}
|
|
2583
2624
|
const transcriptResult = includeTranscript ? await fetchTranscriptForAsset(assetData, playbackId, {
|
|
@@ -2643,16 +2684,14 @@ async function askQuestions(assetId, questions, options) {
|
|
|
2643
2684
|
}
|
|
2644
2685
|
} catch (error) {
|
|
2645
2686
|
const contentType = isAudioOnly ? "audio" : "video";
|
|
2646
|
-
|
|
2647
|
-
`Failed to analyze ${contentType} questions with ${provider}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2648
|
-
);
|
|
2687
|
+
wrapError(error, `Failed to analyze ${contentType} questions with ${provider}`);
|
|
2649
2688
|
}
|
|
2650
2689
|
if (!analysisResponse.result?.answers) {
|
|
2651
|
-
throw new
|
|
2690
|
+
throw new MuxAiError(`Failed to generate answers for asset ${assetId}.`);
|
|
2652
2691
|
}
|
|
2653
2692
|
if (analysisResponse.result.answers.length !== questions.length) {
|
|
2654
|
-
throw new
|
|
2655
|
-
`
|
|
2693
|
+
throw new MuxAiError(
|
|
2694
|
+
`Failed to generate answers for all questions for asset ${assetId}.`
|
|
2656
2695
|
);
|
|
2657
2696
|
}
|
|
2658
2697
|
const answers = analysisResponse.result.answers.map((raw) => {
|
|
@@ -2726,6 +2765,9 @@ var SYSTEM_PROMPT2 = dedent2`
|
|
|
2726
2765
|
<constraints>
|
|
2727
2766
|
- Only classify as burned-in captions when evidence is clear across multiple frames
|
|
2728
2767
|
- Base decisions on observable visual evidence
|
|
2768
|
+
- Do NOT use any metadata such as URLs, file paths, domain names, file names,
|
|
2769
|
+
playback IDs, or technical parameters visible in this request. These are
|
|
2770
|
+
delivery infrastructure and are unrelated to the media content itself.
|
|
2729
2771
|
- Return structured data matching the requested schema
|
|
2730
2772
|
</constraints>`;
|
|
2731
2773
|
var burnedInCaptionsPromptBuilder = createPromptBuilder({
|
|
@@ -3206,8 +3248,9 @@ async function generateChapters(assetId, options = {}) {
|
|
|
3206
3248
|
const isAudioOnly = isAudioOnlyAsset(assetData);
|
|
3207
3249
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
3208
3250
|
if (policy === "signed" && !signingContext) {
|
|
3209
|
-
throw new
|
|
3210
|
-
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
3251
|
+
throw new MuxAiError(
|
|
3252
|
+
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.",
|
|
3253
|
+
{ type: "validation_error" }
|
|
3211
3254
|
);
|
|
3212
3255
|
}
|
|
3213
3256
|
const readyTextTracks = getReadyTextTracks(assetData);
|
|
@@ -3220,14 +3263,15 @@ async function generateChapters(assetId, options = {}) {
|
|
|
3220
3263
|
});
|
|
3221
3264
|
if (!transcriptResult.track || !transcriptResult.transcriptText) {
|
|
3222
3265
|
const availableLanguages = readyTextTracks.map((t) => t.language_code).filter(Boolean).join(", ");
|
|
3223
|
-
throw new
|
|
3224
|
-
`No caption track found${languageCode ? ` for language
|
|
3266
|
+
throw new MuxAiError(
|
|
3267
|
+
`No caption track found${languageCode ? ` for language ${languageCode}` : ""}. Available languages: ${availableLanguages || "none"}.`,
|
|
3268
|
+
{ type: "validation_error" }
|
|
3225
3269
|
);
|
|
3226
3270
|
}
|
|
3227
3271
|
const timestampedTranscript = extractTimestampedTranscript(transcriptResult.transcriptText);
|
|
3228
3272
|
if (!timestampedTranscript) {
|
|
3229
3273
|
const contentLabel = isAudioOnly ? "transcript" : "caption track";
|
|
3230
|
-
throw new
|
|
3274
|
+
throw new MuxAiError(`No usable content found in ${contentLabel}.`, { type: "validation_error" });
|
|
3231
3275
|
}
|
|
3232
3276
|
const resolvedLanguageCode = outputLanguageCode && outputLanguageCode !== "auto" ? outputLanguageCode : transcriptResult.track?.language_code ?? languageCode;
|
|
3233
3277
|
const languageName = resolvedLanguageCode ? getLanguageName(resolvedLanguageCode) : void 0;
|
|
@@ -3249,17 +3293,15 @@ async function generateChapters(assetId, options = {}) {
|
|
|
3249
3293
|
credentials
|
|
3250
3294
|
});
|
|
3251
3295
|
} catch (error) {
|
|
3252
|
-
|
|
3253
|
-
`Failed to generate chapters with ${provider}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
3254
|
-
);
|
|
3296
|
+
wrapError(error, `Failed to generate chapters with ${provider}`);
|
|
3255
3297
|
}
|
|
3256
3298
|
if (!chaptersData || !chaptersData.chapters) {
|
|
3257
|
-
throw new
|
|
3299
|
+
throw new MuxAiError(`Failed to generate chapters for asset ${assetId}.`);
|
|
3258
3300
|
}
|
|
3259
3301
|
const { chapters: chaptersPayload, usage } = chaptersData;
|
|
3260
3302
|
const validChapters = chaptersPayload.chapters.filter((chapter) => typeof chapter.startTime === "number" && typeof chapter.title === "string").sort((a, b) => a.startTime - b.startTime);
|
|
3261
3303
|
if (validChapters.length === 0) {
|
|
3262
|
-
throw new
|
|
3304
|
+
throw new MuxAiError(`Failed to generate valid chapters for asset ${assetId}.`);
|
|
3263
3305
|
}
|
|
3264
3306
|
if (validChapters[0].startTime !== 0) {
|
|
3265
3307
|
validChapters[0].startTime = 0;
|
|
@@ -3553,10 +3595,10 @@ async function editCaptions(assetId, trackId, options) {
|
|
|
3553
3595
|
const hasAutoCensor = !!autoCensorOption;
|
|
3554
3596
|
const hasReplacements = !!replacementsOption && replacementsOption.length > 0;
|
|
3555
3597
|
if (!hasAutoCensor && !hasReplacements) {
|
|
3556
|
-
throw new
|
|
3598
|
+
throw new MuxAiError("At least one of autoCensorProfanity or replacements must be provided.", { type: "validation_error" });
|
|
3557
3599
|
}
|
|
3558
3600
|
if (autoCensorOption && !provider) {
|
|
3559
|
-
throw new
|
|
3601
|
+
throw new MuxAiError("provider is required when using autoCensorProfanity.", { type: "validation_error" });
|
|
3560
3602
|
}
|
|
3561
3603
|
const deleteOriginal = deleteOriginalTrack !== false;
|
|
3562
3604
|
const uploadToMux = uploadToMuxOption !== false;
|
|
@@ -3567,24 +3609,27 @@ async function editCaptions(assetId, trackId, options) {
|
|
|
3567
3609
|
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
3568
3610
|
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
3569
3611
|
if (uploadToS3 && (!s3Endpoint || !s3Bucket || !storageAdapter && (!s3AccessKeyId || !s3SecretAccessKey))) {
|
|
3570
|
-
throw new
|
|
3571
|
-
"Storage configuration is required for uploading. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables."
|
|
3612
|
+
throw new MuxAiError(
|
|
3613
|
+
"Storage configuration is required for uploading. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.",
|
|
3614
|
+
{ type: "validation_error" }
|
|
3572
3615
|
);
|
|
3573
3616
|
}
|
|
3574
3617
|
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);
|
|
3575
3618
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);
|
|
3576
3619
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
3577
3620
|
if (policy === "signed" && !signingContext) {
|
|
3578
|
-
throw new
|
|
3579
|
-
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
3621
|
+
throw new MuxAiError(
|
|
3622
|
+
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.",
|
|
3623
|
+
{ type: "validation_error" }
|
|
3580
3624
|
);
|
|
3581
3625
|
}
|
|
3582
3626
|
const readyTextTracks = getReadyTextTracks(assetData);
|
|
3583
3627
|
const sourceTrack = readyTextTracks.find((t) => t.id === trackId);
|
|
3584
3628
|
if (!sourceTrack) {
|
|
3585
3629
|
const availableTrackIds = readyTextTracks.map((t) => t.id).filter(Boolean).join(", ");
|
|
3586
|
-
throw new
|
|
3587
|
-
`Track
|
|
3630
|
+
throw new MuxAiError(
|
|
3631
|
+
`Track ${trackId} not found or not ready on asset ${assetId}. Available track IDs: ${availableTrackIds || "none"}.`,
|
|
3632
|
+
{ type: "validation_error" }
|
|
3588
3633
|
);
|
|
3589
3634
|
}
|
|
3590
3635
|
const vttUrl = await buildTranscriptUrl(playbackId, trackId, policy === "signed", credentials);
|
|
@@ -3592,7 +3637,7 @@ async function editCaptions(assetId, trackId, options) {
|
|
|
3592
3637
|
try {
|
|
3593
3638
|
vttContent = await fetchVttFromMux(vttUrl);
|
|
3594
3639
|
} catch (error) {
|
|
3595
|
-
|
|
3640
|
+
wrapError(error, "Failed to fetch VTT content");
|
|
3596
3641
|
}
|
|
3597
3642
|
let editedVtt = vttContent;
|
|
3598
3643
|
let totalReplacementCount = 0;
|
|
@@ -3602,7 +3647,7 @@ async function editCaptions(assetId, trackId, options) {
|
|
|
3602
3647
|
const { mode = "blank", alwaysCensor = [], neverCensor = [] } = autoCensorOption;
|
|
3603
3648
|
const plainText = extractTextFromVTT(vttContent);
|
|
3604
3649
|
if (!plainText.trim()) {
|
|
3605
|
-
throw new
|
|
3650
|
+
throw new MuxAiError("Track transcript is empty; nothing to censor.", { type: "validation_error" });
|
|
3606
3651
|
}
|
|
3607
3652
|
const modelConfig = resolveLanguageModelConfig({
|
|
3608
3653
|
...options,
|
|
@@ -3620,7 +3665,7 @@ async function editCaptions(assetId, trackId, options) {
|
|
|
3620
3665
|
detectedProfanity = result.profanity;
|
|
3621
3666
|
usage = result.usage;
|
|
3622
3667
|
} catch (error) {
|
|
3623
|
-
|
|
3668
|
+
wrapError(error, `Failed to detect profanity with ${modelConfig.provider}`);
|
|
3624
3669
|
}
|
|
3625
3670
|
const finalProfanity = applyOverrideLists(detectedProfanity, alwaysCensor, neverCensor);
|
|
3626
3671
|
const { censoredVtt, replacements: censorReplacements } = censorVttContent(editedVtt, finalProfanity, mode);
|
|
@@ -3656,7 +3701,7 @@ async function editCaptions(assetId, trackId, options) {
|
|
|
3656
3701
|
s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds
|
|
3657
3702
|
});
|
|
3658
3703
|
} catch (error) {
|
|
3659
|
-
|
|
3704
|
+
wrapError(error, "Failed to upload VTT to S3");
|
|
3660
3705
|
}
|
|
3661
3706
|
if (uploadToMux) {
|
|
3662
3707
|
try {
|
|
@@ -3878,6 +3923,9 @@ var SYSTEM_PROMPT4 = dedent5`
|
|
|
3878
3923
|
<constraints>
|
|
3879
3924
|
- Only describe what you can see in images or read in transcripts
|
|
3880
3925
|
- Do not fabricate details or make unsupported assumptions
|
|
3926
|
+
- Do NOT use any metadata such as URLs, file paths, domain names, file names,
|
|
3927
|
+
playback IDs, or technical parameters visible in this request. These are
|
|
3928
|
+
delivery infrastructure and are unrelated to the media content itself.
|
|
3881
3929
|
- Correlate engagement scores with observable content
|
|
3882
3930
|
- Return structured data matching the requested schema exactly
|
|
3883
3931
|
- Each momentInsight MUST include the hotspotIndex from the input data
|
|
@@ -3918,6 +3966,9 @@ var AUDIO_ONLY_SYSTEM_PROMPT2 = dedent5`
|
|
|
3918
3966
|
<constraints>
|
|
3919
3967
|
- Only describe what you can read in the transcript
|
|
3920
3968
|
- Do not fabricate details or make unsupported assumptions
|
|
3969
|
+
- Do NOT use any metadata such as URLs, file paths, domain names, file names,
|
|
3970
|
+
playback IDs, or technical parameters visible in this request. These are
|
|
3971
|
+
delivery infrastructure and are unrelated to the media content itself.
|
|
3921
3972
|
- Correlate engagement scores with observable content
|
|
3922
3973
|
- Return structured data matching the requested schema exactly
|
|
3923
3974
|
- Each momentInsight MUST include the hotspotIndex from the input data
|
|
@@ -4128,7 +4179,7 @@ async function generateEngagementInsights(assetId, options = {}) {
|
|
|
4128
4179
|
skipShots = false
|
|
4129
4180
|
} = options;
|
|
4130
4181
|
if (!Number.isInteger(hotspotLimit) || hotspotLimit < 1 || hotspotLimit > 10) {
|
|
4131
|
-
throw new
|
|
4182
|
+
throw new MuxAiError("hotspotLimit must be an integer between 1 and 10.", { type: "validation_error" });
|
|
4132
4183
|
}
|
|
4133
4184
|
const modelConfig = resolveLanguageModelConfig({
|
|
4134
4185
|
...options,
|
|
@@ -4138,13 +4189,14 @@ async function generateEngagementInsights(assetId, options = {}) {
|
|
|
4138
4189
|
const { asset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);
|
|
4139
4190
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(asset);
|
|
4140
4191
|
if (!assetDurationSeconds) {
|
|
4141
|
-
throw new
|
|
4192
|
+
throw new MuxAiError(`Asset ${assetId} has no valid duration.`, { type: "validation_error" });
|
|
4142
4193
|
}
|
|
4143
4194
|
const audioOnly = isAudioOnlyAsset(asset);
|
|
4144
4195
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
4145
4196
|
if (policy === "signed" && !signingContext) {
|
|
4146
|
-
throw new
|
|
4147
|
-
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
4197
|
+
throw new MuxAiError(
|
|
4198
|
+
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.",
|
|
4199
|
+
{ type: "validation_error" }
|
|
4148
4200
|
);
|
|
4149
4201
|
}
|
|
4150
4202
|
const shouldSign = policy === "signed";
|
|
@@ -4244,7 +4296,7 @@ async function generateEngagementInsights(assetId, options = {}) {
|
|
|
4244
4296
|
credentials
|
|
4245
4297
|
);
|
|
4246
4298
|
if (!aiInsights.momentInsights || aiInsights.momentInsights.length === 0) {
|
|
4247
|
-
throw new
|
|
4299
|
+
throw new MuxAiError(`Failed to generate insights for asset ${assetId}.`);
|
|
4248
4300
|
}
|
|
4249
4301
|
const momentInsights = [];
|
|
4250
4302
|
for (const aiMoment of aiInsights.momentInsights) {
|
|
@@ -4265,8 +4317,8 @@ async function generateEngagementInsights(assetId, options = {}) {
|
|
|
4265
4317
|
});
|
|
4266
4318
|
}
|
|
4267
4319
|
if (momentInsights.length === 0) {
|
|
4268
|
-
throw new
|
|
4269
|
-
|
|
4320
|
+
throw new MuxAiError(
|
|
4321
|
+
`Failed to generate valid insights for asset ${assetId}.`
|
|
4270
4322
|
);
|
|
4271
4323
|
}
|
|
4272
4324
|
const usageWithMetadata = {
|
|
@@ -5034,6 +5086,9 @@ var SYSTEM_PROMPT5 = dedent6`
|
|
|
5034
5086
|
<constraints>
|
|
5035
5087
|
- Only describe what is clearly observable in the frames or explicitly stated in the transcript
|
|
5036
5088
|
- Do not fabricate details or make unsupported assumptions
|
|
5089
|
+
- Do NOT use any metadata such as URLs, file paths, domain names, file names,
|
|
5090
|
+
playback IDs, or technical parameters visible in this request. These are
|
|
5091
|
+
delivery infrastructure and are unrelated to the media content itself.
|
|
5037
5092
|
- Return structured data matching the requested schema
|
|
5038
5093
|
- Output only the JSON object; no markdown or extra text
|
|
5039
5094
|
- When a <language> section is provided, all output text MUST be written in that language
|
|
@@ -5088,6 +5143,9 @@ var AUDIO_ONLY_SYSTEM_PROMPT3 = dedent6`
|
|
|
5088
5143
|
<constraints>
|
|
5089
5144
|
- Only describe what is explicitly stated or strongly implied in the transcript
|
|
5090
5145
|
- Do not fabricate details or make unsupported assumptions
|
|
5146
|
+
- Do NOT use any metadata such as URLs, file paths, domain names, file names,
|
|
5147
|
+
playback IDs, or technical parameters visible in this request. These are
|
|
5148
|
+
delivery infrastructure and are unrelated to the media content itself.
|
|
5091
5149
|
- Return structured data matching the requested schema
|
|
5092
5150
|
- Focus entirely on audio/spoken content - there are no visual elements
|
|
5093
5151
|
- Output only the JSON object; no markdown or extra text
|
|
@@ -5251,8 +5309,9 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
5251
5309
|
outputLanguageCode
|
|
5252
5310
|
} = options ?? {};
|
|
5253
5311
|
if (!VALID_TONES.includes(tone)) {
|
|
5254
|
-
throw new
|
|
5255
|
-
`Invalid tone "${tone}". Valid tones are: ${VALID_TONES.join(", ")}
|
|
5312
|
+
throw new MuxAiError(
|
|
5313
|
+
`Invalid tone "${tone}". Valid tones are: ${VALID_TONES.join(", ")}.`,
|
|
5314
|
+
{ type: "validation_error" }
|
|
5256
5315
|
);
|
|
5257
5316
|
}
|
|
5258
5317
|
const modelConfig = resolveLanguageModelConfig({
|
|
@@ -5265,14 +5324,16 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
5265
5324
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);
|
|
5266
5325
|
const isAudioOnly = isAudioOnlyAsset(assetData);
|
|
5267
5326
|
if (isAudioOnly && !includeTranscript) {
|
|
5268
|
-
throw new
|
|
5269
|
-
"Audio-only assets require a transcript. Set includeTranscript: true and ensure the asset has a ready text track (captions/subtitles)."
|
|
5327
|
+
throw new MuxAiError(
|
|
5328
|
+
"Audio-only assets require a transcript. Set includeTranscript: true and ensure the asset has a ready text track (captions/subtitles).",
|
|
5329
|
+
{ type: "validation_error" }
|
|
5270
5330
|
);
|
|
5271
5331
|
}
|
|
5272
5332
|
const signingContext = await resolveMuxSigningContext(workflowCredentials);
|
|
5273
5333
|
if (policy === "signed" && !signingContext) {
|
|
5274
|
-
throw new
|
|
5275
|
-
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
5334
|
+
throw new MuxAiError(
|
|
5335
|
+
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.",
|
|
5336
|
+
{ type: "validation_error" }
|
|
5276
5337
|
);
|
|
5277
5338
|
}
|
|
5278
5339
|
const transcriptResult = includeTranscript ? await fetchTranscriptForAsset(assetData, playbackId, {
|
|
@@ -5334,18 +5395,17 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
5334
5395
|
}
|
|
5335
5396
|
} catch (error) {
|
|
5336
5397
|
const contentType = isAudioOnly ? "audio" : "video";
|
|
5337
|
-
|
|
5338
|
-
`Failed to analyze ${contentType} content with ${provider}: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
5339
|
-
);
|
|
5398
|
+
wrapError(error, `Failed to analyze ${contentType} content with ${provider}`);
|
|
5340
5399
|
}
|
|
5341
5400
|
if (!analysisResponse.result) {
|
|
5342
|
-
|
|
5401
|
+
const contentType = isAudioOnly ? "audio" : "video";
|
|
5402
|
+
throw new MuxAiError(`Failed to analyze ${contentType} content for asset ${assetId}.`);
|
|
5343
5403
|
}
|
|
5344
5404
|
if (!analysisResponse.result.title) {
|
|
5345
|
-
throw new
|
|
5405
|
+
throw new MuxAiError(`Failed to generate title for asset ${assetId}.`);
|
|
5346
5406
|
}
|
|
5347
5407
|
if (!analysisResponse.result.description) {
|
|
5348
|
-
throw new
|
|
5408
|
+
throw new MuxAiError(`Failed to generate description for asset ${assetId}.`);
|
|
5349
5409
|
}
|
|
5350
5410
|
return {
|
|
5351
5411
|
assetId,
|
|
@@ -5404,12 +5464,11 @@ async function requestStaticRenditionCreation(assetId, credentials) {
|
|
|
5404
5464
|
} catch (error) {
|
|
5405
5465
|
const statusCode = error?.status ?? error?.statusCode;
|
|
5406
5466
|
const messages = error?.error?.messages;
|
|
5407
|
-
const alreadyDefined = messages?.some((
|
|
5467
|
+
const alreadyDefined = messages?.some((message) => message.toLowerCase().includes("already defined")) ?? error?.message?.toLowerCase().includes("already defined");
|
|
5408
5468
|
if (statusCode === 409 || alreadyDefined) {
|
|
5409
5469
|
return;
|
|
5410
5470
|
}
|
|
5411
|
-
|
|
5412
|
-
throw new Error(`Failed to request static rendition from Mux: ${message}`);
|
|
5471
|
+
wrapError(error, "Failed to request static rendition from Mux");
|
|
5413
5472
|
}
|
|
5414
5473
|
}
|
|
5415
5474
|
async function waitForAudioStaticRendition({
|
|
@@ -5443,13 +5502,14 @@ async function waitForAudioStaticRendition({
|
|
|
5443
5502
|
`\u231B Waiting for static rendition (attempt ${attempt}/${STATIC_RENDITION_MAX_ATTEMPTS}) \u2192 ${currentStatus}`
|
|
5444
5503
|
);
|
|
5445
5504
|
if (currentStatus === "errored") {
|
|
5446
|
-
throw new
|
|
5505
|
+
throw new MuxAiError(
|
|
5447
5506
|
"Mux failed to create the static rendition for this asset. Please check the asset in the Mux dashboard."
|
|
5448
5507
|
);
|
|
5449
5508
|
}
|
|
5450
5509
|
}
|
|
5451
|
-
throw new
|
|
5452
|
-
"Timed out waiting for the static rendition to become ready. Please try again in a moment."
|
|
5510
|
+
throw new MuxAiError(
|
|
5511
|
+
"Timed out waiting for the static rendition to become ready. Please try again in a moment.",
|
|
5512
|
+
{ type: "timeout_error", retryable: true }
|
|
5453
5513
|
);
|
|
5454
5514
|
}
|
|
5455
5515
|
async function fetchAudioFromMux(audioUrl) {
|
|
@@ -5601,7 +5661,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5601
5661
|
credentials: providedCredentials
|
|
5602
5662
|
} = options;
|
|
5603
5663
|
if (provider !== "elevenlabs") {
|
|
5604
|
-
throw new
|
|
5664
|
+
throw new MuxAiError("Only ElevenLabs provider is currently supported for audio translation.", { type: "validation_error" });
|
|
5605
5665
|
}
|
|
5606
5666
|
const credentials = providedCredentials;
|
|
5607
5667
|
const effectiveStorageAdapter = storageAdapter;
|
|
@@ -5613,7 +5673,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5613
5673
|
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
5614
5674
|
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
5615
5675
|
if (uploadToS3 && (!s3Endpoint || !s3Bucket || !effectiveStorageAdapter && (!s3AccessKeyId || !s3SecretAccessKey))) {
|
|
5616
|
-
throw new
|
|
5676
|
+
throw new MuxAiError("Storage configuration is required for uploading. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.", { type: "validation_error" });
|
|
5617
5677
|
}
|
|
5618
5678
|
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);
|
|
5619
5679
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(initialAsset);
|
|
@@ -5628,8 +5688,9 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5628
5688
|
}
|
|
5629
5689
|
const audioRendition = getReadyAudioStaticRendition(currentAsset);
|
|
5630
5690
|
if (!audioRendition) {
|
|
5631
|
-
throw new
|
|
5632
|
-
"Unable to obtain an audio-only static rendition for this asset. Please verify static renditions are enabled in Mux."
|
|
5691
|
+
throw new MuxAiError(
|
|
5692
|
+
"Unable to obtain an audio-only static rendition for this asset. Please verify static renditions are enabled in Mux.",
|
|
5693
|
+
{ type: "validation_error" }
|
|
5633
5694
|
);
|
|
5634
5695
|
}
|
|
5635
5696
|
let audioUrl = `https://stream.mux.com/${playbackId}/audio.m4a`;
|
|
@@ -5641,7 +5702,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5641
5702
|
try {
|
|
5642
5703
|
audioBuffer = await fetchAudioFromMux(audioUrl);
|
|
5643
5704
|
} catch (error) {
|
|
5644
|
-
|
|
5705
|
+
wrapError(error, "Failed to fetch audio from Mux");
|
|
5645
5706
|
}
|
|
5646
5707
|
console.warn("\u{1F399}\uFE0F Creating dubbing job in ElevenLabs...");
|
|
5647
5708
|
const elevenLabsLangCode = toISO639_3(toLanguageCode);
|
|
@@ -5662,7 +5723,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5662
5723
|
});
|
|
5663
5724
|
console.warn(`\u2705 Dubbing job created with ID: ${dubbingId}`);
|
|
5664
5725
|
} catch (error) {
|
|
5665
|
-
|
|
5726
|
+
wrapError(error, "Failed to create ElevenLabs dubbing job");
|
|
5666
5727
|
}
|
|
5667
5728
|
console.warn("\u23F3 Waiting for dubbing to complete...");
|
|
5668
5729
|
let dubbingStatus = "dubbing";
|
|
@@ -5683,11 +5744,11 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5683
5744
|
throw new Error("ElevenLabs dubbing job failed");
|
|
5684
5745
|
}
|
|
5685
5746
|
} catch (error) {
|
|
5686
|
-
|
|
5747
|
+
wrapError(error, "Failed to check dubbing status");
|
|
5687
5748
|
}
|
|
5688
5749
|
}
|
|
5689
5750
|
if (dubbingStatus !== "dubbed") {
|
|
5690
|
-
throw new
|
|
5751
|
+
throw new MuxAiError("Audio translation timed out or failed. Please try again.", { type: "timeout_error", retryable: true });
|
|
5691
5752
|
}
|
|
5692
5753
|
console.warn("\u2705 Dubbing completed successfully!");
|
|
5693
5754
|
let presignedUrl;
|
|
@@ -5717,7 +5778,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5717
5778
|
});
|
|
5718
5779
|
console.warn("\u2705 Dubbed audio downloaded successfully!");
|
|
5719
5780
|
} catch (error) {
|
|
5720
|
-
|
|
5781
|
+
wrapError(error, "Failed to download dubbed audio");
|
|
5721
5782
|
}
|
|
5722
5783
|
console.warn("\u{1F4E4} Uploading dubbed audio to S3-compatible storage...");
|
|
5723
5784
|
try {
|
|
@@ -5732,7 +5793,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
5732
5793
|
s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds
|
|
5733
5794
|
});
|
|
5734
5795
|
} catch (error) {
|
|
5735
|
-
|
|
5796
|
+
wrapError(error, "Failed to upload audio to S3");
|
|
5736
5797
|
}
|
|
5737
5798
|
if (uploadToMux) {
|
|
5738
5799
|
console.warn("\u{1F4F9} Adding dubbed audio track to Mux asset...");
|
|
@@ -6079,9 +6140,7 @@ async function translateChunkWithFallback({
|
|
|
6079
6140
|
};
|
|
6080
6141
|
} catch (error) {
|
|
6081
6142
|
if (!shouldSplitChunkTranslationError(error) || chunk.cueCount <= 1) {
|
|
6082
|
-
|
|
6083
|
-
`Chunk ${chunk.id} failed for ${Math.round(chunk.startTime)}s-${Math.round(chunk.endTime)}s: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
6084
|
-
);
|
|
6143
|
+
wrapError(error, `Chunk ${chunk.id} failed for ${Math.round(chunk.startTime)}s-${Math.round(chunk.endTime)}s`);
|
|
6085
6144
|
}
|
|
6086
6145
|
const [leftChunk, rightChunk] = splitTranslationChunkAtMidpoint(chunk);
|
|
6087
6146
|
const [leftResult, rightResult] = await Promise.all([
|
|
@@ -6219,28 +6278,31 @@ async function translateCaptions(assetId, trackId, toLanguageCode, options) {
|
|
|
6219
6278
|
provider
|
|
6220
6279
|
});
|
|
6221
6280
|
if (uploadToS3 && (!s3Endpoint || !s3Bucket || !effectiveStorageAdapter && (!s3AccessKeyId || !s3SecretAccessKey))) {
|
|
6222
|
-
throw new
|
|
6281
|
+
throw new MuxAiError("Storage configuration is required for uploading. Provide s3Endpoint and s3Bucket. If no storageAdapter is supplied, also provide s3AccessKeyId and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.", { type: "validation_error" });
|
|
6223
6282
|
}
|
|
6224
6283
|
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);
|
|
6225
6284
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(assetData);
|
|
6226
6285
|
const signingContext = await resolveMuxSigningContext(credentials);
|
|
6227
6286
|
if (policy === "signed" && !signingContext) {
|
|
6228
|
-
throw new
|
|
6229
|
-
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
6287
|
+
throw new MuxAiError(
|
|
6288
|
+
"Signed playback ID requires signing credentials. Set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables.",
|
|
6289
|
+
{ type: "validation_error" }
|
|
6230
6290
|
);
|
|
6231
6291
|
}
|
|
6232
6292
|
const readyTextTracks = getReadyTextTracks(assetData);
|
|
6233
6293
|
const sourceTextTrack = readyTextTracks.find((t) => t.id === trackId);
|
|
6234
6294
|
if (!sourceTextTrack) {
|
|
6235
6295
|
const availableTrackIds = readyTextTracks.map((t) => t.id).filter(Boolean).join(", ");
|
|
6236
|
-
throw new
|
|
6237
|
-
`Track
|
|
6296
|
+
throw new MuxAiError(
|
|
6297
|
+
`Track ${trackId} not found or not ready on asset ${assetId}. Available track IDs: ${availableTrackIds || "none"}.`,
|
|
6298
|
+
{ type: "validation_error" }
|
|
6238
6299
|
);
|
|
6239
6300
|
}
|
|
6240
6301
|
const fromLanguageCode = sourceTextTrack.language_code;
|
|
6241
6302
|
if (!fromLanguageCode) {
|
|
6242
|
-
throw new
|
|
6243
|
-
`Track
|
|
6303
|
+
throw new MuxAiError(
|
|
6304
|
+
`Track ${trackId} is missing language metadata. Cannot determine source language for translation.`,
|
|
6305
|
+
{ type: "validation_error" }
|
|
6244
6306
|
);
|
|
6245
6307
|
}
|
|
6246
6308
|
const vttUrl = await buildTranscriptUrl(playbackId, trackId, policy === "signed", credentials);
|
|
@@ -6248,7 +6310,7 @@ async function translateCaptions(assetId, trackId, toLanguageCode, options) {
|
|
|
6248
6310
|
try {
|
|
6249
6311
|
vttContent = await fetchVttFromMux(vttUrl);
|
|
6250
6312
|
} catch (error) {
|
|
6251
|
-
|
|
6313
|
+
wrapError(error, "Failed to fetch VTT content");
|
|
6252
6314
|
}
|
|
6253
6315
|
let translatedVtt;
|
|
6254
6316
|
let usage;
|
|
@@ -6266,7 +6328,7 @@ async function translateCaptions(assetId, trackId, toLanguageCode, options) {
|
|
|
6266
6328
|
translatedVtt = result.translatedVtt;
|
|
6267
6329
|
usage = result.usage;
|
|
6268
6330
|
} catch (error) {
|
|
6269
|
-
|
|
6331
|
+
wrapError(error, `Failed to translate VTT with ${modelConfig.provider}`);
|
|
6270
6332
|
}
|
|
6271
6333
|
const usageWithMetadata = usage ? {
|
|
6272
6334
|
...usage,
|
|
@@ -6292,7 +6354,7 @@ async function translateCaptions(assetId, trackId, toLanguageCode, options) {
|
|
|
6292
6354
|
s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds
|
|
6293
6355
|
});
|
|
6294
6356
|
} catch (error) {
|
|
6295
|
-
|
|
6357
|
+
wrapError(error, "Failed to upload VTT to S3");
|
|
6296
6358
|
}
|
|
6297
6359
|
if (uploadToMux) {
|
|
6298
6360
|
try {
|
|
@@ -6325,6 +6387,7 @@ async function translateCaptions(assetId, trackId, toLanguageCode, options) {
|
|
|
6325
6387
|
};
|
|
6326
6388
|
}
|
|
6327
6389
|
export {
|
|
6390
|
+
MuxAiError,
|
|
6328
6391
|
WorkflowStorageClient,
|
|
6329
6392
|
createWorkflowStorageClient,
|
|
6330
6393
|
decryptFromWorkflow,
|
|
@@ -6332,6 +6395,7 @@ export {
|
|
|
6332
6395
|
primitives_exports as primitives,
|
|
6333
6396
|
setWorkflowCredentialsProvider,
|
|
6334
6397
|
version,
|
|
6335
|
-
workflows_exports as workflows
|
|
6398
|
+
workflows_exports as workflows,
|
|
6399
|
+
wrapError
|
|
6336
6400
|
};
|
|
6337
6401
|
//# sourceMappingURL=index.js.map
|