@mux/ai 0.30.0 → 0.31.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 } from '../index-D3cB2ltG.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 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 } from '../index-ooikuED7.js';
2
2
  import 'zod';
3
3
  import '@ai-sdk/anthropic';
4
4
  import '@ai-sdk/google';
@@ -1430,13 +1430,23 @@ function createLanguageSection(languageName) {
1430
1430
  }
1431
1431
 
1432
1432
  // src/lib/retry.ts
1433
+ import { DownloadError } from "ai";
1433
1434
  var DEFAULT_RETRY_OPTIONS = {
1434
1435
  maxRetries: 3,
1435
1436
  baseDelay: 2e3,
1436
1437
  maxDelay: 1e4
1437
1438
  };
1438
1439
  function defaultShouldRetry(error, _attempt) {
1439
- return Boolean(error.message && error.message.includes("Timeout while downloading"));
1440
+ if (error.message.includes("Timeout while downloading")) {
1441
+ return true;
1442
+ }
1443
+ const isDownloadError = DownloadError.isInstance(error);
1444
+ const isSerializedDownloadError = error.name === "AI_DownloadError";
1445
+ if (!isDownloadError && !isSerializedDownloadError) {
1446
+ return false;
1447
+ }
1448
+ const statusCode = error.statusCode;
1449
+ return statusCode === void 0 || statusCode === 408 || statusCode === 425 || statusCode === 429 || statusCode >= 500;
1440
1450
  }
1441
1451
  function calculateDelay(attempt, baseDelay, maxDelay) {
1442
1452
  const exponentialDelay = baseDelay * 2 ** (attempt - 1);
@@ -3136,14 +3146,14 @@ async function hasBurnedInCaptionsInternal(assetId, options, collectedUsage) {
3136
3146
  credentials
3137
3147
  });
3138
3148
  } else {
3139
- analysisResponse = await analyzeStoryboard({
3149
+ analysisResponse = await withRetry(() => analyzeStoryboard({
3140
3150
  imageDataUrl: imageUrl,
3141
3151
  provider: modelConfig.provider,
3142
3152
  modelId: modelConfig.modelId,
3143
3153
  userPrompt,
3144
3154
  systemPrompt: SYSTEM_PROMPT2,
3145
3155
  credentials
3146
- });
3156
+ }));
3147
3157
  }
3148
3158
  collectedUsage.push(analysisResponse.usage);
3149
3159
  if (!analysisResponse.result) {
@@ -6834,6 +6844,59 @@ async function downloadAndUploadDubbedAudio({
6834
6844
  console.warn(`\u{1F517} Generated presigned URL (expires in ${expiryHours} hour${expiryHours === 1 ? "" : "s"})`);
6835
6845
  return presignedUrl;
6836
6846
  }
6847
+ async function downloadAndUploadDubTranscript({
6848
+ dubbingId,
6849
+ languageCode,
6850
+ assetId,
6851
+ toLanguageCode,
6852
+ s3Endpoint,
6853
+ s3Region,
6854
+ s3Bucket,
6855
+ storageAdapter,
6856
+ s3SignedUrlExpirySeconds,
6857
+ credentials
6858
+ }) {
6859
+ "use step";
6860
+ const elevenLabsApiKey = await getApiKeyFromEnv("elevenlabs", credentials);
6861
+ const transcriptUrl = `https://api.elevenlabs.io/v1/dubbing/${dubbingId}/transcripts/${languageCode}/format/webvtt`;
6862
+ const transcriptResponse = await fetch(transcriptUrl, {
6863
+ headers: {
6864
+ "xi-api-key": elevenLabsApiKey
6865
+ }
6866
+ });
6867
+ if (!transcriptResponse.ok) {
6868
+ throw new Error(`Failed to fetch dub transcript: ${transcriptResponse.statusText}`);
6869
+ }
6870
+ const transcriptBody = await transcriptResponse.json();
6871
+ const transcriptVtt = transcriptBody.webvtt;
6872
+ if (!transcriptVtt) {
6873
+ throw new Error("Dub transcript response did not include webvtt content");
6874
+ }
6875
+ const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
6876
+ const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
6877
+ const vttKey = `audio-translations/${assetId}/auto-to-${toLanguageCode}-${Date.now()}.vtt`;
6878
+ await putObjectWithStorageAdapter({
6879
+ accessKeyId: s3AccessKeyId,
6880
+ secretAccessKey: s3SecretAccessKey,
6881
+ endpoint: s3Endpoint,
6882
+ region: s3Region,
6883
+ bucket: s3Bucket,
6884
+ key: vttKey,
6885
+ body: transcriptVtt,
6886
+ contentType: "text/vtt"
6887
+ }, storageAdapter);
6888
+ const presignedUrl = await createPresignedGetUrlWithStorageAdapter({
6889
+ accessKeyId: s3AccessKeyId,
6890
+ secretAccessKey: s3SecretAccessKey,
6891
+ endpoint: s3Endpoint,
6892
+ region: s3Region,
6893
+ bucket: s3Bucket,
6894
+ key: vttKey,
6895
+ expiresInSeconds: s3SignedUrlExpirySeconds ?? 86400
6896
+ }, storageAdapter);
6897
+ console.warn(`\u2705 Dub transcript uploaded successfully to: ${vttKey}`);
6898
+ return presignedUrl;
6899
+ }
6837
6900
  async function createAudioTrackOnMux(assetId, languageCode, presignedUrl, credentials) {
6838
6901
  "use step";
6839
6902
  const muxClient = await resolveMuxClient(credentials);
@@ -6860,6 +6923,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
6860
6923
  // 0 = auto-detect
6861
6924
  uploadToS3: uploadToS3Option,
6862
6925
  uploadToMux: uploadToMuxOption,
6926
+ uploadCaptionsToMux = false,
6863
6927
  storageAdapter,
6864
6928
  credentials: providedCredentials
6865
6929
  } = options;
@@ -6869,7 +6933,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
6869
6933
  const credentials = providedCredentials;
6870
6934
  const effectiveStorageAdapter = storageAdapter;
6871
6935
  const uploadToMux = uploadToMuxOption !== false;
6872
- const uploadToS3 = uploadToS3Option || uploadToMux;
6936
+ const uploadToS3 = uploadToS3Option || uploadToMux || uploadCaptionsToMux;
6873
6937
  const s3Endpoint = options.s3Endpoint ?? env_default.S3_ENDPOINT;
6874
6938
  const s3Region = options.s3Region ?? env_default.S3_REGION ?? "auto";
6875
6939
  const s3Bucket = options.s3Bucket ?? env_default.S3_BUCKET;
@@ -6950,10 +7014,12 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
6950
7014
  console.warn("\u2705 Dubbing completed successfully!");
6951
7015
  let presignedUrl;
6952
7016
  let uploadedTrackId;
7017
+ let captionsPresignedUrl;
7018
+ let captionsTrackId;
6953
7019
  if (uploadToS3) {
6954
7020
  console.warn("\u{1F4E5} Downloading dubbed audio from ElevenLabs and staging to S3...");
7021
+ const downloadLangCode = targetLanguages[0] ?? toISO639_1(toLanguageCode);
6955
7022
  try {
6956
- const downloadLangCode = targetLanguages[0] ?? toISO639_1(toLanguageCode);
6957
7023
  presignedUrl = await downloadAndUploadDubbedAudio({
6958
7024
  dubbingId,
6959
7025
  languageCode: downloadLangCode,
@@ -6985,6 +7051,40 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
6985
7051
  console.warn(presignedUrl);
6986
7052
  }
6987
7053
  }
7054
+ try {
7055
+ console.warn("\u{1F4E5} Downloading dub transcript from ElevenLabs and staging to S3...");
7056
+ captionsPresignedUrl = await downloadAndUploadDubTranscript({
7057
+ dubbingId,
7058
+ languageCode: downloadLangCode,
7059
+ assetId,
7060
+ toLanguageCode,
7061
+ s3Endpoint,
7062
+ s3Region,
7063
+ s3Bucket,
7064
+ storageAdapter: effectiveStorageAdapter,
7065
+ s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds,
7066
+ credentials
7067
+ });
7068
+ if (uploadCaptionsToMux) {
7069
+ console.warn("\u{1F4F9} Adding dubbed captions track to Mux asset...");
7070
+ const muxLangCode = toISO639_1(toLanguageCode);
7071
+ const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
7072
+ captionsTrackId = await createTextTrackOnMux(
7073
+ assetId,
7074
+ muxLangCode,
7075
+ `${languageName} (auto-dubbed)`,
7076
+ captionsPresignedUrl,
7077
+ credentials
7078
+ );
7079
+ console.warn(`\u2705 Captions track added to Mux asset with ID: ${captionsTrackId}`);
7080
+ }
7081
+ } catch (error) {
7082
+ console.warn(`\u26A0\uFE0F Failed to attach dubbed captions: ${error instanceof Error ? error.message : "Unknown error"}`);
7083
+ if (captionsPresignedUrl) {
7084
+ console.warn("\u{1F517} You can manually add the captions track using this presigned URL:");
7085
+ console.warn(captionsPresignedUrl);
7086
+ }
7087
+ }
6988
7088
  }
6989
7089
  const targetLanguage = getLanguageCodePair(toLanguageCode);
6990
7090
  return {
@@ -6994,6 +7094,8 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
6994
7094
  dubbingId,
6995
7095
  uploadedTrackId,
6996
7096
  presignedUrl,
7097
+ captionsTrackId,
7098
+ captionsPresignedUrl,
6997
7099
  usage: {
6998
7100
  metadata: {
6999
7101
  assetDurationSeconds