@mux/ai 0.2.0 → 0.3.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.
- 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 +149 -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 +149 -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
|
|
@@ -1521,8 +1527,8 @@ var summarySchema = z4.object({
|
|
|
1521
1527
|
description: z4.string()
|
|
1522
1528
|
});
|
|
1523
1529
|
var TONE_INSTRUCTIONS = {
|
|
1524
|
-
|
|
1525
|
-
|
|
1530
|
+
neutral: "Provide a clear, straightforward analysis.",
|
|
1531
|
+
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
1532
|
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
1527
1533
|
};
|
|
1528
1534
|
var summarizationPromptBuilder = createPromptBuilder({
|
|
@@ -1638,13 +1644,9 @@ function buildUserPrompt2({
|
|
|
1638
1644
|
}
|
|
1639
1645
|
return summarizationPromptBuilder.buildWithContext(promptOverrides, contextSections);
|
|
1640
1646
|
}
|
|
1641
|
-
async function analyzeStoryboard2(imageDataUrl,
|
|
1647
|
+
async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, systemPrompt) {
|
|
1642
1648
|
"use step";
|
|
1643
|
-
const model = createLanguageModelFromConfig(
|
|
1644
|
-
workflowConfig.provider,
|
|
1645
|
-
workflowConfig.modelId,
|
|
1646
|
-
workflowConfig.credentials
|
|
1647
|
-
);
|
|
1649
|
+
const model = createLanguageModelFromConfig(provider, modelId);
|
|
1648
1650
|
const response = await generateObject3({
|
|
1649
1651
|
model,
|
|
1650
1652
|
schema: summarySchema,
|
|
@@ -1701,7 +1703,7 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1701
1703
|
const {
|
|
1702
1704
|
provider = "openai",
|
|
1703
1705
|
model,
|
|
1704
|
-
tone = "
|
|
1706
|
+
tone = "neutral",
|
|
1705
1707
|
includeTranscript = true,
|
|
1706
1708
|
cleanTranscript = true,
|
|
1707
1709
|
imageSubmissionMode = "url",
|
|
@@ -1713,8 +1715,8 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1713
1715
|
{ ...options, model },
|
|
1714
1716
|
provider
|
|
1715
1717
|
);
|
|
1716
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
1717
|
-
const signingContext =
|
|
1718
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1719
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1718
1720
|
if (policy === "signed" && !signingContext) {
|
|
1719
1721
|
throw new Error(
|
|
1720
1722
|
"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 +1724,7 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1722
1724
|
}
|
|
1723
1725
|
const transcriptText = includeTranscript ? (await fetchTranscriptForAsset(assetData, playbackId, {
|
|
1724
1726
|
cleanTranscript,
|
|
1725
|
-
|
|
1727
|
+
shouldSign: policy === "signed"
|
|
1726
1728
|
})).transcriptText : "";
|
|
1727
1729
|
const userPrompt = buildUserPrompt2({
|
|
1728
1730
|
tone,
|
|
@@ -1730,19 +1732,20 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1730
1732
|
isCleanTranscript: cleanTranscript,
|
|
1731
1733
|
promptOverrides
|
|
1732
1734
|
});
|
|
1733
|
-
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed"
|
|
1735
|
+
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed");
|
|
1734
1736
|
let analysisResponse;
|
|
1735
1737
|
try {
|
|
1736
1738
|
if (imageSubmissionMode === "base64") {
|
|
1737
1739
|
const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);
|
|
1738
1740
|
analysisResponse = await analyzeStoryboard2(
|
|
1739
1741
|
downloadResult.base64Data,
|
|
1740
|
-
config,
|
|
1742
|
+
config.provider,
|
|
1743
|
+
config.modelId,
|
|
1741
1744
|
userPrompt,
|
|
1742
1745
|
SYSTEM_PROMPT3
|
|
1743
1746
|
);
|
|
1744
1747
|
} else {
|
|
1745
|
-
analysisResponse = await withRetry(() => analyzeStoryboard2(imageUrl, config, userPrompt, SYSTEM_PROMPT3));
|
|
1748
|
+
analysisResponse = await withRetry(() => analyzeStoryboard2(imageUrl, config.provider, config.modelId, userPrompt, SYSTEM_PROMPT3));
|
|
1746
1749
|
}
|
|
1747
1750
|
} catch (error) {
|
|
1748
1751
|
throw new Error(
|
|
@@ -1947,11 +1950,12 @@ function getReadyAudioStaticRendition(asset) {
|
|
|
1947
1950
|
);
|
|
1948
1951
|
}
|
|
1949
1952
|
var hasReadyAudioStaticRendition = (asset) => Boolean(getReadyAudioStaticRendition(asset));
|
|
1950
|
-
async function requestStaticRenditionCreation(
|
|
1953
|
+
async function requestStaticRenditionCreation(assetId) {
|
|
1951
1954
|
"use step";
|
|
1955
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
1952
1956
|
const mux = new Mux3({
|
|
1953
|
-
tokenId:
|
|
1954
|
-
tokenSecret:
|
|
1957
|
+
tokenId: muxTokenId,
|
|
1958
|
+
tokenSecret: muxTokenSecret
|
|
1955
1959
|
});
|
|
1956
1960
|
try {
|
|
1957
1961
|
await mux.video.assets.createStaticRendition(assetId, {
|
|
@@ -1970,13 +1974,13 @@ async function requestStaticRenditionCreation(credentials, assetId) {
|
|
|
1970
1974
|
}
|
|
1971
1975
|
async function waitForAudioStaticRendition({
|
|
1972
1976
|
assetId,
|
|
1973
|
-
credentials,
|
|
1974
1977
|
initialAsset
|
|
1975
1978
|
}) {
|
|
1976
1979
|
"use step";
|
|
1980
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
1977
1981
|
const mux = new Mux3({
|
|
1978
|
-
tokenId:
|
|
1979
|
-
tokenSecret:
|
|
1982
|
+
tokenId: muxTokenId,
|
|
1983
|
+
tokenSecret: muxTokenSecret
|
|
1980
1984
|
});
|
|
1981
1985
|
let currentAsset = initialAsset;
|
|
1982
1986
|
if (hasReadyAudioStaticRendition(currentAsset)) {
|
|
@@ -1984,9 +1988,9 @@ async function waitForAudioStaticRendition({
|
|
|
1984
1988
|
}
|
|
1985
1989
|
const status = currentAsset.static_renditions?.status ?? "not_requested";
|
|
1986
1990
|
if (status === "not_requested" || status === void 0) {
|
|
1987
|
-
await requestStaticRenditionCreation(
|
|
1991
|
+
await requestStaticRenditionCreation(assetId);
|
|
1988
1992
|
} else if (status === "errored") {
|
|
1989
|
-
await requestStaticRenditionCreation(
|
|
1993
|
+
await requestStaticRenditionCreation(assetId);
|
|
1990
1994
|
} else {
|
|
1991
1995
|
console.warn(`\u2139\uFE0F Static rendition already ${status}. Waiting for it to finish...`);
|
|
1992
1996
|
}
|
|
@@ -2022,10 +2026,10 @@ async function createElevenLabsDubbingJob({
|
|
|
2022
2026
|
audioBuffer,
|
|
2023
2027
|
assetId,
|
|
2024
2028
|
elevenLabsLangCode,
|
|
2025
|
-
elevenLabsApiKey,
|
|
2026
2029
|
numSpeakers
|
|
2027
2030
|
}) {
|
|
2028
2031
|
"use step";
|
|
2032
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2029
2033
|
const audioBlob = new Blob([audioBuffer], { type: "audio/mp4" });
|
|
2030
2034
|
const formData = new FormData();
|
|
2031
2035
|
formData.append("file", audioBlob);
|
|
@@ -2046,10 +2050,10 @@ async function createElevenLabsDubbingJob({
|
|
|
2046
2050
|
return dubbingData.dubbing_id;
|
|
2047
2051
|
}
|
|
2048
2052
|
async function checkElevenLabsDubbingStatus({
|
|
2049
|
-
dubbingId
|
|
2050
|
-
elevenLabsApiKey
|
|
2053
|
+
dubbingId
|
|
2051
2054
|
}) {
|
|
2052
2055
|
"use step";
|
|
2056
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2053
2057
|
const statusResponse = await fetch(`https://api.elevenlabs.io/v1/dubbing/${dubbingId}`, {
|
|
2054
2058
|
headers: {
|
|
2055
2059
|
"xi-api-key": elevenLabsApiKey
|
|
@@ -2066,10 +2070,10 @@ async function checkElevenLabsDubbingStatus({
|
|
|
2066
2070
|
}
|
|
2067
2071
|
async function downloadDubbedAudioFromElevenLabs({
|
|
2068
2072
|
dubbingId,
|
|
2069
|
-
languageCode
|
|
2070
|
-
elevenLabsApiKey
|
|
2073
|
+
languageCode
|
|
2071
2074
|
}) {
|
|
2072
2075
|
"use step";
|
|
2076
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2073
2077
|
const audioUrl = `https://api.elevenlabs.io/v1/dubbing/${dubbingId}/audio/${languageCode}`;
|
|
2074
2078
|
const audioResponse = await fetch(audioUrl, {
|
|
2075
2079
|
headers: {
|
|
@@ -2087,14 +2091,14 @@ async function uploadDubbedAudioToS3({
|
|
|
2087
2091
|
toLanguageCode,
|
|
2088
2092
|
s3Endpoint,
|
|
2089
2093
|
s3Region,
|
|
2090
|
-
s3Bucket
|
|
2091
|
-
s3AccessKeyId,
|
|
2092
|
-
s3SecretAccessKey
|
|
2094
|
+
s3Bucket
|
|
2093
2095
|
}) {
|
|
2094
2096
|
"use step";
|
|
2095
2097
|
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
2096
2098
|
const { Upload } = await import("@aws-sdk/lib-storage");
|
|
2097
2099
|
const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
|
|
2100
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2101
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2098
2102
|
const s3Client = new S3Client({
|
|
2099
2103
|
region: s3Region,
|
|
2100
2104
|
endpoint: s3Endpoint,
|
|
@@ -2127,11 +2131,12 @@ async function uploadDubbedAudioToS3({
|
|
|
2127
2131
|
console.warn(`\u{1F517} Generated presigned URL (expires in 1 hour)`);
|
|
2128
2132
|
return presignedUrl;
|
|
2129
2133
|
}
|
|
2130
|
-
async function createAudioTrackOnMux(
|
|
2134
|
+
async function createAudioTrackOnMux(assetId, languageCode, presignedUrl) {
|
|
2131
2135
|
"use step";
|
|
2136
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
2132
2137
|
const mux = new Mux3({
|
|
2133
|
-
tokenId:
|
|
2134
|
-
tokenSecret:
|
|
2138
|
+
tokenId: muxTokenId,
|
|
2139
|
+
tokenSecret: muxTokenSecret
|
|
2135
2140
|
});
|
|
2136
2141
|
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(languageCode) || languageCode.toUpperCase();
|
|
2137
2142
|
const trackName = `${languageName} (auto-dubbed)`;
|
|
@@ -2158,21 +2163,20 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2158
2163
|
if (provider !== "elevenlabs") {
|
|
2159
2164
|
throw new Error("Only ElevenLabs provider is currently supported for audio translation");
|
|
2160
2165
|
}
|
|
2161
|
-
const credentials = await validateCredentials(options);
|
|
2162
2166
|
const elevenLabsKey = elevenLabsApiKey ?? env_default.ELEVENLABS_API_KEY;
|
|
2163
2167
|
const s3Endpoint = options.s3Endpoint ?? env_default.S3_ENDPOINT;
|
|
2164
2168
|
const s3Region = options.s3Region ?? env_default.S3_REGION ?? "auto";
|
|
2165
2169
|
const s3Bucket = options.s3Bucket ?? env_default.S3_BUCKET;
|
|
2166
|
-
const s3AccessKeyId =
|
|
2167
|
-
const s3SecretAccessKey =
|
|
2170
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2171
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2168
2172
|
if (!elevenLabsKey) {
|
|
2169
2173
|
throw new Error("ElevenLabs API key is required. Provide elevenLabsApiKey in options or set ELEVENLABS_API_KEY environment variable.");
|
|
2170
2174
|
}
|
|
2171
2175
|
if (uploadToMux && (!s3Endpoint || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey)) {
|
|
2172
2176
|
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
2177
|
}
|
|
2174
|
-
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(
|
|
2175
|
-
const signingContext =
|
|
2178
|
+
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
2179
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
2176
2180
|
if (policy === "signed" && !signingContext) {
|
|
2177
2181
|
throw new Error(
|
|
2178
2182
|
"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 +2187,6 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2183
2187
|
console.warn("\u274C No ready audio static rendition found. Requesting one now...");
|
|
2184
2188
|
currentAsset = await waitForAudioStaticRendition({
|
|
2185
2189
|
assetId,
|
|
2186
|
-
credentials,
|
|
2187
2190
|
initialAsset: currentAsset
|
|
2188
2191
|
});
|
|
2189
2192
|
}
|
|
@@ -2213,7 +2216,6 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2213
2216
|
audioBuffer,
|
|
2214
2217
|
assetId,
|
|
2215
2218
|
elevenLabsLangCode,
|
|
2216
|
-
elevenLabsApiKey: elevenLabsKey,
|
|
2217
2219
|
numSpeakers
|
|
2218
2220
|
});
|
|
2219
2221
|
console.warn(`\u2705 Dubbing job created with ID: ${dubbingId}`);
|
|
@@ -2230,8 +2232,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2230
2232
|
pollAttempts++;
|
|
2231
2233
|
try {
|
|
2232
2234
|
const statusResult = await checkElevenLabsDubbingStatus({
|
|
2233
|
-
dubbingId
|
|
2234
|
-
elevenLabsApiKey: elevenLabsKey
|
|
2235
|
+
dubbingId
|
|
2235
2236
|
});
|
|
2236
2237
|
dubbingStatus = statusResult.status;
|
|
2237
2238
|
targetLanguages = statusResult.targetLanguages;
|
|
@@ -2274,8 +2275,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2274
2275
|
}
|
|
2275
2276
|
dubbedAudioBuffer = await downloadDubbedAudioFromElevenLabs({
|
|
2276
2277
|
dubbingId,
|
|
2277
|
-
languageCode: downloadLangCode
|
|
2278
|
-
elevenLabsApiKey: elevenLabsKey
|
|
2278
|
+
languageCode: downloadLangCode
|
|
2279
2279
|
});
|
|
2280
2280
|
console.warn("\u2705 Dubbed audio downloaded successfully!");
|
|
2281
2281
|
} catch (error) {
|
|
@@ -2290,9 +2290,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2290
2290
|
toLanguageCode,
|
|
2291
2291
|
s3Endpoint,
|
|
2292
2292
|
s3Region,
|
|
2293
|
-
s3Bucket
|
|
2294
|
-
s3AccessKeyId,
|
|
2295
|
-
s3SecretAccessKey
|
|
2293
|
+
s3Bucket
|
|
2296
2294
|
});
|
|
2297
2295
|
} catch (error) {
|
|
2298
2296
|
throw new Error(`Failed to upload audio to S3: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -2301,7 +2299,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2301
2299
|
let uploadedTrackId;
|
|
2302
2300
|
const muxLangCode = toISO639_1(toLanguageCode);
|
|
2303
2301
|
try {
|
|
2304
|
-
uploadedTrackId = await createAudioTrackOnMux(
|
|
2302
|
+
uploadedTrackId = await createAudioTrackOnMux(assetId, muxLangCode, presignedUrl);
|
|
2305
2303
|
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
|
|
2306
2304
|
const trackName = `${languageName} (auto-dubbed)`;
|
|
2307
2305
|
console.warn(`\u2705 Track added to Mux asset with ID: ${uploadedTrackId}`);
|
|
@@ -2343,15 +2341,10 @@ async function translateVttWithAI({
|
|
|
2343
2341
|
toLanguageCode,
|
|
2344
2342
|
provider,
|
|
2345
2343
|
modelId,
|
|
2346
|
-
credentials,
|
|
2347
2344
|
abortSignal
|
|
2348
2345
|
}) {
|
|
2349
2346
|
"use step";
|
|
2350
|
-
const languageModel = createLanguageModelFromConfig(
|
|
2351
|
-
provider,
|
|
2352
|
-
modelId,
|
|
2353
|
-
credentials
|
|
2354
|
-
);
|
|
2347
|
+
const languageModel = createLanguageModelFromConfig(provider, modelId);
|
|
2355
2348
|
const response = await generateObject4({
|
|
2356
2349
|
model: languageModel,
|
|
2357
2350
|
schema: translationSchema,
|
|
@@ -2383,14 +2376,14 @@ async function uploadVttToS3({
|
|
|
2383
2376
|
toLanguageCode,
|
|
2384
2377
|
s3Endpoint,
|
|
2385
2378
|
s3Region,
|
|
2386
|
-
s3Bucket
|
|
2387
|
-
s3AccessKeyId,
|
|
2388
|
-
s3SecretAccessKey
|
|
2379
|
+
s3Bucket
|
|
2389
2380
|
}) {
|
|
2390
2381
|
"use step";
|
|
2391
2382
|
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
2392
2383
|
const { Upload } = await import("@aws-sdk/lib-storage");
|
|
2393
2384
|
const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
|
|
2385
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2386
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2394
2387
|
const s3Client = new S3Client({
|
|
2395
2388
|
region: s3Region,
|
|
2396
2389
|
endpoint: s3Endpoint,
|
|
@@ -2421,11 +2414,12 @@ async function uploadVttToS3({
|
|
|
2421
2414
|
});
|
|
2422
2415
|
return presignedUrl;
|
|
2423
2416
|
}
|
|
2424
|
-
async function createTextTrackOnMux(
|
|
2417
|
+
async function createTextTrackOnMux(assetId, languageCode, trackName, presignedUrl) {
|
|
2425
2418
|
"use step";
|
|
2419
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
2426
2420
|
const mux = new Mux4({
|
|
2427
|
-
tokenId:
|
|
2428
|
-
tokenSecret:
|
|
2421
|
+
tokenId: muxTokenId,
|
|
2422
|
+
tokenSecret: muxTokenSecret
|
|
2429
2423
|
});
|
|
2430
2424
|
const trackResponse = await mux.video.assets.createTrack(assetId, {
|
|
2431
2425
|
type: "text",
|
|
@@ -2447,15 +2441,13 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2447
2441
|
s3Endpoint: providedS3Endpoint,
|
|
2448
2442
|
s3Region: providedS3Region,
|
|
2449
2443
|
s3Bucket: providedS3Bucket,
|
|
2450
|
-
s3AccessKeyId: providedS3AccessKeyId,
|
|
2451
|
-
s3SecretAccessKey: providedS3SecretAccessKey,
|
|
2452
2444
|
uploadToMux: uploadToMuxOption
|
|
2453
2445
|
} = options;
|
|
2454
2446
|
const s3Endpoint = providedS3Endpoint ?? env_default.S3_ENDPOINT;
|
|
2455
2447
|
const s3Region = providedS3Region ?? env_default.S3_REGION ?? "auto";
|
|
2456
2448
|
const s3Bucket = providedS3Bucket ?? env_default.S3_BUCKET;
|
|
2457
|
-
const s3AccessKeyId =
|
|
2458
|
-
const s3SecretAccessKey =
|
|
2449
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2450
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2459
2451
|
const uploadToMux = uploadToMuxOption !== false;
|
|
2460
2452
|
const config = await createWorkflowConfig(
|
|
2461
2453
|
{ ...options, model },
|
|
@@ -2464,8 +2456,8 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2464
2456
|
if (uploadToMux && (!s3Endpoint || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey)) {
|
|
2465
2457
|
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
2458
|
}
|
|
2467
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
2468
|
-
const signingContext =
|
|
2459
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
2460
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
2469
2461
|
if (policy === "signed" && !signingContext) {
|
|
2470
2462
|
throw new Error(
|
|
2471
2463
|
"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 +2491,6 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2499
2491
|
toLanguageCode,
|
|
2500
2492
|
provider: config.provider,
|
|
2501
2493
|
modelId: config.modelId,
|
|
2502
|
-
credentials: config.credentials,
|
|
2503
2494
|
abortSignal: options.abortSignal
|
|
2504
2495
|
});
|
|
2505
2496
|
translatedVtt = result.translatedVtt;
|
|
@@ -2530,9 +2521,7 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2530
2521
|
toLanguageCode,
|
|
2531
2522
|
s3Endpoint,
|
|
2532
2523
|
s3Region,
|
|
2533
|
-
s3Bucket
|
|
2534
|
-
s3AccessKeyId,
|
|
2535
|
-
s3SecretAccessKey
|
|
2524
|
+
s3Bucket
|
|
2536
2525
|
});
|
|
2537
2526
|
} catch (error) {
|
|
2538
2527
|
throw new Error(`Failed to upload VTT to S3: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -2541,7 +2530,7 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2541
2530
|
try {
|
|
2542
2531
|
const languageName = getLanguageName(toLanguageCode);
|
|
2543
2532
|
const trackName = `${languageName} (auto-translated)`;
|
|
2544
|
-
uploadedTrackId = await createTextTrackOnMux(
|
|
2533
|
+
uploadedTrackId = await createTextTrackOnMux(assetId, toLanguageCode, trackName, presignedUrl);
|
|
2545
2534
|
} catch (error) {
|
|
2546
2535
|
console.warn(`Failed to add track to Mux asset: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2547
2536
|
}
|