@mux/ai 0.2.0 → 0.3.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/README.md +61 -16
- package/dist/{index-DyTSka2R.d.ts → index-BcNDGOI6.d.ts} +12 -24
- package/dist/{index-CMZYZcj6.d.ts → index-D3fZHu0h.d.ts} +2 -10
- package/dist/index.d.ts +3 -3
- package/dist/index.js +155 -160
- package/dist/index.js.map +1 -1
- package/dist/primitives/index.d.ts +2 -2
- package/dist/primitives/index.js +19 -8
- package/dist/primitives/index.js.map +1 -1
- package/dist/{types-ktXDZ93V.d.ts → types-DzOQNn9R.d.ts} +3 -25
- package/dist/workflows/index.d.ts +2 -2
- package/dist/workflows/index.js +155 -160
- package/dist/workflows/index.js.map +1 -1
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -72,10 +72,9 @@ var env = parseEnv();
|
|
|
72
72
|
var env_default = env;
|
|
73
73
|
|
|
74
74
|
// src/lib/url-signing.ts
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
const keySecret = config.muxPrivateKey ?? env_default.MUX_PRIVATE_KEY;
|
|
75
|
+
function getMuxSigningContextFromEnv() {
|
|
76
|
+
const keyId = env_default.MUX_SIGNING_KEY;
|
|
77
|
+
const keySecret = env_default.MUX_PRIVATE_KEY;
|
|
79
78
|
if (!keyId || !keySecret) {
|
|
80
79
|
return void 0;
|
|
81
80
|
}
|
|
@@ -112,10 +111,11 @@ async function signUrl(url, playbackId, context, type = "video", params) {
|
|
|
112
111
|
|
|
113
112
|
// src/primitives/storyboards.ts
|
|
114
113
|
var DEFAULT_STORYBOARD_WIDTH = 640;
|
|
115
|
-
async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH,
|
|
114
|
+
async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH, shouldSign = false) {
|
|
116
115
|
"use step";
|
|
117
116
|
const baseUrl = `https://image.mux.com/${playbackId}/storyboard.png`;
|
|
118
|
-
if (
|
|
117
|
+
if (shouldSign) {
|
|
118
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
119
119
|
return signUrl(baseUrl, playbackId, signingContext, "storyboard", { width });
|
|
120
120
|
}
|
|
121
121
|
return `${baseUrl}?width=${width}`;
|
|
@@ -209,7 +209,7 @@ function chunkText(text, strategy) {
|
|
|
209
209
|
// src/primitives/thumbnails.ts
|
|
210
210
|
async function getThumbnailUrls(playbackId, duration, options = {}) {
|
|
211
211
|
"use step";
|
|
212
|
-
const { interval = 10, width = 640,
|
|
212
|
+
const { interval = 10, width = 640, shouldSign = false } = options;
|
|
213
213
|
const timestamps = [];
|
|
214
214
|
if (duration <= 50) {
|
|
215
215
|
const spacing = duration / 6;
|
|
@@ -223,7 +223,8 @@ async function getThumbnailUrls(playbackId, duration, options = {}) {
|
|
|
223
223
|
}
|
|
224
224
|
const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;
|
|
225
225
|
const urlPromises = timestamps.map(async (time) => {
|
|
226
|
-
if (
|
|
226
|
+
if (shouldSign) {
|
|
227
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
227
228
|
return signUrl(baseUrl, playbackId, signingContext, "thumbnail", { time, width });
|
|
228
229
|
}
|
|
229
230
|
return `${baseUrl}?time=${time}&width=${width}`;
|
|
@@ -339,17 +340,18 @@ function parseVTTCues(vttContent) {
|
|
|
339
340
|
}
|
|
340
341
|
return cues;
|
|
341
342
|
}
|
|
342
|
-
async function buildTranscriptUrl(playbackId, trackId,
|
|
343
|
+
async function buildTranscriptUrl(playbackId, trackId, shouldSign = false) {
|
|
343
344
|
"use step";
|
|
344
345
|
const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;
|
|
345
|
-
if (
|
|
346
|
+
if (shouldSign) {
|
|
347
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
346
348
|
return signUrl(baseUrl, playbackId, signingContext, "video");
|
|
347
349
|
}
|
|
348
350
|
return baseUrl;
|
|
349
351
|
}
|
|
350
352
|
async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
351
353
|
"use step";
|
|
352
|
-
const { languageCode, cleanTranscript = true,
|
|
354
|
+
const { languageCode, cleanTranscript = true, shouldSign } = options;
|
|
353
355
|
const track = findCaptionTrack(asset, languageCode);
|
|
354
356
|
if (!track) {
|
|
355
357
|
return { transcriptText: "" };
|
|
@@ -357,7 +359,7 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
357
359
|
if (!track.id) {
|
|
358
360
|
return { transcriptText: "", track };
|
|
359
361
|
}
|
|
360
|
-
const transcriptUrl = await buildTranscriptUrl(playbackId, track.id,
|
|
362
|
+
const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign);
|
|
361
363
|
try {
|
|
362
364
|
const response = await fetch(transcriptUrl);
|
|
363
365
|
if (!response.ok) {
|
|
@@ -414,22 +416,22 @@ function requireEnv(value, name) {
|
|
|
414
416
|
}
|
|
415
417
|
return value;
|
|
416
418
|
}
|
|
417
|
-
function createLanguageModelFromConfig(provider, modelId
|
|
419
|
+
function createLanguageModelFromConfig(provider, modelId) {
|
|
418
420
|
switch (provider) {
|
|
419
421
|
case "openai": {
|
|
420
|
-
const apiKey =
|
|
422
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
421
423
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
422
424
|
const openai = createOpenAI({ apiKey });
|
|
423
425
|
return openai(modelId);
|
|
424
426
|
}
|
|
425
427
|
case "anthropic": {
|
|
426
|
-
const apiKey =
|
|
428
|
+
const apiKey = env_default.ANTHROPIC_API_KEY;
|
|
427
429
|
requireEnv(apiKey, "ANTHROPIC_API_KEY");
|
|
428
430
|
const anthropic = createAnthropic({ apiKey });
|
|
429
431
|
return anthropic(modelId);
|
|
430
432
|
}
|
|
431
433
|
case "google": {
|
|
432
|
-
const apiKey =
|
|
434
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
433
435
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
434
436
|
const google = createGoogleGenerativeAI({ apiKey });
|
|
435
437
|
return google(modelId);
|
|
@@ -440,16 +442,16 @@ function createLanguageModelFromConfig(provider, modelId, credentials) {
|
|
|
440
442
|
}
|
|
441
443
|
}
|
|
442
444
|
}
|
|
443
|
-
function createEmbeddingModelFromConfig(provider, modelId
|
|
445
|
+
function createEmbeddingModelFromConfig(provider, modelId) {
|
|
444
446
|
switch (provider) {
|
|
445
447
|
case "openai": {
|
|
446
|
-
const apiKey =
|
|
448
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
447
449
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
448
450
|
const openai = createOpenAI({ apiKey });
|
|
449
451
|
return openai.embedding(modelId);
|
|
450
452
|
}
|
|
451
453
|
case "google": {
|
|
452
|
-
const apiKey =
|
|
454
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
453
455
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
454
456
|
const google = createGoogleGenerativeAI({ apiKey });
|
|
455
457
|
return google.textEmbeddingModel(modelId);
|
|
@@ -465,7 +467,7 @@ function resolveLanguageModel(options = {}) {
|
|
|
465
467
|
const modelId = options.model || DEFAULT_LANGUAGE_MODELS[provider];
|
|
466
468
|
switch (provider) {
|
|
467
469
|
case "openai": {
|
|
468
|
-
const apiKey =
|
|
470
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
469
471
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
470
472
|
const openai = createOpenAI({
|
|
471
473
|
apiKey
|
|
@@ -477,7 +479,7 @@ function resolveLanguageModel(options = {}) {
|
|
|
477
479
|
};
|
|
478
480
|
}
|
|
479
481
|
case "anthropic": {
|
|
480
|
-
const apiKey =
|
|
482
|
+
const apiKey = env_default.ANTHROPIC_API_KEY;
|
|
481
483
|
requireEnv(apiKey, "ANTHROPIC_API_KEY");
|
|
482
484
|
const anthropic = createAnthropic({
|
|
483
485
|
apiKey
|
|
@@ -489,7 +491,7 @@ function resolveLanguageModel(options = {}) {
|
|
|
489
491
|
};
|
|
490
492
|
}
|
|
491
493
|
case "google": {
|
|
492
|
-
const apiKey =
|
|
494
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
493
495
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
494
496
|
const google = createGoogleGenerativeAI({
|
|
495
497
|
apiKey
|
|
@@ -511,7 +513,7 @@ function resolveEmbeddingModel(options = {}) {
|
|
|
511
513
|
const modelId = options.model || DEFAULT_EMBEDDING_MODELS[provider];
|
|
512
514
|
switch (provider) {
|
|
513
515
|
case "openai": {
|
|
514
|
-
const apiKey =
|
|
516
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
515
517
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
516
518
|
const openai = createOpenAI({
|
|
517
519
|
apiKey
|
|
@@ -523,7 +525,7 @@ function resolveEmbeddingModel(options = {}) {
|
|
|
523
525
|
};
|
|
524
526
|
}
|
|
525
527
|
case "google": {
|
|
526
|
-
const apiKey =
|
|
528
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
527
529
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
528
530
|
const google = createGoogleGenerativeAI({
|
|
529
531
|
apiKey
|
|
@@ -542,13 +544,45 @@ function resolveEmbeddingModel(options = {}) {
|
|
|
542
544
|
}
|
|
543
545
|
|
|
544
546
|
// src/lib/client-factory.ts
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
547
|
+
function getMuxCredentialsFromEnv() {
|
|
548
|
+
const muxTokenId = env_default.MUX_TOKEN_ID;
|
|
549
|
+
const muxTokenSecret = env_default.MUX_TOKEN_SECRET;
|
|
550
|
+
if (!muxTokenId || !muxTokenSecret) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
"Mux credentials are required. Set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables."
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
return { muxTokenId, muxTokenSecret };
|
|
556
|
+
}
|
|
557
|
+
function getApiKeyFromEnv(provider) {
|
|
558
|
+
const envVarMap = {
|
|
559
|
+
openai: env_default.OPENAI_API_KEY,
|
|
560
|
+
anthropic: env_default.ANTHROPIC_API_KEY,
|
|
561
|
+
google: env_default.GOOGLE_GENERATIVE_AI_API_KEY,
|
|
562
|
+
hive: env_default.HIVE_API_KEY,
|
|
563
|
+
elevenlabs: env_default.ELEVENLABS_API_KEY
|
|
564
|
+
};
|
|
565
|
+
const apiKey = envVarMap[provider];
|
|
566
|
+
if (!apiKey) {
|
|
567
|
+
const envVarNames = {
|
|
568
|
+
openai: "OPENAI_API_KEY",
|
|
569
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
570
|
+
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
571
|
+
hive: "HIVE_API_KEY",
|
|
572
|
+
elevenlabs: "ELEVENLABS_API_KEY"
|
|
573
|
+
};
|
|
574
|
+
throw new Error(
|
|
575
|
+
`${provider} API key is required. Set ${envVarNames[provider]} environment variable.`
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
return apiKey;
|
|
579
|
+
}
|
|
580
|
+
async function validateCredentials(requiredProvider) {
|
|
581
|
+
const muxTokenId = env_default.MUX_TOKEN_ID;
|
|
582
|
+
const muxTokenSecret = env_default.MUX_TOKEN_SECRET;
|
|
583
|
+
const openaiApiKey = env_default.OPENAI_API_KEY;
|
|
584
|
+
const anthropicApiKey = env_default.ANTHROPIC_API_KEY;
|
|
585
|
+
const googleApiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
552
586
|
if (!muxTokenId || !muxTokenSecret) {
|
|
553
587
|
throw new Error(
|
|
554
588
|
"Mux credentials are required. Provide muxTokenId and muxTokenSecret in options or set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables."
|
|
@@ -578,9 +612,8 @@ async function validateCredentials(options, requiredProvider) {
|
|
|
578
612
|
};
|
|
579
613
|
}
|
|
580
614
|
async function createWorkflowConfig(options, provider) {
|
|
581
|
-
"use step";
|
|
582
615
|
const providerToUse = provider || options.provider || "openai";
|
|
583
|
-
const credentials = await validateCredentials(
|
|
616
|
+
const credentials = await validateCredentials(providerToUse);
|
|
584
617
|
const resolved = resolveLanguageModel({
|
|
585
618
|
...options,
|
|
586
619
|
provider: providerToUse
|
|
@@ -701,11 +734,12 @@ function getPlaybackId(asset) {
|
|
|
701
734
|
"No public or signed playback ID found for this asset. A public or signed playback ID is required. DRM playback IDs are not currently supported."
|
|
702
735
|
);
|
|
703
736
|
}
|
|
704
|
-
async function getPlaybackIdForAsset(
|
|
737
|
+
async function getPlaybackIdForAsset(assetId) {
|
|
705
738
|
"use step";
|
|
739
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
706
740
|
const mux = new Mux2({
|
|
707
|
-
tokenId:
|
|
708
|
-
tokenSecret:
|
|
741
|
+
tokenId: muxTokenId,
|
|
742
|
+
tokenSecret: muxTokenSecret
|
|
709
743
|
});
|
|
710
744
|
const asset = await mux.video.assets.retrieve(assetId);
|
|
711
745
|
const { id: playbackId, policy } = getPlaybackId(asset);
|
|
@@ -884,16 +918,11 @@ async function analyzeStoryboard({
|
|
|
884
918
|
imageDataUrl,
|
|
885
919
|
provider,
|
|
886
920
|
modelId,
|
|
887
|
-
credentials,
|
|
888
921
|
userPrompt,
|
|
889
922
|
systemPrompt
|
|
890
923
|
}) {
|
|
891
924
|
"use step";
|
|
892
|
-
const model = createLanguageModelFromConfig(
|
|
893
|
-
provider,
|
|
894
|
-
modelId,
|
|
895
|
-
credentials
|
|
896
|
-
);
|
|
925
|
+
const model = createLanguageModelFromConfig(provider, modelId);
|
|
897
926
|
const response = await generateObject({
|
|
898
927
|
model,
|
|
899
928
|
schema: burnedInCaptionsSchema,
|
|
@@ -938,14 +967,14 @@ async function hasBurnedInCaptions(assetId, options = {}) {
|
|
|
938
967
|
{ ...config, model },
|
|
939
968
|
provider
|
|
940
969
|
);
|
|
941
|
-
const { playbackId, policy } = await getPlaybackIdForAsset(
|
|
942
|
-
const signingContext =
|
|
970
|
+
const { playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
971
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
943
972
|
if (policy === "signed" && !signingContext) {
|
|
944
973
|
throw new Error(
|
|
945
974
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
946
975
|
);
|
|
947
976
|
}
|
|
948
|
-
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed"
|
|
977
|
+
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed");
|
|
949
978
|
let analysisResponse;
|
|
950
979
|
if (imageSubmissionMode === "base64") {
|
|
951
980
|
const base64Data = await fetchImageAsBase64(imageUrl, imageDownloadOptions);
|
|
@@ -953,7 +982,6 @@ async function hasBurnedInCaptions(assetId, options = {}) {
|
|
|
953
982
|
imageDataUrl: base64Data,
|
|
954
983
|
provider: workflowConfig.provider,
|
|
955
984
|
modelId: workflowConfig.modelId,
|
|
956
|
-
credentials: workflowConfig.credentials,
|
|
957
985
|
userPrompt,
|
|
958
986
|
systemPrompt: SYSTEM_PROMPT
|
|
959
987
|
});
|
|
@@ -962,7 +990,6 @@ async function hasBurnedInCaptions(assetId, options = {}) {
|
|
|
962
990
|
imageDataUrl: imageUrl,
|
|
963
991
|
provider: workflowConfig.provider,
|
|
964
992
|
modelId: workflowConfig.modelId,
|
|
965
|
-
credentials: workflowConfig.credentials,
|
|
966
993
|
userPrompt,
|
|
967
994
|
systemPrompt: SYSTEM_PROMPT
|
|
968
995
|
});
|
|
@@ -1035,16 +1062,11 @@ var chaptersSchema = z3.object({
|
|
|
1035
1062
|
async function generateChaptersWithAI({
|
|
1036
1063
|
provider,
|
|
1037
1064
|
modelId,
|
|
1038
|
-
credentials,
|
|
1039
1065
|
timestampedTranscript,
|
|
1040
1066
|
systemPrompt
|
|
1041
1067
|
}) {
|
|
1042
1068
|
"use step";
|
|
1043
|
-
const model = createLanguageModelFromConfig(
|
|
1044
|
-
provider,
|
|
1045
|
-
modelId,
|
|
1046
|
-
credentials
|
|
1047
|
-
);
|
|
1069
|
+
const model = createLanguageModelFromConfig(provider, modelId);
|
|
1048
1070
|
const response = await withRetry(
|
|
1049
1071
|
() => generateObject2({
|
|
1050
1072
|
model,
|
|
@@ -1087,8 +1109,8 @@ async function generateChapters(assetId, languageCode, options = {}) {
|
|
|
1087
1109
|
"use workflow";
|
|
1088
1110
|
const { provider = "openai", model } = options;
|
|
1089
1111
|
const config = await createWorkflowConfig({ ...options, model }, provider);
|
|
1090
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
1091
|
-
const signingContext =
|
|
1112
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1113
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1092
1114
|
if (policy === "signed" && !signingContext) {
|
|
1093
1115
|
throw new Error(
|
|
1094
1116
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -1098,7 +1120,7 @@ async function generateChapters(assetId, languageCode, options = {}) {
|
|
|
1098
1120
|
languageCode,
|
|
1099
1121
|
cleanTranscript: false,
|
|
1100
1122
|
// keep timestamps for chapter segmentation
|
|
1101
|
-
|
|
1123
|
+
shouldSign: policy === "signed"
|
|
1102
1124
|
});
|
|
1103
1125
|
if (!transcriptResult.track || !transcriptResult.transcriptText) {
|
|
1104
1126
|
const availableLanguages = getReadyTextTracks(assetData).map((t) => t.language_code).filter(Boolean).join(", ");
|
|
@@ -1115,7 +1137,6 @@ async function generateChapters(assetId, languageCode, options = {}) {
|
|
|
1115
1137
|
chaptersData = await generateChaptersWithAI({
|
|
1116
1138
|
provider: config.provider,
|
|
1117
1139
|
modelId: config.modelId,
|
|
1118
|
-
credentials: config.credentials,
|
|
1119
1140
|
timestampedTranscript,
|
|
1120
1141
|
systemPrompt: SYSTEM_PROMPT2
|
|
1121
1142
|
});
|
|
@@ -1162,11 +1183,10 @@ function averageEmbeddings(embeddings) {
|
|
|
1162
1183
|
async function generateSingleChunkEmbedding({
|
|
1163
1184
|
chunk,
|
|
1164
1185
|
provider,
|
|
1165
|
-
modelId
|
|
1166
|
-
credentials
|
|
1186
|
+
modelId
|
|
1167
1187
|
}) {
|
|
1168
1188
|
"use step";
|
|
1169
|
-
const model = createEmbeddingModelFromConfig(provider, modelId
|
|
1189
|
+
const model = createEmbeddingModelFromConfig(provider, modelId);
|
|
1170
1190
|
const response = await withRetry(
|
|
1171
1191
|
() => embed({
|
|
1172
1192
|
model,
|
|
@@ -1192,13 +1212,9 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
1192
1212
|
chunkingStrategy = { type: "token", maxTokens: 500, overlap: 100 },
|
|
1193
1213
|
batchSize = 5
|
|
1194
1214
|
} = options;
|
|
1195
|
-
const credentials = await validateCredentials(options, provider === "google" ? "google" : "openai");
|
|
1196
1215
|
const embeddingModel = resolveEmbeddingModel({ ...options, provider, model });
|
|
1197
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
1198
|
-
|
|
1199
|
-
assetId
|
|
1200
|
-
);
|
|
1201
|
-
const signingContext = await resolveSigningContext(options);
|
|
1216
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1217
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1202
1218
|
if (policy === "signed" && !signingContext) {
|
|
1203
1219
|
throw new Error(
|
|
1204
1220
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -1208,7 +1224,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
1208
1224
|
const transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {
|
|
1209
1225
|
languageCode,
|
|
1210
1226
|
cleanTranscript: !useVttChunking,
|
|
1211
|
-
|
|
1227
|
+
shouldSign: policy === "signed"
|
|
1212
1228
|
});
|
|
1213
1229
|
if (!transcriptResult.track || !transcriptResult.transcriptText) {
|
|
1214
1230
|
const availableLanguages = getReadyTextTracks(assetData).map((t) => t.language_code).filter(Boolean).join(", ");
|
|
@@ -1237,8 +1253,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
1237
1253
|
(chunk) => generateSingleChunkEmbedding({
|
|
1238
1254
|
chunk,
|
|
1239
1255
|
provider: embeddingModel.provider,
|
|
1240
|
-
modelId: embeddingModel.modelId
|
|
1241
|
-
credentials
|
|
1256
|
+
modelId: embeddingModel.modelId
|
|
1242
1257
|
})
|
|
1243
1258
|
)
|
|
1244
1259
|
);
|
|
@@ -1323,19 +1338,20 @@ async function processConcurrently(items, processor, maxConcurrent = 5) {
|
|
|
1323
1338
|
}
|
|
1324
1339
|
return results;
|
|
1325
1340
|
}
|
|
1326
|
-
async function requestOpenAIModeration(imageUrls,
|
|
1341
|
+
async function requestOpenAIModeration(imageUrls, model, maxConcurrent = 5, submissionMode = "url", downloadOptions) {
|
|
1327
1342
|
"use step";
|
|
1328
1343
|
const targetUrls = submissionMode === "base64" ? (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map(
|
|
1329
|
-
(img) => ({ url: img.url, image: img.base64Data,
|
|
1330
|
-
) : imageUrls.map((url) => ({ url, image: url,
|
|
1344
|
+
(img) => ({ url: img.url, image: img.base64Data, model })
|
|
1345
|
+
) : imageUrls.map((url) => ({ url, image: url, model }));
|
|
1331
1346
|
const moderate = async (entry) => {
|
|
1332
1347
|
"use step";
|
|
1348
|
+
const apiKey = getApiKeyFromEnv("openai");
|
|
1333
1349
|
try {
|
|
1334
1350
|
const res = await fetch("https://api.openai.com/v1/moderations", {
|
|
1335
1351
|
method: "POST",
|
|
1336
1352
|
headers: {
|
|
1337
1353
|
"Content-Type": "application/json",
|
|
1338
|
-
"Authorization": `Bearer ${
|
|
1354
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1339
1355
|
},
|
|
1340
1356
|
body: JSON.stringify({
|
|
1341
1357
|
model: entry.model,
|
|
@@ -1381,7 +1397,7 @@ function getHiveCategoryScores(classes, categoryNames) {
|
|
|
1381
1397
|
const scores = categoryNames.map((category) => scoreMap[category] || 0);
|
|
1382
1398
|
return Math.max(...scores, 0);
|
|
1383
1399
|
}
|
|
1384
|
-
async function requestHiveModeration(imageUrls,
|
|
1400
|
+
async function requestHiveModeration(imageUrls, maxConcurrent = 5, submissionMode = "url", downloadOptions) {
|
|
1385
1401
|
"use step";
|
|
1386
1402
|
const targets = submissionMode === "base64" ? (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map((img) => ({
|
|
1387
1403
|
url: img.url,
|
|
@@ -1396,6 +1412,7 @@ async function requestHiveModeration(imageUrls, apiKey, maxConcurrent = 5, submi
|
|
|
1396
1412
|
}));
|
|
1397
1413
|
const moderate = async (entry) => {
|
|
1398
1414
|
"use step";
|
|
1415
|
+
const apiKey = getApiKeyFromEnv("hive");
|
|
1399
1416
|
try {
|
|
1400
1417
|
const formData = new FormData();
|
|
1401
1418
|
if (entry.source.kind === "url") {
|
|
@@ -1452,10 +1469,9 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
1452
1469
|
imageSubmissionMode = "url",
|
|
1453
1470
|
imageDownloadOptions
|
|
1454
1471
|
} = options;
|
|
1455
|
-
const
|
|
1456
|
-
const { asset, playbackId, policy } = await getPlaybackIdForAsset(credentials, assetId);
|
|
1472
|
+
const { asset, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1457
1473
|
const duration = asset.duration || 0;
|
|
1458
|
-
const signingContext =
|
|
1474
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1459
1475
|
if (policy === "signed" && !signingContext) {
|
|
1460
1476
|
throw new Error(
|
|
1461
1477
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -1464,30 +1480,20 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
1464
1480
|
const thumbnailUrls = await getThumbnailUrls(playbackId, duration, {
|
|
1465
1481
|
interval: thumbnailInterval,
|
|
1466
1482
|
width: thumbnailWidth,
|
|
1467
|
-
|
|
1483
|
+
shouldSign: policy === "signed"
|
|
1468
1484
|
});
|
|
1469
1485
|
let thumbnailScores;
|
|
1470
1486
|
if (provider === "openai") {
|
|
1471
|
-
const apiKey = credentials.openaiApiKey;
|
|
1472
|
-
if (!apiKey) {
|
|
1473
|
-
throw new Error("OpenAI API key is required for moderation. Set OPENAI_API_KEY or pass openaiApiKey.");
|
|
1474
|
-
}
|
|
1475
1487
|
thumbnailScores = await requestOpenAIModeration(
|
|
1476
1488
|
thumbnailUrls,
|
|
1477
|
-
apiKey,
|
|
1478
1489
|
model || "omni-moderation-latest",
|
|
1479
1490
|
maxConcurrent,
|
|
1480
1491
|
imageSubmissionMode,
|
|
1481
1492
|
imageDownloadOptions
|
|
1482
1493
|
);
|
|
1483
1494
|
} else if (provider === "hive") {
|
|
1484
|
-
const hiveApiKey = options.hiveApiKey || env_default.HIVE_API_KEY;
|
|
1485
|
-
if (!hiveApiKey) {
|
|
1486
|
-
throw new Error("Hive API key is required for moderation. Set HIVE_API_KEY or pass hiveApiKey.");
|
|
1487
|
-
}
|
|
1488
1495
|
thumbnailScores = await requestHiveModeration(
|
|
1489
1496
|
thumbnailUrls,
|
|
1490
|
-
hiveApiKey,
|
|
1491
1497
|
maxConcurrent,
|
|
1492
1498
|
imageSubmissionMode,
|
|
1493
1499
|
imageDownloadOptions
|
|
@@ -1520,9 +1526,10 @@ var summarySchema = z4.object({
|
|
|
1520
1526
|
title: z4.string(),
|
|
1521
1527
|
description: z4.string()
|
|
1522
1528
|
});
|
|
1529
|
+
var VALID_TONES = ["neutral", "playful", "professional"];
|
|
1523
1530
|
var TONE_INSTRUCTIONS = {
|
|
1524
|
-
|
|
1525
|
-
|
|
1531
|
+
neutral: "Provide a clear, straightforward analysis.",
|
|
1532
|
+
playful: "Channel your inner diva! Answer with maximum sass, wit, and playful attitude. Don't hold back - be cheeky, clever, and delightfully snarky. Make it pop!",
|
|
1526
1533
|
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
1527
1534
|
};
|
|
1528
1535
|
var summarizationPromptBuilder = createPromptBuilder({
|
|
@@ -1638,13 +1645,9 @@ function buildUserPrompt2({
|
|
|
1638
1645
|
}
|
|
1639
1646
|
return summarizationPromptBuilder.buildWithContext(promptOverrides, contextSections);
|
|
1640
1647
|
}
|
|
1641
|
-
async function analyzeStoryboard2(imageDataUrl,
|
|
1648
|
+
async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, systemPrompt) {
|
|
1642
1649
|
"use step";
|
|
1643
|
-
const model = createLanguageModelFromConfig(
|
|
1644
|
-
workflowConfig.provider,
|
|
1645
|
-
workflowConfig.modelId,
|
|
1646
|
-
workflowConfig.credentials
|
|
1647
|
-
);
|
|
1650
|
+
const model = createLanguageModelFromConfig(provider, modelId);
|
|
1648
1651
|
const response = await generateObject3({
|
|
1649
1652
|
model,
|
|
1650
1653
|
schema: summarySchema,
|
|
@@ -1701,7 +1704,7 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1701
1704
|
const {
|
|
1702
1705
|
provider = "openai",
|
|
1703
1706
|
model,
|
|
1704
|
-
tone = "
|
|
1707
|
+
tone = "neutral",
|
|
1705
1708
|
includeTranscript = true,
|
|
1706
1709
|
cleanTranscript = true,
|
|
1707
1710
|
imageSubmissionMode = "url",
|
|
@@ -1709,12 +1712,17 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1709
1712
|
abortSignal: _abortSignal,
|
|
1710
1713
|
promptOverrides
|
|
1711
1714
|
} = options ?? {};
|
|
1715
|
+
if (!VALID_TONES.includes(tone)) {
|
|
1716
|
+
throw new Error(
|
|
1717
|
+
`Invalid tone "${tone}". Valid tones are: ${VALID_TONES.join(", ")}`
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1712
1720
|
const config = await createWorkflowConfig(
|
|
1713
1721
|
{ ...options, model },
|
|
1714
1722
|
provider
|
|
1715
1723
|
);
|
|
1716
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
1717
|
-
const signingContext =
|
|
1724
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1725
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1718
1726
|
if (policy === "signed" && !signingContext) {
|
|
1719
1727
|
throw new Error(
|
|
1720
1728
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -1722,7 +1730,7 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1722
1730
|
}
|
|
1723
1731
|
const transcriptText = includeTranscript ? (await fetchTranscriptForAsset(assetData, playbackId, {
|
|
1724
1732
|
cleanTranscript,
|
|
1725
|
-
|
|
1733
|
+
shouldSign: policy === "signed"
|
|
1726
1734
|
})).transcriptText : "";
|
|
1727
1735
|
const userPrompt = buildUserPrompt2({
|
|
1728
1736
|
tone,
|
|
@@ -1730,19 +1738,20 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1730
1738
|
isCleanTranscript: cleanTranscript,
|
|
1731
1739
|
promptOverrides
|
|
1732
1740
|
});
|
|
1733
|
-
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed"
|
|
1741
|
+
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed");
|
|
1734
1742
|
let analysisResponse;
|
|
1735
1743
|
try {
|
|
1736
1744
|
if (imageSubmissionMode === "base64") {
|
|
1737
1745
|
const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);
|
|
1738
1746
|
analysisResponse = await analyzeStoryboard2(
|
|
1739
1747
|
downloadResult.base64Data,
|
|
1740
|
-
config,
|
|
1748
|
+
config.provider,
|
|
1749
|
+
config.modelId,
|
|
1741
1750
|
userPrompt,
|
|
1742
1751
|
SYSTEM_PROMPT3
|
|
1743
1752
|
);
|
|
1744
1753
|
} else {
|
|
1745
|
-
analysisResponse = await withRetry(() => analyzeStoryboard2(imageUrl, config, userPrompt, SYSTEM_PROMPT3));
|
|
1754
|
+
analysisResponse = await withRetry(() => analyzeStoryboard2(imageUrl, config.provider, config.modelId, userPrompt, SYSTEM_PROMPT3));
|
|
1746
1755
|
}
|
|
1747
1756
|
} catch (error) {
|
|
1748
1757
|
throw new Error(
|
|
@@ -1947,11 +1956,12 @@ function getReadyAudioStaticRendition(asset) {
|
|
|
1947
1956
|
);
|
|
1948
1957
|
}
|
|
1949
1958
|
var hasReadyAudioStaticRendition = (asset) => Boolean(getReadyAudioStaticRendition(asset));
|
|
1950
|
-
async function requestStaticRenditionCreation(
|
|
1959
|
+
async function requestStaticRenditionCreation(assetId) {
|
|
1951
1960
|
"use step";
|
|
1961
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
1952
1962
|
const mux = new Mux3({
|
|
1953
|
-
tokenId:
|
|
1954
|
-
tokenSecret:
|
|
1963
|
+
tokenId: muxTokenId,
|
|
1964
|
+
tokenSecret: muxTokenSecret
|
|
1955
1965
|
});
|
|
1956
1966
|
try {
|
|
1957
1967
|
await mux.video.assets.createStaticRendition(assetId, {
|
|
@@ -1970,13 +1980,13 @@ async function requestStaticRenditionCreation(credentials, assetId) {
|
|
|
1970
1980
|
}
|
|
1971
1981
|
async function waitForAudioStaticRendition({
|
|
1972
1982
|
assetId,
|
|
1973
|
-
credentials,
|
|
1974
1983
|
initialAsset
|
|
1975
1984
|
}) {
|
|
1976
1985
|
"use step";
|
|
1986
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
1977
1987
|
const mux = new Mux3({
|
|
1978
|
-
tokenId:
|
|
1979
|
-
tokenSecret:
|
|
1988
|
+
tokenId: muxTokenId,
|
|
1989
|
+
tokenSecret: muxTokenSecret
|
|
1980
1990
|
});
|
|
1981
1991
|
let currentAsset = initialAsset;
|
|
1982
1992
|
if (hasReadyAudioStaticRendition(currentAsset)) {
|
|
@@ -1984,9 +1994,9 @@ async function waitForAudioStaticRendition({
|
|
|
1984
1994
|
}
|
|
1985
1995
|
const status = currentAsset.static_renditions?.status ?? "not_requested";
|
|
1986
1996
|
if (status === "not_requested" || status === void 0) {
|
|
1987
|
-
await requestStaticRenditionCreation(
|
|
1997
|
+
await requestStaticRenditionCreation(assetId);
|
|
1988
1998
|
} else if (status === "errored") {
|
|
1989
|
-
await requestStaticRenditionCreation(
|
|
1999
|
+
await requestStaticRenditionCreation(assetId);
|
|
1990
2000
|
} else {
|
|
1991
2001
|
console.warn(`\u2139\uFE0F Static rendition already ${status}. Waiting for it to finish...`);
|
|
1992
2002
|
}
|
|
@@ -2022,10 +2032,10 @@ async function createElevenLabsDubbingJob({
|
|
|
2022
2032
|
audioBuffer,
|
|
2023
2033
|
assetId,
|
|
2024
2034
|
elevenLabsLangCode,
|
|
2025
|
-
elevenLabsApiKey,
|
|
2026
2035
|
numSpeakers
|
|
2027
2036
|
}) {
|
|
2028
2037
|
"use step";
|
|
2038
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2029
2039
|
const audioBlob = new Blob([audioBuffer], { type: "audio/mp4" });
|
|
2030
2040
|
const formData = new FormData();
|
|
2031
2041
|
formData.append("file", audioBlob);
|
|
@@ -2046,10 +2056,10 @@ async function createElevenLabsDubbingJob({
|
|
|
2046
2056
|
return dubbingData.dubbing_id;
|
|
2047
2057
|
}
|
|
2048
2058
|
async function checkElevenLabsDubbingStatus({
|
|
2049
|
-
dubbingId
|
|
2050
|
-
elevenLabsApiKey
|
|
2059
|
+
dubbingId
|
|
2051
2060
|
}) {
|
|
2052
2061
|
"use step";
|
|
2062
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2053
2063
|
const statusResponse = await fetch(`https://api.elevenlabs.io/v1/dubbing/${dubbingId}`, {
|
|
2054
2064
|
headers: {
|
|
2055
2065
|
"xi-api-key": elevenLabsApiKey
|
|
@@ -2066,10 +2076,10 @@ async function checkElevenLabsDubbingStatus({
|
|
|
2066
2076
|
}
|
|
2067
2077
|
async function downloadDubbedAudioFromElevenLabs({
|
|
2068
2078
|
dubbingId,
|
|
2069
|
-
languageCode
|
|
2070
|
-
elevenLabsApiKey
|
|
2079
|
+
languageCode
|
|
2071
2080
|
}) {
|
|
2072
2081
|
"use step";
|
|
2082
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2073
2083
|
const audioUrl = `https://api.elevenlabs.io/v1/dubbing/${dubbingId}/audio/${languageCode}`;
|
|
2074
2084
|
const audioResponse = await fetch(audioUrl, {
|
|
2075
2085
|
headers: {
|
|
@@ -2087,14 +2097,14 @@ async function uploadDubbedAudioToS3({
|
|
|
2087
2097
|
toLanguageCode,
|
|
2088
2098
|
s3Endpoint,
|
|
2089
2099
|
s3Region,
|
|
2090
|
-
s3Bucket
|
|
2091
|
-
s3AccessKeyId,
|
|
2092
|
-
s3SecretAccessKey
|
|
2100
|
+
s3Bucket
|
|
2093
2101
|
}) {
|
|
2094
2102
|
"use step";
|
|
2095
2103
|
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
2096
2104
|
const { Upload } = await import("@aws-sdk/lib-storage");
|
|
2097
2105
|
const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
|
|
2106
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2107
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2098
2108
|
const s3Client = new S3Client({
|
|
2099
2109
|
region: s3Region,
|
|
2100
2110
|
endpoint: s3Endpoint,
|
|
@@ -2127,11 +2137,12 @@ async function uploadDubbedAudioToS3({
|
|
|
2127
2137
|
console.warn(`\u{1F517} Generated presigned URL (expires in 1 hour)`);
|
|
2128
2138
|
return presignedUrl;
|
|
2129
2139
|
}
|
|
2130
|
-
async function createAudioTrackOnMux(
|
|
2140
|
+
async function createAudioTrackOnMux(assetId, languageCode, presignedUrl) {
|
|
2131
2141
|
"use step";
|
|
2142
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
2132
2143
|
const mux = new Mux3({
|
|
2133
|
-
tokenId:
|
|
2134
|
-
tokenSecret:
|
|
2144
|
+
tokenId: muxTokenId,
|
|
2145
|
+
tokenSecret: muxTokenSecret
|
|
2135
2146
|
});
|
|
2136
2147
|
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(languageCode) || languageCode.toUpperCase();
|
|
2137
2148
|
const trackName = `${languageName} (auto-dubbed)`;
|
|
@@ -2158,21 +2169,20 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2158
2169
|
if (provider !== "elevenlabs") {
|
|
2159
2170
|
throw new Error("Only ElevenLabs provider is currently supported for audio translation");
|
|
2160
2171
|
}
|
|
2161
|
-
const credentials = await validateCredentials(options);
|
|
2162
2172
|
const elevenLabsKey = elevenLabsApiKey ?? env_default.ELEVENLABS_API_KEY;
|
|
2163
2173
|
const s3Endpoint = options.s3Endpoint ?? env_default.S3_ENDPOINT;
|
|
2164
2174
|
const s3Region = options.s3Region ?? env_default.S3_REGION ?? "auto";
|
|
2165
2175
|
const s3Bucket = options.s3Bucket ?? env_default.S3_BUCKET;
|
|
2166
|
-
const s3AccessKeyId =
|
|
2167
|
-
const s3SecretAccessKey =
|
|
2176
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2177
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2168
2178
|
if (!elevenLabsKey) {
|
|
2169
2179
|
throw new Error("ElevenLabs API key is required. Provide elevenLabsApiKey in options or set ELEVENLABS_API_KEY environment variable.");
|
|
2170
2180
|
}
|
|
2171
2181
|
if (uploadToMux && (!s3Endpoint || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey)) {
|
|
2172
2182
|
throw new Error("S3 configuration is required for uploading to Mux. Provide s3Endpoint, s3Bucket, s3AccessKeyId, and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.");
|
|
2173
2183
|
}
|
|
2174
|
-
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(
|
|
2175
|
-
const signingContext =
|
|
2184
|
+
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
2185
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
2176
2186
|
if (policy === "signed" && !signingContext) {
|
|
2177
2187
|
throw new Error(
|
|
2178
2188
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -2183,7 +2193,6 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2183
2193
|
console.warn("\u274C No ready audio static rendition found. Requesting one now...");
|
|
2184
2194
|
currentAsset = await waitForAudioStaticRendition({
|
|
2185
2195
|
assetId,
|
|
2186
|
-
credentials,
|
|
2187
2196
|
initialAsset: currentAsset
|
|
2188
2197
|
});
|
|
2189
2198
|
}
|
|
@@ -2213,7 +2222,6 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2213
2222
|
audioBuffer,
|
|
2214
2223
|
assetId,
|
|
2215
2224
|
elevenLabsLangCode,
|
|
2216
|
-
elevenLabsApiKey: elevenLabsKey,
|
|
2217
2225
|
numSpeakers
|
|
2218
2226
|
});
|
|
2219
2227
|
console.warn(`\u2705 Dubbing job created with ID: ${dubbingId}`);
|
|
@@ -2230,8 +2238,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2230
2238
|
pollAttempts++;
|
|
2231
2239
|
try {
|
|
2232
2240
|
const statusResult = await checkElevenLabsDubbingStatus({
|
|
2233
|
-
dubbingId
|
|
2234
|
-
elevenLabsApiKey: elevenLabsKey
|
|
2241
|
+
dubbingId
|
|
2235
2242
|
});
|
|
2236
2243
|
dubbingStatus = statusResult.status;
|
|
2237
2244
|
targetLanguages = statusResult.targetLanguages;
|
|
@@ -2274,8 +2281,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2274
2281
|
}
|
|
2275
2282
|
dubbedAudioBuffer = await downloadDubbedAudioFromElevenLabs({
|
|
2276
2283
|
dubbingId,
|
|
2277
|
-
languageCode: downloadLangCode
|
|
2278
|
-
elevenLabsApiKey: elevenLabsKey
|
|
2284
|
+
languageCode: downloadLangCode
|
|
2279
2285
|
});
|
|
2280
2286
|
console.warn("\u2705 Dubbed audio downloaded successfully!");
|
|
2281
2287
|
} catch (error) {
|
|
@@ -2290,9 +2296,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2290
2296
|
toLanguageCode,
|
|
2291
2297
|
s3Endpoint,
|
|
2292
2298
|
s3Region,
|
|
2293
|
-
s3Bucket
|
|
2294
|
-
s3AccessKeyId,
|
|
2295
|
-
s3SecretAccessKey
|
|
2299
|
+
s3Bucket
|
|
2296
2300
|
});
|
|
2297
2301
|
} catch (error) {
|
|
2298
2302
|
throw new Error(`Failed to upload audio to S3: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -2301,7 +2305,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2301
2305
|
let uploadedTrackId;
|
|
2302
2306
|
const muxLangCode = toISO639_1(toLanguageCode);
|
|
2303
2307
|
try {
|
|
2304
|
-
uploadedTrackId = await createAudioTrackOnMux(
|
|
2308
|
+
uploadedTrackId = await createAudioTrackOnMux(assetId, muxLangCode, presignedUrl);
|
|
2305
2309
|
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
|
|
2306
2310
|
const trackName = `${languageName} (auto-dubbed)`;
|
|
2307
2311
|
console.warn(`\u2705 Track added to Mux asset with ID: ${uploadedTrackId}`);
|
|
@@ -2343,15 +2347,10 @@ async function translateVttWithAI({
|
|
|
2343
2347
|
toLanguageCode,
|
|
2344
2348
|
provider,
|
|
2345
2349
|
modelId,
|
|
2346
|
-
credentials,
|
|
2347
2350
|
abortSignal
|
|
2348
2351
|
}) {
|
|
2349
2352
|
"use step";
|
|
2350
|
-
const languageModel = createLanguageModelFromConfig(
|
|
2351
|
-
provider,
|
|
2352
|
-
modelId,
|
|
2353
|
-
credentials
|
|
2354
|
-
);
|
|
2353
|
+
const languageModel = createLanguageModelFromConfig(provider, modelId);
|
|
2355
2354
|
const response = await generateObject4({
|
|
2356
2355
|
model: languageModel,
|
|
2357
2356
|
schema: translationSchema,
|
|
@@ -2383,14 +2382,14 @@ async function uploadVttToS3({
|
|
|
2383
2382
|
toLanguageCode,
|
|
2384
2383
|
s3Endpoint,
|
|
2385
2384
|
s3Region,
|
|
2386
|
-
s3Bucket
|
|
2387
|
-
s3AccessKeyId,
|
|
2388
|
-
s3SecretAccessKey
|
|
2385
|
+
s3Bucket
|
|
2389
2386
|
}) {
|
|
2390
2387
|
"use step";
|
|
2391
2388
|
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
2392
2389
|
const { Upload } = await import("@aws-sdk/lib-storage");
|
|
2393
2390
|
const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
|
|
2391
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2392
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2394
2393
|
const s3Client = new S3Client({
|
|
2395
2394
|
region: s3Region,
|
|
2396
2395
|
endpoint: s3Endpoint,
|
|
@@ -2421,11 +2420,12 @@ async function uploadVttToS3({
|
|
|
2421
2420
|
});
|
|
2422
2421
|
return presignedUrl;
|
|
2423
2422
|
}
|
|
2424
|
-
async function createTextTrackOnMux(
|
|
2423
|
+
async function createTextTrackOnMux(assetId, languageCode, trackName, presignedUrl) {
|
|
2425
2424
|
"use step";
|
|
2425
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
2426
2426
|
const mux = new Mux4({
|
|
2427
|
-
tokenId:
|
|
2428
|
-
tokenSecret:
|
|
2427
|
+
tokenId: muxTokenId,
|
|
2428
|
+
tokenSecret: muxTokenSecret
|
|
2429
2429
|
});
|
|
2430
2430
|
const trackResponse = await mux.video.assets.createTrack(assetId, {
|
|
2431
2431
|
type: "text",
|
|
@@ -2447,15 +2447,13 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2447
2447
|
s3Endpoint: providedS3Endpoint,
|
|
2448
2448
|
s3Region: providedS3Region,
|
|
2449
2449
|
s3Bucket: providedS3Bucket,
|
|
2450
|
-
s3AccessKeyId: providedS3AccessKeyId,
|
|
2451
|
-
s3SecretAccessKey: providedS3SecretAccessKey,
|
|
2452
2450
|
uploadToMux: uploadToMuxOption
|
|
2453
2451
|
} = options;
|
|
2454
2452
|
const s3Endpoint = providedS3Endpoint ?? env_default.S3_ENDPOINT;
|
|
2455
2453
|
const s3Region = providedS3Region ?? env_default.S3_REGION ?? "auto";
|
|
2456
2454
|
const s3Bucket = providedS3Bucket ?? env_default.S3_BUCKET;
|
|
2457
|
-
const s3AccessKeyId =
|
|
2458
|
-
const s3SecretAccessKey =
|
|
2455
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2456
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2459
2457
|
const uploadToMux = uploadToMuxOption !== false;
|
|
2460
2458
|
const config = await createWorkflowConfig(
|
|
2461
2459
|
{ ...options, model },
|
|
@@ -2464,8 +2462,8 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2464
2462
|
if (uploadToMux && (!s3Endpoint || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey)) {
|
|
2465
2463
|
throw new Error("S3 configuration is required for uploading to Mux. Provide s3Endpoint, s3Bucket, s3AccessKeyId, and s3SecretAccessKey in options or set S3_ENDPOINT, S3_BUCKET, S3_ACCESS_KEY_ID, and S3_SECRET_ACCESS_KEY environment variables.");
|
|
2466
2464
|
}
|
|
2467
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
2468
|
-
const signingContext =
|
|
2465
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
2466
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
2469
2467
|
if (policy === "signed" && !signingContext) {
|
|
2470
2468
|
throw new Error(
|
|
2471
2469
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -2499,7 +2497,6 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2499
2497
|
toLanguageCode,
|
|
2500
2498
|
provider: config.provider,
|
|
2501
2499
|
modelId: config.modelId,
|
|
2502
|
-
credentials: config.credentials,
|
|
2503
2500
|
abortSignal: options.abortSignal
|
|
2504
2501
|
});
|
|
2505
2502
|
translatedVtt = result.translatedVtt;
|
|
@@ -2530,9 +2527,7 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2530
2527
|
toLanguageCode,
|
|
2531
2528
|
s3Endpoint,
|
|
2532
2529
|
s3Region,
|
|
2533
|
-
s3Bucket
|
|
2534
|
-
s3AccessKeyId,
|
|
2535
|
-
s3SecretAccessKey
|
|
2530
|
+
s3Bucket
|
|
2536
2531
|
});
|
|
2537
2532
|
} catch (error) {
|
|
2538
2533
|
throw new Error(`Failed to upload VTT to S3: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -2541,7 +2536,7 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2541
2536
|
try {
|
|
2542
2537
|
const languageName = getLanguageName(toLanguageCode);
|
|
2543
2538
|
const trackName = `${languageName} (auto-translated)`;
|
|
2544
|
-
uploadedTrackId = await createTextTrackOnMux(
|
|
2539
|
+
uploadedTrackId = await createTextTrackOnMux(assetId, toLanguageCode, trackName, presignedUrl);
|
|
2545
2540
|
} catch (error) {
|
|
2546
2541
|
console.warn(`Failed to add track to Mux asset: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2547
2542
|
}
|