@mux/ai 0.33.0 → 0.35.0

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.
@@ -1,4 +1,4 @@
1
- export { A as ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH, j as ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH, k as ASK_QUESTIONS_MAX_QUESTION_LENGTH, l as AskQuestionsOptions, m as AskQuestionsResult, n as AskQuestionsType, o as AudioTranslationOptions, p as AudioTranslationResult, q as AutoCensorProfanityOptions, B as BurnedInCaptionsAnalysis, r as BurnedInCaptionsOptions, s as BurnedInCaptionsPromptOverrides, u as BurnedInCaptionsPromptSections, v as BurnedInCaptionsResult, C as CaptionReplacement, w as CensorMode, x as Chapter, y as ChapterSystemPromptSections, z as ChaptersOptions, D as ChaptersPromptOverrides, E as ChaptersPromptSections, F as ChaptersResult, G as ChaptersType, H as DEFAULT_DESCRIPTION_LENGTH, J as DEFAULT_SUMMARY_KEYWORD_LIMIT, K as DEFAULT_TITLE_LENGTH, M as EditCaptionsOptions, N as EditCaptionsResult, O as EmbeddingsOptions, P as EmbeddingsResult, Q as EngagementInsightsOptions, R as EngagementInsightsResult, T as GOOGLE_VISION_LIKELIHOOD_TO_SCORE, U as HIVE_SEXUAL_CATEGORIES, V as HIVE_VIOLENCE_CATEGORIES, W as HiveModerationOutput, X as HiveModerationSource, Y as ModerationOptions, Z as ModerationProvider, _ as ModerationResult, $ as MomentInsight, a0 as OverallInsight, a1 as ProfanityDetectionPayload, a2 as Question, a3 as QuestionAnswer, a4 as QuestionAnswerType, a5 as ReplacementRecord, a6 as SummarizationOptions, a7 as SummarizationPromptOverrides, a8 as SummarizationPromptSections, a9 as SummaryAndTagsResult, aa as SummaryType, ab as ThumbnailModerationScore, ac as TranslationChunkingOptions, ad as TranslationOptions, ae as TranslationPayload, af as TranslationResult, ag as aggregateTokenUsage, ah as applyOverrideLists, ai as applyReplacements, aj as askQuestions, ak as buildReplacementRegex, al as burnedInCaptionsSchema, am as censorVttContent, an as chapterSchema, ao as chaptersSchema, ap as createReplacer, aq as editCaptions, ar as extractTranscriptSegmentsForHotspots, as as generateChapters, at as generateEmbeddings, au as generateEngagementInsights, av as generateVideoEmbeddings, aw as getModerationScores, ax as getSummaryAndTags, ay as hasBurnedInCaptions, az as mapShotsToHotspots, aA as normalizeTranslatedVtt, aB as profanityDetectionSchema, aC as questionAnswerSchema, aD as shouldSplitChunkTranslationError, aE as summarySchema, aF as transformCueText, aG as translateAudio, aH as translateCaptions, aI as translationSchema, aJ as validateNeverTranslateTerms, aK as verifyNeverTranslateTerms } from '../index-Chccb380.js';
1
+ export { A as ASK_QUESTIONS_DEFAULT_MAX_ANSWER_OPTION_LENGTH, j as ASK_QUESTIONS_DEFAULT_MAX_FREE_FORM_ANSWER_LENGTH, k as ASK_QUESTIONS_MAX_QUESTION_LENGTH, l as AskQuestionsOptions, m as AskQuestionsResult, n as AskQuestionsType, o as AudioTranslationOptions, p as AudioTranslationResult, q as AutoCensorProfanityOptions, B as BurnedInCaptionsAnalysis, r as BurnedInCaptionsOptions, s as BurnedInCaptionsPromptOverrides, u as BurnedInCaptionsPromptSections, v as BurnedInCaptionsResult, C as CaptionReplacement, w as CensorMode, x as Chapter, y as ChapterSystemPromptSections, z as ChaptersOptions, D as ChaptersPromptOverrides, E as ChaptersPromptSections, F as ChaptersResult, G as ChaptersType, H as DEFAULT_DESCRIPTION_LENGTH, J as DEFAULT_SUMMARY_KEYWORD_LIMIT, K as DEFAULT_TITLE_LENGTH, M as EditCaptionsOptions, N as EditCaptionsResult, O as EmbeddingsOptions, P as EmbeddingsResult, Q as EngagementInsightsOptions, R as EngagementInsightsResult, T as GOOGLE_VISION_LIKELIHOOD_TO_SCORE, U as HIVE_SEXUAL_CATEGORIES, V as HIVE_VIOLENCE_CATEGORIES, W as HiveModerationOutput, X as HiveModerationSource, Y as ModerationOptions, Z as ModerationProvider, _ as ModerationResult, $ as MomentInsight, a0 as OverallInsight, a1 as ProfanityDetectionPayload, a2 as Question, a3 as QuestionAnswer, a4 as QuestionAnswerType, a5 as ReplacementRecord, a6 as StaticRenditionCleanupOutcome, a7 as SummarizationOptions, a8 as SummarizationPromptOverrides, a9 as SummarizationPromptSections, aa as SummaryAndTagsResult, ab as SummaryType, ac as ThumbnailModerationScore, ad as TranslationChunkingOptions, ae as TranslationOptions, af as TranslationPayload, ag as TranslationResult, ah as aggregateTokenUsage, ai as applyOverrideLists, aj as applyReplacements, ak as askQuestions, al as buildReplacementRegex, am as burnedInCaptionsSchema, an as censorVttContent, ao as chapterSchema, ap as chaptersSchema, aq as createReplacer, ar as editCaptions, as as extractTranscriptSegmentsForHotspots, at as generateChapters, au as generateEmbeddings, av as generateEngagementInsights, aw as generateVideoEmbeddings, ax as getModerationScores, ay as getSummaryAndTags, az as hasBurnedInCaptions, aA as mapShotsToHotspots, aB as normalizeTranslatedVtt, aC as profanityDetectionSchema, aD as questionAnswerSchema, aE as shouldSplitChunkTranslationError, aF as summarySchema, aG as transformCueText, aH as translateAudio, aI as translateCaptions, aJ as translationSchema, aK as validateNeverTranslateTerms, aL as verifyNeverTranslateTerms } from '../index-fU3hmKZu.js';
2
2
  import 'zod';
3
3
  import '@ai-sdk/anthropic';
4
4
  import '@ai-sdk/google';
@@ -643,6 +643,22 @@ var CONTENT_POLICY_REASONS = /* @__PURE__ */ new Set([
643
643
  "SAFETY",
644
644
  "SPII"
645
645
  ]);
646
+ var IncompleteGenerationError = class extends MuxAiError {
647
+ constructor(finishReason, rawFinishReason, usage) {
648
+ const reason = rawFinishReason ?? finishReason;
649
+ const message = finishReason === "length" ? `The model reached its output limit before producing a complete response (finish reason: ${reason}).` : `The model stopped before producing a complete response (finish reason: ${reason}).`;
650
+ super(message, {
651
+ type: "processing_error",
652
+ retryable: finishReason !== "length"
653
+ });
654
+ this.finishReason = finishReason;
655
+ this.rawFinishReason = rawFinishReason;
656
+ this.usage = usage;
657
+ }
658
+ };
659
+ function isIncompleteGenerationError(error) {
660
+ return MuxAiError.is(error) && typeof error.finishReason === "string";
661
+ }
646
662
  async function withContentPolicyErrorHandling(operation) {
647
663
  try {
648
664
  return await operation();
@@ -669,6 +685,14 @@ function getGeneratedOutputWithContentPolicyHandling(response) {
669
685
  const reason = rawReason.success && isContentPolicyReason(rawReason.data) ? rawReason.data : "CONTENT_FILTER";
670
686
  throwContentPolicyError({ reason }, getErrorTokenUsage(response));
671
687
  }
688
+ if (response.finishReason !== "stop") {
689
+ const rawReason = PolicyTokenSchema.safeParse(response.rawFinishReason);
690
+ throw new IncompleteGenerationError(
691
+ response.finishReason,
692
+ rawReason.success ? rawReason.data : void 0,
693
+ getErrorTokenUsage(response)
694
+ );
695
+ }
672
696
  return response.output;
673
697
  }
674
698
  function throwContentPolicyError(block, usage) {
@@ -6914,19 +6938,41 @@ async function requestStaticRenditionCreation(assetId, credentials) {
6914
6938
  const muxClient = await resolveMuxClient(credentials);
6915
6939
  const mux = await muxClient.createClient();
6916
6940
  try {
6917
- await mux.video.assets.createStaticRendition(assetId, {
6941
+ const rendition = await mux.video.assets.createStaticRendition(assetId, {
6918
6942
  resolution: "audio-only"
6919
6943
  });
6944
+ return rendition.id;
6920
6945
  } catch (error) {
6921
6946
  const statusCode = error?.status ?? error?.statusCode;
6922
6947
  const messages = error?.error?.messages;
6923
6948
  const alreadyDefined = messages?.some((message) => message.toLowerCase().includes("already defined")) ?? error?.message?.toLowerCase().includes("already defined");
6924
6949
  if (statusCode === 409 || alreadyDefined) {
6925
- return;
6950
+ return void 0;
6926
6951
  }
6927
6952
  wrapError(error, "Failed to request static rendition from Mux");
6928
6953
  }
6929
6954
  }
6955
+ async function deleteCreatedStaticRendition(assetId, staticRenditionId, credentials) {
6956
+ "use step";
6957
+ try {
6958
+ const muxClient = await resolveMuxClient(credentials);
6959
+ const mux = await muxClient.createClient();
6960
+ await mux.video.assets.deleteStaticRendition(assetId, staticRenditionId);
6961
+ console.warn(`\u{1F9F9} Deleted static rendition ${staticRenditionId} created on asset ${assetId}`);
6962
+ return "deleted";
6963
+ } catch (error) {
6964
+ const statusCode = error?.status ?? error?.statusCode;
6965
+ if (statusCode === 404) {
6966
+ console.warn(`\u{1F9F9} Static rendition ${staticRenditionId} on asset ${assetId} was already deleted`);
6967
+ return "deleted";
6968
+ }
6969
+ const message = error instanceof Error ? error.message : String(error);
6970
+ console.warn(
6971
+ `\u26A0\uFE0F Failed to delete static rendition ${staticRenditionId} on asset ${assetId} (status: ${statusCode ?? "unknown"}): ${message}`
6972
+ );
6973
+ return "delete_failed";
6974
+ }
6975
+ }
6930
6976
  async function retrieveAsset(assetId, credentials) {
6931
6977
  "use step";
6932
6978
  const muxClient = await resolveMuxClient(credentials);
@@ -6942,14 +6988,6 @@ async function waitForAudioStaticRendition({
6942
6988
  if (hasReadyAudioStaticRendition(currentAsset)) {
6943
6989
  return currentAsset;
6944
6990
  }
6945
- const status = currentAsset.static_renditions?.status ?? "not_requested";
6946
- if (status === "not_requested" || status === void 0) {
6947
- await requestStaticRenditionCreation(assetId, credentials);
6948
- } else if (status === "errored") {
6949
- await requestStaticRenditionCreation(assetId, credentials);
6950
- } else {
6951
- console.warn(`\u2139\uFE0F Static rendition already ${status}. Waiting for it to finish...`);
6952
- }
6953
6991
  for (let attempt = 1; attempt <= STATIC_RENDITION_MAX_ATTEMPTS; attempt++) {
6954
6992
  await sleep(STATIC_RENDITION_POLL_INTERVAL_MS);
6955
6993
  currentAsset = await retrieveAsset(assetId, credentials);
@@ -7156,6 +7194,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
7156
7194
  uploadToS3: uploadToS3Option,
7157
7195
  uploadToMux: uploadToMuxOption,
7158
7196
  uploadCaptionsToMux = false,
7197
+ staticRenditionCleanup: staticRenditionCleanupOption = "delete",
7159
7198
  storageAdapter,
7160
7199
  credentials: providedCredentials
7161
7200
  } = options;
@@ -7177,144 +7216,167 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
7177
7216
  const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials);
7178
7217
  const assetDurationSeconds = getAssetDurationSecondsFromAsset(initialAsset);
7179
7218
  let currentAsset = initialAsset;
7219
+ let createdStaticRenditionId;
7180
7220
  if (!hasReadyAudioStaticRendition(currentAsset)) {
7181
7221
  console.warn("\u274C No ready audio static rendition found. Requesting one now...");
7182
- currentAsset = await waitForAudioStaticRendition({
7183
- assetId,
7184
- initialAsset: currentAsset,
7185
- credentials
7186
- });
7187
- }
7188
- const audioRendition = getReadyAudioStaticRendition(currentAsset);
7189
- if (!audioRendition) {
7190
- throw new MuxAiError(
7191
- "Unable to obtain an audio-only static rendition for this asset. Please verify static renditions are enabled in Mux.",
7192
- { type: "validation_error" }
7193
- );
7194
- }
7195
- let audioUrl = `${getMuxStreamOrigin()}/${playbackId}/audio.m4a`;
7196
- if (policy === "signed") {
7197
- audioUrl = await signUrl(audioUrl, playbackId, "video", void 0, credentials);
7222
+ const renditionStatus = currentAsset.static_renditions?.status ?? "not_requested";
7223
+ if (renditionStatus === "not_requested" || renditionStatus === "errored") {
7224
+ createdStaticRenditionId = await requestStaticRenditionCreation(assetId, credentials);
7225
+ } else {
7226
+ console.warn(`\u2139\uFE0F Static rendition already ${renditionStatus}. Waiting for it to finish...`);
7227
+ }
7198
7228
  }
7199
- console.warn("\u{1F399}\uFE0F Creating dubbing job in ElevenLabs...");
7200
- const elevenLabsLangCode = toISO639_3(toLanguageCode);
7201
- const normalizedFromLanguageCode = fromLanguageCode?.trim();
7202
- const elevenLabsSourceLangCode = normalizedFromLanguageCode ? toISO639_3(normalizedFromLanguageCode) : void 0;
7203
- console.warn(
7204
- `\u{1F50D} Creating dubbing job for asset ${assetId}: ${elevenLabsSourceLangCode ?? "auto"} -> ${elevenLabsLangCode}`
7205
- );
7229
+ let staticRenditionCleanup = "not_created";
7206
7230
  let dubbingId;
7231
+ let presignedUrl;
7232
+ let uploadedTrackId;
7233
+ let captionsPresignedUrl;
7234
+ let captionsTrackId;
7207
7235
  try {
7208
- dubbingId = await createElevenLabsDubbingJob({
7209
- sourceUrl: audioUrl,
7210
- assetId,
7211
- elevenLabsLangCode,
7212
- elevenLabsSourceLangCode,
7213
- numSpeakers,
7214
- credentials
7215
- });
7216
- console.warn(`\u2705 Dubbing job created with ID: ${dubbingId}`);
7217
- } catch (error) {
7218
- wrapError(error, "Failed to create ElevenLabs dubbing job");
7219
- }
7220
- console.warn("\u23F3 Waiting for dubbing to complete...");
7221
- const dubbingPollTimeoutSeconds = options.dubbingPollTimeoutSeconds ?? DEFAULT_DUBBING_POLL_TIMEOUT_SECONDS;
7222
- const maxPollAttempts = Math.max(1, Math.ceil(dubbingPollTimeoutSeconds * 1e3 / DUBBING_POLL_INTERVAL_MS));
7223
- let dubbingStatus = "dubbing";
7224
- let pollAttempts = 0;
7225
- let targetLanguages = [];
7226
- while (dubbingStatus !== "dubbed" && pollAttempts < maxPollAttempts) {
7227
- await sleep(DUBBING_POLL_INTERVAL_MS);
7228
- pollAttempts++;
7229
- try {
7230
- const statusResult = await checkElevenLabsDubbingStatus({
7231
- dubbingId,
7236
+ if (!hasReadyAudioStaticRendition(currentAsset)) {
7237
+ currentAsset = await waitForAudioStaticRendition({
7238
+ assetId,
7239
+ initialAsset: currentAsset,
7232
7240
  credentials
7233
7241
  });
7234
- dubbingStatus = statusResult.status;
7235
- targetLanguages = statusResult.targetLanguages;
7236
- } catch (error) {
7237
- wrapError(error, "Failed to check dubbing status");
7238
7242
  }
7239
- if (dubbingStatus === "failed") {
7240
- throw new MuxAiError("ElevenLabs reported that the dubbing job failed.", { type: "processing_error" });
7243
+ const audioRendition = getReadyAudioStaticRendition(currentAsset);
7244
+ if (!audioRendition) {
7245
+ throw new MuxAiError(
7246
+ "Unable to obtain an audio-only static rendition for this asset. Please verify static renditions are enabled in Mux.",
7247
+ { type: "validation_error" }
7248
+ );
7241
7249
  }
7242
- }
7243
- if (dubbingStatus !== "dubbed") {
7244
- throw new MuxAiError("Audio translation timed out or failed. Please try again.", { type: "timeout_error", retryable: true });
7245
- }
7246
- console.warn("\u2705 Dubbing completed successfully!");
7247
- let presignedUrl;
7248
- let uploadedTrackId;
7249
- let captionsPresignedUrl;
7250
- let captionsTrackId;
7251
- if (uploadToS3) {
7252
- console.warn("\u{1F4E5} Downloading dubbed audio from ElevenLabs and staging to S3...");
7253
- const downloadLangCode = targetLanguages[0] ?? toISO639_1(toLanguageCode);
7250
+ let audioUrl = `${getMuxStreamOrigin()}/${playbackId}/audio.m4a`;
7251
+ if (policy === "signed") {
7252
+ audioUrl = await signUrl(audioUrl, playbackId, "video", void 0, credentials);
7253
+ }
7254
+ console.warn("\u{1F399}\uFE0F Creating dubbing job in ElevenLabs...");
7255
+ const elevenLabsLangCode = toISO639_3(toLanguageCode);
7256
+ const normalizedFromLanguageCode = fromLanguageCode?.trim();
7257
+ const elevenLabsSourceLangCode = normalizedFromLanguageCode ? toISO639_3(normalizedFromLanguageCode) : void 0;
7258
+ console.warn(
7259
+ `\u{1F50D} Creating dubbing job for asset ${assetId}: ${elevenLabsSourceLangCode ?? "auto"} -> ${elevenLabsLangCode}`
7260
+ );
7254
7261
  try {
7255
- presignedUrl = await downloadAndUploadDubbedAudio({
7256
- dubbingId,
7257
- languageCode: downloadLangCode,
7262
+ dubbingId = await createElevenLabsDubbingJob({
7263
+ sourceUrl: audioUrl,
7258
7264
  assetId,
7259
- toLanguageCode,
7260
- s3Endpoint,
7261
- s3Region,
7262
- s3Bucket,
7263
- storageAdapter: effectiveStorageAdapter,
7264
- s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds,
7265
+ elevenLabsLangCode,
7266
+ elevenLabsSourceLangCode,
7267
+ numSpeakers,
7265
7268
  credentials
7266
7269
  });
7267
- console.warn("\u2705 Dubbed audio staged to S3 successfully!");
7270
+ console.warn(`\u2705 Dubbing job created with ID: ${dubbingId}`);
7268
7271
  } catch (error) {
7269
- wrapError(error, "Failed to download and upload dubbed audio");
7270
- }
7271
- if (uploadToMux) {
7272
- console.warn("\u{1F4F9} Adding dubbed audio track to Mux asset...");
7273
- const muxLangCode = toISO639_1(toLanguageCode);
7272
+ wrapError(error, "Failed to create ElevenLabs dubbing job");
7273
+ }
7274
+ console.warn("\u23F3 Waiting for dubbing to complete...");
7275
+ const dubbingPollTimeoutSeconds = options.dubbingPollTimeoutSeconds ?? DEFAULT_DUBBING_POLL_TIMEOUT_SECONDS;
7276
+ const maxPollAttempts = Math.max(1, Math.ceil(dubbingPollTimeoutSeconds * 1e3 / DUBBING_POLL_INTERVAL_MS));
7277
+ let dubbingStatus = "dubbing";
7278
+ let pollAttempts = 0;
7279
+ let targetLanguages = [];
7280
+ while (dubbingStatus !== "dubbed" && pollAttempts < maxPollAttempts) {
7281
+ await sleep(DUBBING_POLL_INTERVAL_MS);
7282
+ pollAttempts++;
7274
7283
  try {
7275
- uploadedTrackId = await createAudioTrackOnMux(assetId, muxLangCode, presignedUrl, credentials);
7276
- const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
7277
- const trackName = `${languageName} (auto-dubbed)`;
7278
- console.warn(`\u2705 Track added to Mux asset with ID: ${uploadedTrackId}`);
7279
- console.warn(`\u{1F4CB} Track name: "${trackName}"`);
7284
+ const statusResult = await checkElevenLabsDubbingStatus({
7285
+ dubbingId,
7286
+ credentials
7287
+ });
7288
+ dubbingStatus = statusResult.status;
7289
+ targetLanguages = statusResult.targetLanguages;
7280
7290
  } catch (error) {
7281
- console.warn(`\u26A0\uFE0F Failed to add audio track to Mux asset: ${error instanceof Error ? error.message : "Unknown error"}`);
7282
- console.warn("\u{1F517} You can manually add the track using this presigned URL:");
7283
- console.warn(presignedUrl);
7291
+ wrapError(error, "Failed to check dubbing status");
7292
+ }
7293
+ if (dubbingStatus === "failed") {
7294
+ throw new MuxAiError("ElevenLabs reported that the dubbing job failed.", { type: "processing_error" });
7284
7295
  }
7285
7296
  }
7286
- try {
7287
- console.warn("\u{1F4E5} Downloading dub transcript from ElevenLabs and staging to S3...");
7288
- captionsPresignedUrl = await downloadAndUploadDubTranscript({
7289
- dubbingId,
7290
- languageCode: downloadLangCode,
7291
- assetId,
7292
- toLanguageCode,
7293
- s3Endpoint,
7294
- s3Region,
7295
- s3Bucket,
7296
- storageAdapter: effectiveStorageAdapter,
7297
- s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds,
7298
- credentials
7299
- });
7300
- if (uploadCaptionsToMux) {
7301
- console.warn("\u{1F4F9} Adding dubbed captions track to Mux asset...");
7297
+ if (dubbingStatus !== "dubbed") {
7298
+ throw new MuxAiError("Audio translation timed out or failed. Please try again.", { type: "timeout_error", retryable: true });
7299
+ }
7300
+ console.warn("\u2705 Dubbing completed successfully!");
7301
+ if (uploadToS3) {
7302
+ console.warn("\u{1F4E5} Downloading dubbed audio from ElevenLabs and staging to S3...");
7303
+ const downloadLangCode = targetLanguages[0] ?? toISO639_1(toLanguageCode);
7304
+ try {
7305
+ presignedUrl = await downloadAndUploadDubbedAudio({
7306
+ dubbingId,
7307
+ languageCode: downloadLangCode,
7308
+ assetId,
7309
+ toLanguageCode,
7310
+ s3Endpoint,
7311
+ s3Region,
7312
+ s3Bucket,
7313
+ storageAdapter: effectiveStorageAdapter,
7314
+ s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds,
7315
+ credentials
7316
+ });
7317
+ console.warn("\u2705 Dubbed audio staged to S3 successfully!");
7318
+ } catch (error) {
7319
+ wrapError(error, "Failed to download and upload dubbed audio");
7320
+ }
7321
+ if (uploadToMux) {
7322
+ console.warn("\u{1F4F9} Adding dubbed audio track to Mux asset...");
7302
7323
  const muxLangCode = toISO639_1(toLanguageCode);
7303
- const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
7304
- captionsTrackId = await createTextTrackOnMux(
7324
+ try {
7325
+ uploadedTrackId = await createAudioTrackOnMux(assetId, muxLangCode, presignedUrl, credentials);
7326
+ const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
7327
+ const trackName = `${languageName} (auto-dubbed)`;
7328
+ console.warn(`\u2705 Track added to Mux asset with ID: ${uploadedTrackId}`);
7329
+ console.warn(`\u{1F4CB} Track name: "${trackName}"`);
7330
+ } catch (error) {
7331
+ console.warn(`\u26A0\uFE0F Failed to add audio track to Mux asset: ${error instanceof Error ? error.message : "Unknown error"}`);
7332
+ console.warn("\u{1F517} You can manually add the track using this presigned URL:");
7333
+ console.warn(presignedUrl);
7334
+ }
7335
+ }
7336
+ try {
7337
+ console.warn("\u{1F4E5} Downloading dub transcript from ElevenLabs and staging to S3...");
7338
+ captionsPresignedUrl = await downloadAndUploadDubTranscript({
7339
+ dubbingId,
7340
+ languageCode: downloadLangCode,
7305
7341
  assetId,
7306
- muxLangCode,
7307
- `${languageName} (auto-dubbed)`,
7308
- captionsPresignedUrl,
7342
+ toLanguageCode,
7343
+ s3Endpoint,
7344
+ s3Region,
7345
+ s3Bucket,
7346
+ storageAdapter: effectiveStorageAdapter,
7347
+ s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds,
7309
7348
  credentials
7310
- );
7311
- console.warn(`\u2705 Captions track added to Mux asset with ID: ${captionsTrackId}`);
7349
+ });
7350
+ if (uploadCaptionsToMux) {
7351
+ console.warn("\u{1F4F9} Adding dubbed captions track to Mux asset...");
7352
+ const muxLangCode = toISO639_1(toLanguageCode);
7353
+ const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
7354
+ captionsTrackId = await createTextTrackOnMux(
7355
+ assetId,
7356
+ muxLangCode,
7357
+ `${languageName} (auto-dubbed)`,
7358
+ captionsPresignedUrl,
7359
+ credentials
7360
+ );
7361
+ console.warn(`\u2705 Captions track added to Mux asset with ID: ${captionsTrackId}`);
7362
+ }
7363
+ } catch (error) {
7364
+ console.warn(`\u26A0\uFE0F Failed to attach dubbed captions: ${error instanceof Error ? error.message : "Unknown error"}`);
7365
+ if (captionsPresignedUrl) {
7366
+ console.warn("\u{1F517} You can manually add the captions track using this presigned URL:");
7367
+ console.warn(captionsPresignedUrl);
7368
+ }
7312
7369
  }
7313
- } catch (error) {
7314
- console.warn(`\u26A0\uFE0F Failed to attach dubbed captions: ${error instanceof Error ? error.message : "Unknown error"}`);
7315
- if (captionsPresignedUrl) {
7316
- console.warn("\u{1F517} You can manually add the captions track using this presigned URL:");
7317
- console.warn(captionsPresignedUrl);
7370
+ }
7371
+ } finally {
7372
+ if (createdStaticRenditionId) {
7373
+ if (staticRenditionCleanupOption === "keep") {
7374
+ console.warn(
7375
+ `\u2139\uFE0F Keeping static rendition ${createdStaticRenditionId} on asset ${assetId} (staticRenditionCleanup: "keep")`
7376
+ );
7377
+ staticRenditionCleanup = "kept";
7378
+ } else {
7379
+ staticRenditionCleanup = await deleteCreatedStaticRendition(assetId, createdStaticRenditionId, credentials);
7318
7380
  }
7319
7381
  }
7320
7382
  }
@@ -7328,6 +7390,8 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
7328
7390
  presignedUrl,
7329
7391
  captionsTrackId,
7330
7392
  captionsPresignedUrl,
7393
+ createdStaticRenditionId,
7394
+ staticRenditionCleanup,
7331
7395
  usage: {
7332
7396
  metadata: {
7333
7397
  assetDurationSeconds
@@ -7341,6 +7405,7 @@ import {
7341
7405
  APICallError as APICallError3,
7342
7406
  generateText as generateText7,
7343
7407
  NoObjectGeneratedError as NoObjectGeneratedError2,
7408
+ NoOutputGeneratedError as NoOutputGeneratedError2,
7344
7409
  Output as Output7,
7345
7410
  RetryError as RetryError2,
7346
7411
  TypeValidationError
@@ -7504,7 +7569,7 @@ function shouldSplitChunkTranslationError(error) {
7504
7569
  if (isProviderServiceError(error)) {
7505
7570
  return false;
7506
7571
  }
7507
- return NoObjectGeneratedError2.isInstance(error) || TypeValidationError.isInstance(error) || isTranslationChunkValidationError(error);
7572
+ return NoObjectGeneratedError2.isInstance(error) || NoOutputGeneratedError2.isInstance(error) || TypeValidationError.isInstance(error) || isIncompleteGenerationError(error) || isTranslationChunkValidationError(error);
7508
7573
  }
7509
7574
  function resolveTranslationChunkingOptions(options) {
7510
7575
  const targetChunkDurationSeconds = Math.max(
@@ -7601,9 +7666,13 @@ function buildTranslationChunkRequests(vttContent, assetDurationSeconds, chunkin
7601
7666
  if (typeof assetDurationSeconds !== "number" || assetDurationSeconds < resolvedChunking.minimumAssetDurationSeconds) {
7602
7667
  return {
7603
7668
  preamble,
7604
- chunks: [
7605
- createTranslationChunkRequest("chunk-0", cues, cueBlocks)
7606
- ]
7669
+ chunks: splitTranslationChunkRequestByBudget(
7670
+ "chunk-0",
7671
+ cues,
7672
+ cueBlocks,
7673
+ resolvedChunking.maxCuesPerChunk,
7674
+ resolvedChunking.maxCueTextTokensPerChunk
7675
+ )
7607
7676
  };
7608
7677
  }
7609
7678
  const targetChunkDurationSeconds = resolvedChunking.targetChunkDurationSeconds;
@@ -7864,7 +7933,11 @@ async function translateChunkWithFallback({
7864
7933
  );
7865
7934
  return {
7866
7935
  translatedVtt: concatenateVttSegments([leftResult.translatedVtt, rightResult.translatedVtt]),
7867
- usage: aggregateTokenUsage([leftResult.usage, rightResult.usage]),
7936
+ usage: aggregateTokenUsage([
7937
+ ...failedAttemptUsage ? [failedAttemptUsage] : [],
7938
+ leftResult.usage,
7939
+ rightResult.usage
7940
+ ]),
7868
7941
  scrubbedCueCounts: mergeScrubbedCueCounts(
7869
7942
  leftResult.scrubbedCueCounts,
7870
7943
  rightResult.scrubbedCueCounts