@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/workflows/index.js
CHANGED
|
@@ -66,22 +66,22 @@ function requireEnv(value, name) {
|
|
|
66
66
|
}
|
|
67
67
|
return value;
|
|
68
68
|
}
|
|
69
|
-
function createLanguageModelFromConfig(provider, modelId
|
|
69
|
+
function createLanguageModelFromConfig(provider, modelId) {
|
|
70
70
|
switch (provider) {
|
|
71
71
|
case "openai": {
|
|
72
|
-
const apiKey =
|
|
72
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
73
73
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
74
74
|
const openai = createOpenAI({ apiKey });
|
|
75
75
|
return openai(modelId);
|
|
76
76
|
}
|
|
77
77
|
case "anthropic": {
|
|
78
|
-
const apiKey =
|
|
78
|
+
const apiKey = env_default.ANTHROPIC_API_KEY;
|
|
79
79
|
requireEnv(apiKey, "ANTHROPIC_API_KEY");
|
|
80
80
|
const anthropic = createAnthropic({ apiKey });
|
|
81
81
|
return anthropic(modelId);
|
|
82
82
|
}
|
|
83
83
|
case "google": {
|
|
84
|
-
const apiKey =
|
|
84
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
85
85
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
86
86
|
const google = createGoogleGenerativeAI({ apiKey });
|
|
87
87
|
return google(modelId);
|
|
@@ -92,16 +92,16 @@ function createLanguageModelFromConfig(provider, modelId, credentials) {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
function createEmbeddingModelFromConfig(provider, modelId
|
|
95
|
+
function createEmbeddingModelFromConfig(provider, modelId) {
|
|
96
96
|
switch (provider) {
|
|
97
97
|
case "openai": {
|
|
98
|
-
const apiKey =
|
|
98
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
99
99
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
100
100
|
const openai = createOpenAI({ apiKey });
|
|
101
101
|
return openai.embedding(modelId);
|
|
102
102
|
}
|
|
103
103
|
case "google": {
|
|
104
|
-
const apiKey =
|
|
104
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
105
105
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
106
106
|
const google = createGoogleGenerativeAI({ apiKey });
|
|
107
107
|
return google.textEmbeddingModel(modelId);
|
|
@@ -117,7 +117,7 @@ function resolveLanguageModel(options = {}) {
|
|
|
117
117
|
const modelId = options.model || DEFAULT_LANGUAGE_MODELS[provider];
|
|
118
118
|
switch (provider) {
|
|
119
119
|
case "openai": {
|
|
120
|
-
const apiKey =
|
|
120
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
121
121
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
122
122
|
const openai = createOpenAI({
|
|
123
123
|
apiKey
|
|
@@ -129,7 +129,7 @@ function resolveLanguageModel(options = {}) {
|
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
131
|
case "anthropic": {
|
|
132
|
-
const apiKey =
|
|
132
|
+
const apiKey = env_default.ANTHROPIC_API_KEY;
|
|
133
133
|
requireEnv(apiKey, "ANTHROPIC_API_KEY");
|
|
134
134
|
const anthropic = createAnthropic({
|
|
135
135
|
apiKey
|
|
@@ -141,7 +141,7 @@ function resolveLanguageModel(options = {}) {
|
|
|
141
141
|
};
|
|
142
142
|
}
|
|
143
143
|
case "google": {
|
|
144
|
-
const apiKey =
|
|
144
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
145
145
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
146
146
|
const google = createGoogleGenerativeAI({
|
|
147
147
|
apiKey
|
|
@@ -163,7 +163,7 @@ function resolveEmbeddingModel(options = {}) {
|
|
|
163
163
|
const modelId = options.model || DEFAULT_EMBEDDING_MODELS[provider];
|
|
164
164
|
switch (provider) {
|
|
165
165
|
case "openai": {
|
|
166
|
-
const apiKey =
|
|
166
|
+
const apiKey = env_default.OPENAI_API_KEY;
|
|
167
167
|
requireEnv(apiKey, "OPENAI_API_KEY");
|
|
168
168
|
const openai = createOpenAI({
|
|
169
169
|
apiKey
|
|
@@ -175,7 +175,7 @@ function resolveEmbeddingModel(options = {}) {
|
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
case "google": {
|
|
178
|
-
const apiKey =
|
|
178
|
+
const apiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
179
179
|
requireEnv(apiKey, "GOOGLE_GENERATIVE_AI_API_KEY");
|
|
180
180
|
const google = createGoogleGenerativeAI({
|
|
181
181
|
apiKey
|
|
@@ -194,13 +194,45 @@ function resolveEmbeddingModel(options = {}) {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// src/lib/client-factory.ts
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
197
|
+
function getMuxCredentialsFromEnv() {
|
|
198
|
+
const muxTokenId = env_default.MUX_TOKEN_ID;
|
|
199
|
+
const muxTokenSecret = env_default.MUX_TOKEN_SECRET;
|
|
200
|
+
if (!muxTokenId || !muxTokenSecret) {
|
|
201
|
+
throw new Error(
|
|
202
|
+
"Mux credentials are required. Set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables."
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
return { muxTokenId, muxTokenSecret };
|
|
206
|
+
}
|
|
207
|
+
function getApiKeyFromEnv(provider) {
|
|
208
|
+
const envVarMap = {
|
|
209
|
+
openai: env_default.OPENAI_API_KEY,
|
|
210
|
+
anthropic: env_default.ANTHROPIC_API_KEY,
|
|
211
|
+
google: env_default.GOOGLE_GENERATIVE_AI_API_KEY,
|
|
212
|
+
hive: env_default.HIVE_API_KEY,
|
|
213
|
+
elevenlabs: env_default.ELEVENLABS_API_KEY
|
|
214
|
+
};
|
|
215
|
+
const apiKey = envVarMap[provider];
|
|
216
|
+
if (!apiKey) {
|
|
217
|
+
const envVarNames = {
|
|
218
|
+
openai: "OPENAI_API_KEY",
|
|
219
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
220
|
+
google: "GOOGLE_GENERATIVE_AI_API_KEY",
|
|
221
|
+
hive: "HIVE_API_KEY",
|
|
222
|
+
elevenlabs: "ELEVENLABS_API_KEY"
|
|
223
|
+
};
|
|
224
|
+
throw new Error(
|
|
225
|
+
`${provider} API key is required. Set ${envVarNames[provider]} environment variable.`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return apiKey;
|
|
229
|
+
}
|
|
230
|
+
async function validateCredentials(requiredProvider) {
|
|
231
|
+
const muxTokenId = env_default.MUX_TOKEN_ID;
|
|
232
|
+
const muxTokenSecret = env_default.MUX_TOKEN_SECRET;
|
|
233
|
+
const openaiApiKey = env_default.OPENAI_API_KEY;
|
|
234
|
+
const anthropicApiKey = env_default.ANTHROPIC_API_KEY;
|
|
235
|
+
const googleApiKey = env_default.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
204
236
|
if (!muxTokenId || !muxTokenSecret) {
|
|
205
237
|
throw new Error(
|
|
206
238
|
"Mux credentials are required. Provide muxTokenId and muxTokenSecret in options or set MUX_TOKEN_ID and MUX_TOKEN_SECRET environment variables."
|
|
@@ -230,9 +262,8 @@ async function validateCredentials(options, requiredProvider) {
|
|
|
230
262
|
};
|
|
231
263
|
}
|
|
232
264
|
async function createWorkflowConfig(options, provider) {
|
|
233
|
-
"use step";
|
|
234
265
|
const providerToUse = provider || options.provider || "openai";
|
|
235
|
-
const credentials = await validateCredentials(
|
|
266
|
+
const credentials = await validateCredentials(providerToUse);
|
|
236
267
|
const resolved = resolveLanguageModel({
|
|
237
268
|
...options,
|
|
238
269
|
provider: providerToUse
|
|
@@ -353,11 +384,12 @@ function getPlaybackId(asset) {
|
|
|
353
384
|
"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."
|
|
354
385
|
);
|
|
355
386
|
}
|
|
356
|
-
async function getPlaybackIdForAsset(
|
|
387
|
+
async function getPlaybackIdForAsset(assetId) {
|
|
357
388
|
"use step";
|
|
389
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
358
390
|
const mux = new Mux({
|
|
359
|
-
tokenId:
|
|
360
|
-
tokenSecret:
|
|
391
|
+
tokenId: muxTokenId,
|
|
392
|
+
tokenSecret: muxTokenSecret
|
|
361
393
|
});
|
|
362
394
|
const asset = await mux.video.assets.retrieve(assetId);
|
|
363
395
|
const { id: playbackId, policy } = getPlaybackId(asset);
|
|
@@ -440,10 +472,9 @@ function createToneSection(instruction) {
|
|
|
440
472
|
|
|
441
473
|
// src/lib/url-signing.ts
|
|
442
474
|
import Mux2 from "@mux/mux-node";
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
const
|
|
446
|
-
const keySecret = config.muxPrivateKey ?? env_default.MUX_PRIVATE_KEY;
|
|
475
|
+
function getMuxSigningContextFromEnv() {
|
|
476
|
+
const keyId = env_default.MUX_SIGNING_KEY;
|
|
477
|
+
const keySecret = env_default.MUX_PRIVATE_KEY;
|
|
447
478
|
if (!keyId || !keySecret) {
|
|
448
479
|
return void 0;
|
|
449
480
|
}
|
|
@@ -480,10 +511,11 @@ async function signUrl(url, playbackId, context, type = "video", params) {
|
|
|
480
511
|
|
|
481
512
|
// src/primitives/storyboards.ts
|
|
482
513
|
var DEFAULT_STORYBOARD_WIDTH = 640;
|
|
483
|
-
async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH,
|
|
514
|
+
async function getStoryboardUrl(playbackId, width = DEFAULT_STORYBOARD_WIDTH, shouldSign = false) {
|
|
484
515
|
"use step";
|
|
485
516
|
const baseUrl = `https://image.mux.com/${playbackId}/storyboard.png`;
|
|
486
|
-
if (
|
|
517
|
+
if (shouldSign) {
|
|
518
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
487
519
|
return signUrl(baseUrl, playbackId, signingContext, "storyboard", { width });
|
|
488
520
|
}
|
|
489
521
|
return `${baseUrl}?width=${width}`;
|
|
@@ -587,16 +619,11 @@ async function analyzeStoryboard({
|
|
|
587
619
|
imageDataUrl,
|
|
588
620
|
provider,
|
|
589
621
|
modelId,
|
|
590
|
-
credentials,
|
|
591
622
|
userPrompt,
|
|
592
623
|
systemPrompt
|
|
593
624
|
}) {
|
|
594
625
|
"use step";
|
|
595
|
-
const model = createLanguageModelFromConfig(
|
|
596
|
-
provider,
|
|
597
|
-
modelId,
|
|
598
|
-
credentials
|
|
599
|
-
);
|
|
626
|
+
const model = createLanguageModelFromConfig(provider, modelId);
|
|
600
627
|
const response = await generateObject({
|
|
601
628
|
model,
|
|
602
629
|
schema: burnedInCaptionsSchema,
|
|
@@ -641,14 +668,14 @@ async function hasBurnedInCaptions(assetId, options = {}) {
|
|
|
641
668
|
{ ...config, model },
|
|
642
669
|
provider
|
|
643
670
|
);
|
|
644
|
-
const { playbackId, policy } = await getPlaybackIdForAsset(
|
|
645
|
-
const signingContext =
|
|
671
|
+
const { playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
672
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
646
673
|
if (policy === "signed" && !signingContext) {
|
|
647
674
|
throw new Error(
|
|
648
675
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
649
676
|
);
|
|
650
677
|
}
|
|
651
|
-
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed"
|
|
678
|
+
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed");
|
|
652
679
|
let analysisResponse;
|
|
653
680
|
if (imageSubmissionMode === "base64") {
|
|
654
681
|
const base64Data = await fetchImageAsBase64(imageUrl, imageDownloadOptions);
|
|
@@ -656,7 +683,6 @@ async function hasBurnedInCaptions(assetId, options = {}) {
|
|
|
656
683
|
imageDataUrl: base64Data,
|
|
657
684
|
provider: workflowConfig.provider,
|
|
658
685
|
modelId: workflowConfig.modelId,
|
|
659
|
-
credentials: workflowConfig.credentials,
|
|
660
686
|
userPrompt,
|
|
661
687
|
systemPrompt: SYSTEM_PROMPT
|
|
662
688
|
});
|
|
@@ -665,7 +691,6 @@ async function hasBurnedInCaptions(assetId, options = {}) {
|
|
|
665
691
|
imageDataUrl: imageUrl,
|
|
666
692
|
provider: workflowConfig.provider,
|
|
667
693
|
modelId: workflowConfig.modelId,
|
|
668
|
-
credentials: workflowConfig.credentials,
|
|
669
694
|
userPrompt,
|
|
670
695
|
systemPrompt: SYSTEM_PROMPT
|
|
671
696
|
});
|
|
@@ -835,17 +860,18 @@ function parseVTTCues(vttContent) {
|
|
|
835
860
|
}
|
|
836
861
|
return cues;
|
|
837
862
|
}
|
|
838
|
-
async function buildTranscriptUrl(playbackId, trackId,
|
|
863
|
+
async function buildTranscriptUrl(playbackId, trackId, shouldSign = false) {
|
|
839
864
|
"use step";
|
|
840
865
|
const baseUrl = `https://stream.mux.com/${playbackId}/text/${trackId}.vtt`;
|
|
841
|
-
if (
|
|
866
|
+
if (shouldSign) {
|
|
867
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
842
868
|
return signUrl(baseUrl, playbackId, signingContext, "video");
|
|
843
869
|
}
|
|
844
870
|
return baseUrl;
|
|
845
871
|
}
|
|
846
872
|
async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
847
873
|
"use step";
|
|
848
|
-
const { languageCode, cleanTranscript = true,
|
|
874
|
+
const { languageCode, cleanTranscript = true, shouldSign } = options;
|
|
849
875
|
const track = findCaptionTrack(asset, languageCode);
|
|
850
876
|
if (!track) {
|
|
851
877
|
return { transcriptText: "" };
|
|
@@ -853,7 +879,7 @@ async function fetchTranscriptForAsset(asset, playbackId, options = {}) {
|
|
|
853
879
|
if (!track.id) {
|
|
854
880
|
return { transcriptText: "", track };
|
|
855
881
|
}
|
|
856
|
-
const transcriptUrl = await buildTranscriptUrl(playbackId, track.id,
|
|
882
|
+
const transcriptUrl = await buildTranscriptUrl(playbackId, track.id, shouldSign);
|
|
857
883
|
try {
|
|
858
884
|
const response = await fetch(transcriptUrl);
|
|
859
885
|
if (!response.ok) {
|
|
@@ -879,16 +905,11 @@ var chaptersSchema = z3.object({
|
|
|
879
905
|
async function generateChaptersWithAI({
|
|
880
906
|
provider,
|
|
881
907
|
modelId,
|
|
882
|
-
credentials,
|
|
883
908
|
timestampedTranscript,
|
|
884
909
|
systemPrompt
|
|
885
910
|
}) {
|
|
886
911
|
"use step";
|
|
887
|
-
const model = createLanguageModelFromConfig(
|
|
888
|
-
provider,
|
|
889
|
-
modelId,
|
|
890
|
-
credentials
|
|
891
|
-
);
|
|
912
|
+
const model = createLanguageModelFromConfig(provider, modelId);
|
|
892
913
|
const response = await withRetry(
|
|
893
914
|
() => generateObject2({
|
|
894
915
|
model,
|
|
@@ -931,8 +952,8 @@ async function generateChapters(assetId, languageCode, options = {}) {
|
|
|
931
952
|
"use workflow";
|
|
932
953
|
const { provider = "openai", model } = options;
|
|
933
954
|
const config = await createWorkflowConfig({ ...options, model }, provider);
|
|
934
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
935
|
-
const signingContext =
|
|
955
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
956
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
936
957
|
if (policy === "signed" && !signingContext) {
|
|
937
958
|
throw new Error(
|
|
938
959
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -942,7 +963,7 @@ async function generateChapters(assetId, languageCode, options = {}) {
|
|
|
942
963
|
languageCode,
|
|
943
964
|
cleanTranscript: false,
|
|
944
965
|
// keep timestamps for chapter segmentation
|
|
945
|
-
|
|
966
|
+
shouldSign: policy === "signed"
|
|
946
967
|
});
|
|
947
968
|
if (!transcriptResult.track || !transcriptResult.transcriptText) {
|
|
948
969
|
const availableLanguages = getReadyTextTracks(assetData).map((t) => t.language_code).filter(Boolean).join(", ");
|
|
@@ -959,7 +980,6 @@ async function generateChapters(assetId, languageCode, options = {}) {
|
|
|
959
980
|
chaptersData = await generateChaptersWithAI({
|
|
960
981
|
provider: config.provider,
|
|
961
982
|
modelId: config.modelId,
|
|
962
|
-
credentials: config.credentials,
|
|
963
983
|
timestampedTranscript,
|
|
964
984
|
systemPrompt: SYSTEM_PROMPT2
|
|
965
985
|
});
|
|
@@ -1093,11 +1113,10 @@ function averageEmbeddings(embeddings) {
|
|
|
1093
1113
|
async function generateSingleChunkEmbedding({
|
|
1094
1114
|
chunk,
|
|
1095
1115
|
provider,
|
|
1096
|
-
modelId
|
|
1097
|
-
credentials
|
|
1116
|
+
modelId
|
|
1098
1117
|
}) {
|
|
1099
1118
|
"use step";
|
|
1100
|
-
const model = createEmbeddingModelFromConfig(provider, modelId
|
|
1119
|
+
const model = createEmbeddingModelFromConfig(provider, modelId);
|
|
1101
1120
|
const response = await withRetry(
|
|
1102
1121
|
() => embed({
|
|
1103
1122
|
model,
|
|
@@ -1123,13 +1142,9 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
1123
1142
|
chunkingStrategy = { type: "token", maxTokens: 500, overlap: 100 },
|
|
1124
1143
|
batchSize = 5
|
|
1125
1144
|
} = options;
|
|
1126
|
-
const credentials = await validateCredentials(options, provider === "google" ? "google" : "openai");
|
|
1127
1145
|
const embeddingModel = resolveEmbeddingModel({ ...options, provider, model });
|
|
1128
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
1129
|
-
|
|
1130
|
-
assetId
|
|
1131
|
-
);
|
|
1132
|
-
const signingContext = await resolveSigningContext(options);
|
|
1146
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1147
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1133
1148
|
if (policy === "signed" && !signingContext) {
|
|
1134
1149
|
throw new Error(
|
|
1135
1150
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -1139,7 +1154,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
1139
1154
|
const transcriptResult = await fetchTranscriptForAsset(assetData, playbackId, {
|
|
1140
1155
|
languageCode,
|
|
1141
1156
|
cleanTranscript: !useVttChunking,
|
|
1142
|
-
|
|
1157
|
+
shouldSign: policy === "signed"
|
|
1143
1158
|
});
|
|
1144
1159
|
if (!transcriptResult.track || !transcriptResult.transcriptText) {
|
|
1145
1160
|
const availableLanguages = getReadyTextTracks(assetData).map((t) => t.language_code).filter(Boolean).join(", ");
|
|
@@ -1168,8 +1183,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
1168
1183
|
(chunk) => generateSingleChunkEmbedding({
|
|
1169
1184
|
chunk,
|
|
1170
1185
|
provider: embeddingModel.provider,
|
|
1171
|
-
modelId: embeddingModel.modelId
|
|
1172
|
-
credentials
|
|
1186
|
+
modelId: embeddingModel.modelId
|
|
1173
1187
|
})
|
|
1174
1188
|
)
|
|
1175
1189
|
);
|
|
@@ -1204,7 +1218,7 @@ async function generateVideoEmbeddings(assetId, options = {}) {
|
|
|
1204
1218
|
// src/primitives/thumbnails.ts
|
|
1205
1219
|
async function getThumbnailUrls(playbackId, duration, options = {}) {
|
|
1206
1220
|
"use step";
|
|
1207
|
-
const { interval = 10, width = 640,
|
|
1221
|
+
const { interval = 10, width = 640, shouldSign = false } = options;
|
|
1208
1222
|
const timestamps = [];
|
|
1209
1223
|
if (duration <= 50) {
|
|
1210
1224
|
const spacing = duration / 6;
|
|
@@ -1218,7 +1232,8 @@ async function getThumbnailUrls(playbackId, duration, options = {}) {
|
|
|
1218
1232
|
}
|
|
1219
1233
|
const baseUrl = `https://image.mux.com/${playbackId}/thumbnail.png`;
|
|
1220
1234
|
const urlPromises = timestamps.map(async (time) => {
|
|
1221
|
-
if (
|
|
1235
|
+
if (shouldSign) {
|
|
1236
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1222
1237
|
return signUrl(baseUrl, playbackId, signingContext, "thumbnail", { time, width });
|
|
1223
1238
|
}
|
|
1224
1239
|
return `${baseUrl}?time=${time}&width=${width}`;
|
|
@@ -1279,19 +1294,20 @@ async function processConcurrently(items, processor, maxConcurrent = 5) {
|
|
|
1279
1294
|
}
|
|
1280
1295
|
return results;
|
|
1281
1296
|
}
|
|
1282
|
-
async function requestOpenAIModeration(imageUrls,
|
|
1297
|
+
async function requestOpenAIModeration(imageUrls, model, maxConcurrent = 5, submissionMode = "url", downloadOptions) {
|
|
1283
1298
|
"use step";
|
|
1284
1299
|
const targetUrls = submissionMode === "base64" ? (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map(
|
|
1285
|
-
(img) => ({ url: img.url, image: img.base64Data,
|
|
1286
|
-
) : imageUrls.map((url) => ({ url, image: url,
|
|
1300
|
+
(img) => ({ url: img.url, image: img.base64Data, model })
|
|
1301
|
+
) : imageUrls.map((url) => ({ url, image: url, model }));
|
|
1287
1302
|
const moderate = async (entry) => {
|
|
1288
1303
|
"use step";
|
|
1304
|
+
const apiKey = getApiKeyFromEnv("openai");
|
|
1289
1305
|
try {
|
|
1290
1306
|
const res = await fetch("https://api.openai.com/v1/moderations", {
|
|
1291
1307
|
method: "POST",
|
|
1292
1308
|
headers: {
|
|
1293
1309
|
"Content-Type": "application/json",
|
|
1294
|
-
"Authorization": `Bearer ${
|
|
1310
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1295
1311
|
},
|
|
1296
1312
|
body: JSON.stringify({
|
|
1297
1313
|
model: entry.model,
|
|
@@ -1337,7 +1353,7 @@ function getHiveCategoryScores(classes, categoryNames) {
|
|
|
1337
1353
|
const scores = categoryNames.map((category) => scoreMap[category] || 0);
|
|
1338
1354
|
return Math.max(...scores, 0);
|
|
1339
1355
|
}
|
|
1340
|
-
async function requestHiveModeration(imageUrls,
|
|
1356
|
+
async function requestHiveModeration(imageUrls, maxConcurrent = 5, submissionMode = "url", downloadOptions) {
|
|
1341
1357
|
"use step";
|
|
1342
1358
|
const targets = submissionMode === "base64" ? (await downloadImagesAsBase64(imageUrls, downloadOptions, maxConcurrent)).map((img) => ({
|
|
1343
1359
|
url: img.url,
|
|
@@ -1352,6 +1368,7 @@ async function requestHiveModeration(imageUrls, apiKey, maxConcurrent = 5, submi
|
|
|
1352
1368
|
}));
|
|
1353
1369
|
const moderate = async (entry) => {
|
|
1354
1370
|
"use step";
|
|
1371
|
+
const apiKey = getApiKeyFromEnv("hive");
|
|
1355
1372
|
try {
|
|
1356
1373
|
const formData = new FormData();
|
|
1357
1374
|
if (entry.source.kind === "url") {
|
|
@@ -1408,10 +1425,9 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
1408
1425
|
imageSubmissionMode = "url",
|
|
1409
1426
|
imageDownloadOptions
|
|
1410
1427
|
} = options;
|
|
1411
|
-
const
|
|
1412
|
-
const { asset, playbackId, policy } = await getPlaybackIdForAsset(credentials, assetId);
|
|
1428
|
+
const { asset, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1413
1429
|
const duration = asset.duration || 0;
|
|
1414
|
-
const signingContext =
|
|
1430
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1415
1431
|
if (policy === "signed" && !signingContext) {
|
|
1416
1432
|
throw new Error(
|
|
1417
1433
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -1420,30 +1436,20 @@ async function getModerationScores(assetId, options = {}) {
|
|
|
1420
1436
|
const thumbnailUrls = await getThumbnailUrls(playbackId, duration, {
|
|
1421
1437
|
interval: thumbnailInterval,
|
|
1422
1438
|
width: thumbnailWidth,
|
|
1423
|
-
|
|
1439
|
+
shouldSign: policy === "signed"
|
|
1424
1440
|
});
|
|
1425
1441
|
let thumbnailScores;
|
|
1426
1442
|
if (provider === "openai") {
|
|
1427
|
-
const apiKey = credentials.openaiApiKey;
|
|
1428
|
-
if (!apiKey) {
|
|
1429
|
-
throw new Error("OpenAI API key is required for moderation. Set OPENAI_API_KEY or pass openaiApiKey.");
|
|
1430
|
-
}
|
|
1431
1443
|
thumbnailScores = await requestOpenAIModeration(
|
|
1432
1444
|
thumbnailUrls,
|
|
1433
|
-
apiKey,
|
|
1434
1445
|
model || "omni-moderation-latest",
|
|
1435
1446
|
maxConcurrent,
|
|
1436
1447
|
imageSubmissionMode,
|
|
1437
1448
|
imageDownloadOptions
|
|
1438
1449
|
);
|
|
1439
1450
|
} else if (provider === "hive") {
|
|
1440
|
-
const hiveApiKey = options.hiveApiKey || env_default.HIVE_API_KEY;
|
|
1441
|
-
if (!hiveApiKey) {
|
|
1442
|
-
throw new Error("Hive API key is required for moderation. Set HIVE_API_KEY or pass hiveApiKey.");
|
|
1443
|
-
}
|
|
1444
1451
|
thumbnailScores = await requestHiveModeration(
|
|
1445
1452
|
thumbnailUrls,
|
|
1446
|
-
hiveApiKey,
|
|
1447
1453
|
maxConcurrent,
|
|
1448
1454
|
imageSubmissionMode,
|
|
1449
1455
|
imageDownloadOptions
|
|
@@ -1477,8 +1483,8 @@ var summarySchema = z4.object({
|
|
|
1477
1483
|
description: z4.string()
|
|
1478
1484
|
});
|
|
1479
1485
|
var TONE_INSTRUCTIONS = {
|
|
1480
|
-
|
|
1481
|
-
|
|
1486
|
+
neutral: "Provide a clear, straightforward analysis.",
|
|
1487
|
+
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!",
|
|
1482
1488
|
professional: "Provide a professional, executive-level analysis suitable for business reporting."
|
|
1483
1489
|
};
|
|
1484
1490
|
var summarizationPromptBuilder = createPromptBuilder({
|
|
@@ -1594,13 +1600,9 @@ function buildUserPrompt2({
|
|
|
1594
1600
|
}
|
|
1595
1601
|
return summarizationPromptBuilder.buildWithContext(promptOverrides, contextSections);
|
|
1596
1602
|
}
|
|
1597
|
-
async function analyzeStoryboard2(imageDataUrl,
|
|
1603
|
+
async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, systemPrompt) {
|
|
1598
1604
|
"use step";
|
|
1599
|
-
const model = createLanguageModelFromConfig(
|
|
1600
|
-
workflowConfig.provider,
|
|
1601
|
-
workflowConfig.modelId,
|
|
1602
|
-
workflowConfig.credentials
|
|
1603
|
-
);
|
|
1605
|
+
const model = createLanguageModelFromConfig(provider, modelId);
|
|
1604
1606
|
const response = await generateObject3({
|
|
1605
1607
|
model,
|
|
1606
1608
|
schema: summarySchema,
|
|
@@ -1657,7 +1659,7 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1657
1659
|
const {
|
|
1658
1660
|
provider = "openai",
|
|
1659
1661
|
model,
|
|
1660
|
-
tone = "
|
|
1662
|
+
tone = "neutral",
|
|
1661
1663
|
includeTranscript = true,
|
|
1662
1664
|
cleanTranscript = true,
|
|
1663
1665
|
imageSubmissionMode = "url",
|
|
@@ -1669,8 +1671,8 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1669
1671
|
{ ...options, model },
|
|
1670
1672
|
provider
|
|
1671
1673
|
);
|
|
1672
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
1673
|
-
const signingContext =
|
|
1674
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
1675
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
1674
1676
|
if (policy === "signed" && !signingContext) {
|
|
1675
1677
|
throw new Error(
|
|
1676
1678
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -1678,7 +1680,7 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1678
1680
|
}
|
|
1679
1681
|
const transcriptText = includeTranscript ? (await fetchTranscriptForAsset(assetData, playbackId, {
|
|
1680
1682
|
cleanTranscript,
|
|
1681
|
-
|
|
1683
|
+
shouldSign: policy === "signed"
|
|
1682
1684
|
})).transcriptText : "";
|
|
1683
1685
|
const userPrompt = buildUserPrompt2({
|
|
1684
1686
|
tone,
|
|
@@ -1686,19 +1688,20 @@ async function getSummaryAndTags(assetId, options) {
|
|
|
1686
1688
|
isCleanTranscript: cleanTranscript,
|
|
1687
1689
|
promptOverrides
|
|
1688
1690
|
});
|
|
1689
|
-
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed"
|
|
1691
|
+
const imageUrl = await getStoryboardUrl(playbackId, 640, policy === "signed");
|
|
1690
1692
|
let analysisResponse;
|
|
1691
1693
|
try {
|
|
1692
1694
|
if (imageSubmissionMode === "base64") {
|
|
1693
1695
|
const downloadResult = await downloadImageAsBase64(imageUrl, imageDownloadOptions);
|
|
1694
1696
|
analysisResponse = await analyzeStoryboard2(
|
|
1695
1697
|
downloadResult.base64Data,
|
|
1696
|
-
config,
|
|
1698
|
+
config.provider,
|
|
1699
|
+
config.modelId,
|
|
1697
1700
|
userPrompt,
|
|
1698
1701
|
SYSTEM_PROMPT3
|
|
1699
1702
|
);
|
|
1700
1703
|
} else {
|
|
1701
|
-
analysisResponse = await withRetry(() => analyzeStoryboard2(imageUrl, config, userPrompt, SYSTEM_PROMPT3));
|
|
1704
|
+
analysisResponse = await withRetry(() => analyzeStoryboard2(imageUrl, config.provider, config.modelId, userPrompt, SYSTEM_PROMPT3));
|
|
1702
1705
|
}
|
|
1703
1706
|
} catch (error) {
|
|
1704
1707
|
throw new Error(
|
|
@@ -1903,11 +1906,12 @@ function getReadyAudioStaticRendition(asset) {
|
|
|
1903
1906
|
);
|
|
1904
1907
|
}
|
|
1905
1908
|
var hasReadyAudioStaticRendition = (asset) => Boolean(getReadyAudioStaticRendition(asset));
|
|
1906
|
-
async function requestStaticRenditionCreation(
|
|
1909
|
+
async function requestStaticRenditionCreation(assetId) {
|
|
1907
1910
|
"use step";
|
|
1911
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
1908
1912
|
const mux = new Mux3({
|
|
1909
|
-
tokenId:
|
|
1910
|
-
tokenSecret:
|
|
1913
|
+
tokenId: muxTokenId,
|
|
1914
|
+
tokenSecret: muxTokenSecret
|
|
1911
1915
|
});
|
|
1912
1916
|
try {
|
|
1913
1917
|
await mux.video.assets.createStaticRendition(assetId, {
|
|
@@ -1926,13 +1930,13 @@ async function requestStaticRenditionCreation(credentials, assetId) {
|
|
|
1926
1930
|
}
|
|
1927
1931
|
async function waitForAudioStaticRendition({
|
|
1928
1932
|
assetId,
|
|
1929
|
-
credentials,
|
|
1930
1933
|
initialAsset
|
|
1931
1934
|
}) {
|
|
1932
1935
|
"use step";
|
|
1936
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
1933
1937
|
const mux = new Mux3({
|
|
1934
|
-
tokenId:
|
|
1935
|
-
tokenSecret:
|
|
1938
|
+
tokenId: muxTokenId,
|
|
1939
|
+
tokenSecret: muxTokenSecret
|
|
1936
1940
|
});
|
|
1937
1941
|
let currentAsset = initialAsset;
|
|
1938
1942
|
if (hasReadyAudioStaticRendition(currentAsset)) {
|
|
@@ -1940,9 +1944,9 @@ async function waitForAudioStaticRendition({
|
|
|
1940
1944
|
}
|
|
1941
1945
|
const status = currentAsset.static_renditions?.status ?? "not_requested";
|
|
1942
1946
|
if (status === "not_requested" || status === void 0) {
|
|
1943
|
-
await requestStaticRenditionCreation(
|
|
1947
|
+
await requestStaticRenditionCreation(assetId);
|
|
1944
1948
|
} else if (status === "errored") {
|
|
1945
|
-
await requestStaticRenditionCreation(
|
|
1949
|
+
await requestStaticRenditionCreation(assetId);
|
|
1946
1950
|
} else {
|
|
1947
1951
|
console.warn(`\u2139\uFE0F Static rendition already ${status}. Waiting for it to finish...`);
|
|
1948
1952
|
}
|
|
@@ -1978,10 +1982,10 @@ async function createElevenLabsDubbingJob({
|
|
|
1978
1982
|
audioBuffer,
|
|
1979
1983
|
assetId,
|
|
1980
1984
|
elevenLabsLangCode,
|
|
1981
|
-
elevenLabsApiKey,
|
|
1982
1985
|
numSpeakers
|
|
1983
1986
|
}) {
|
|
1984
1987
|
"use step";
|
|
1988
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
1985
1989
|
const audioBlob = new Blob([audioBuffer], { type: "audio/mp4" });
|
|
1986
1990
|
const formData = new FormData();
|
|
1987
1991
|
formData.append("file", audioBlob);
|
|
@@ -2002,10 +2006,10 @@ async function createElevenLabsDubbingJob({
|
|
|
2002
2006
|
return dubbingData.dubbing_id;
|
|
2003
2007
|
}
|
|
2004
2008
|
async function checkElevenLabsDubbingStatus({
|
|
2005
|
-
dubbingId
|
|
2006
|
-
elevenLabsApiKey
|
|
2009
|
+
dubbingId
|
|
2007
2010
|
}) {
|
|
2008
2011
|
"use step";
|
|
2012
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2009
2013
|
const statusResponse = await fetch(`https://api.elevenlabs.io/v1/dubbing/${dubbingId}`, {
|
|
2010
2014
|
headers: {
|
|
2011
2015
|
"xi-api-key": elevenLabsApiKey
|
|
@@ -2022,10 +2026,10 @@ async function checkElevenLabsDubbingStatus({
|
|
|
2022
2026
|
}
|
|
2023
2027
|
async function downloadDubbedAudioFromElevenLabs({
|
|
2024
2028
|
dubbingId,
|
|
2025
|
-
languageCode
|
|
2026
|
-
elevenLabsApiKey
|
|
2029
|
+
languageCode
|
|
2027
2030
|
}) {
|
|
2028
2031
|
"use step";
|
|
2032
|
+
const elevenLabsApiKey = getApiKeyFromEnv("elevenlabs");
|
|
2029
2033
|
const audioUrl = `https://api.elevenlabs.io/v1/dubbing/${dubbingId}/audio/${languageCode}`;
|
|
2030
2034
|
const audioResponse = await fetch(audioUrl, {
|
|
2031
2035
|
headers: {
|
|
@@ -2043,14 +2047,14 @@ async function uploadDubbedAudioToS3({
|
|
|
2043
2047
|
toLanguageCode,
|
|
2044
2048
|
s3Endpoint,
|
|
2045
2049
|
s3Region,
|
|
2046
|
-
s3Bucket
|
|
2047
|
-
s3AccessKeyId,
|
|
2048
|
-
s3SecretAccessKey
|
|
2050
|
+
s3Bucket
|
|
2049
2051
|
}) {
|
|
2050
2052
|
"use step";
|
|
2051
2053
|
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
2052
2054
|
const { Upload } = await import("@aws-sdk/lib-storage");
|
|
2053
2055
|
const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
|
|
2056
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2057
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2054
2058
|
const s3Client = new S3Client({
|
|
2055
2059
|
region: s3Region,
|
|
2056
2060
|
endpoint: s3Endpoint,
|
|
@@ -2083,11 +2087,12 @@ async function uploadDubbedAudioToS3({
|
|
|
2083
2087
|
console.warn(`\u{1F517} Generated presigned URL (expires in 1 hour)`);
|
|
2084
2088
|
return presignedUrl;
|
|
2085
2089
|
}
|
|
2086
|
-
async function createAudioTrackOnMux(
|
|
2090
|
+
async function createAudioTrackOnMux(assetId, languageCode, presignedUrl) {
|
|
2087
2091
|
"use step";
|
|
2092
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
2088
2093
|
const mux = new Mux3({
|
|
2089
|
-
tokenId:
|
|
2090
|
-
tokenSecret:
|
|
2094
|
+
tokenId: muxTokenId,
|
|
2095
|
+
tokenSecret: muxTokenSecret
|
|
2091
2096
|
});
|
|
2092
2097
|
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(languageCode) || languageCode.toUpperCase();
|
|
2093
2098
|
const trackName = `${languageName} (auto-dubbed)`;
|
|
@@ -2114,21 +2119,20 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2114
2119
|
if (provider !== "elevenlabs") {
|
|
2115
2120
|
throw new Error("Only ElevenLabs provider is currently supported for audio translation");
|
|
2116
2121
|
}
|
|
2117
|
-
const credentials = await validateCredentials(options);
|
|
2118
2122
|
const elevenLabsKey = elevenLabsApiKey ?? env_default.ELEVENLABS_API_KEY;
|
|
2119
2123
|
const s3Endpoint = options.s3Endpoint ?? env_default.S3_ENDPOINT;
|
|
2120
2124
|
const s3Region = options.s3Region ?? env_default.S3_REGION ?? "auto";
|
|
2121
2125
|
const s3Bucket = options.s3Bucket ?? env_default.S3_BUCKET;
|
|
2122
|
-
const s3AccessKeyId =
|
|
2123
|
-
const s3SecretAccessKey =
|
|
2126
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2127
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2124
2128
|
if (!elevenLabsKey) {
|
|
2125
2129
|
throw new Error("ElevenLabs API key is required. Provide elevenLabsApiKey in options or set ELEVENLABS_API_KEY environment variable.");
|
|
2126
2130
|
}
|
|
2127
2131
|
if (uploadToMux && (!s3Endpoint || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey)) {
|
|
2128
2132
|
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.");
|
|
2129
2133
|
}
|
|
2130
|
-
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(
|
|
2131
|
-
const signingContext =
|
|
2134
|
+
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
2135
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
2132
2136
|
if (policy === "signed" && !signingContext) {
|
|
2133
2137
|
throw new Error(
|
|
2134
2138
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -2139,7 +2143,6 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2139
2143
|
console.warn("\u274C No ready audio static rendition found. Requesting one now...");
|
|
2140
2144
|
currentAsset = await waitForAudioStaticRendition({
|
|
2141
2145
|
assetId,
|
|
2142
|
-
credentials,
|
|
2143
2146
|
initialAsset: currentAsset
|
|
2144
2147
|
});
|
|
2145
2148
|
}
|
|
@@ -2169,7 +2172,6 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2169
2172
|
audioBuffer,
|
|
2170
2173
|
assetId,
|
|
2171
2174
|
elevenLabsLangCode,
|
|
2172
|
-
elevenLabsApiKey: elevenLabsKey,
|
|
2173
2175
|
numSpeakers
|
|
2174
2176
|
});
|
|
2175
2177
|
console.warn(`\u2705 Dubbing job created with ID: ${dubbingId}`);
|
|
@@ -2186,8 +2188,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2186
2188
|
pollAttempts++;
|
|
2187
2189
|
try {
|
|
2188
2190
|
const statusResult = await checkElevenLabsDubbingStatus({
|
|
2189
|
-
dubbingId
|
|
2190
|
-
elevenLabsApiKey: elevenLabsKey
|
|
2191
|
+
dubbingId
|
|
2191
2192
|
});
|
|
2192
2193
|
dubbingStatus = statusResult.status;
|
|
2193
2194
|
targetLanguages = statusResult.targetLanguages;
|
|
@@ -2230,8 +2231,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2230
2231
|
}
|
|
2231
2232
|
dubbedAudioBuffer = await downloadDubbedAudioFromElevenLabs({
|
|
2232
2233
|
dubbingId,
|
|
2233
|
-
languageCode: downloadLangCode
|
|
2234
|
-
elevenLabsApiKey: elevenLabsKey
|
|
2234
|
+
languageCode: downloadLangCode
|
|
2235
2235
|
});
|
|
2236
2236
|
console.warn("\u2705 Dubbed audio downloaded successfully!");
|
|
2237
2237
|
} catch (error) {
|
|
@@ -2246,9 +2246,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2246
2246
|
toLanguageCode,
|
|
2247
2247
|
s3Endpoint,
|
|
2248
2248
|
s3Region,
|
|
2249
|
-
s3Bucket
|
|
2250
|
-
s3AccessKeyId,
|
|
2251
|
-
s3SecretAccessKey
|
|
2249
|
+
s3Bucket
|
|
2252
2250
|
});
|
|
2253
2251
|
} catch (error) {
|
|
2254
2252
|
throw new Error(`Failed to upload audio to S3: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -2257,7 +2255,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
2257
2255
|
let uploadedTrackId;
|
|
2258
2256
|
const muxLangCode = toISO639_1(toLanguageCode);
|
|
2259
2257
|
try {
|
|
2260
|
-
uploadedTrackId = await createAudioTrackOnMux(
|
|
2258
|
+
uploadedTrackId = await createAudioTrackOnMux(assetId, muxLangCode, presignedUrl);
|
|
2261
2259
|
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
|
|
2262
2260
|
const trackName = `${languageName} (auto-dubbed)`;
|
|
2263
2261
|
console.warn(`\u2705 Track added to Mux asset with ID: ${uploadedTrackId}`);
|
|
@@ -2299,15 +2297,10 @@ async function translateVttWithAI({
|
|
|
2299
2297
|
toLanguageCode,
|
|
2300
2298
|
provider,
|
|
2301
2299
|
modelId,
|
|
2302
|
-
credentials,
|
|
2303
2300
|
abortSignal
|
|
2304
2301
|
}) {
|
|
2305
2302
|
"use step";
|
|
2306
|
-
const languageModel = createLanguageModelFromConfig(
|
|
2307
|
-
provider,
|
|
2308
|
-
modelId,
|
|
2309
|
-
credentials
|
|
2310
|
-
);
|
|
2303
|
+
const languageModel = createLanguageModelFromConfig(provider, modelId);
|
|
2311
2304
|
const response = await generateObject4({
|
|
2312
2305
|
model: languageModel,
|
|
2313
2306
|
schema: translationSchema,
|
|
@@ -2339,14 +2332,14 @@ async function uploadVttToS3({
|
|
|
2339
2332
|
toLanguageCode,
|
|
2340
2333
|
s3Endpoint,
|
|
2341
2334
|
s3Region,
|
|
2342
|
-
s3Bucket
|
|
2343
|
-
s3AccessKeyId,
|
|
2344
|
-
s3SecretAccessKey
|
|
2335
|
+
s3Bucket
|
|
2345
2336
|
}) {
|
|
2346
2337
|
"use step";
|
|
2347
2338
|
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
2348
2339
|
const { Upload } = await import("@aws-sdk/lib-storage");
|
|
2349
2340
|
const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
|
|
2341
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2342
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2350
2343
|
const s3Client = new S3Client({
|
|
2351
2344
|
region: s3Region,
|
|
2352
2345
|
endpoint: s3Endpoint,
|
|
@@ -2377,11 +2370,12 @@ async function uploadVttToS3({
|
|
|
2377
2370
|
});
|
|
2378
2371
|
return presignedUrl;
|
|
2379
2372
|
}
|
|
2380
|
-
async function createTextTrackOnMux(
|
|
2373
|
+
async function createTextTrackOnMux(assetId, languageCode, trackName, presignedUrl) {
|
|
2381
2374
|
"use step";
|
|
2375
|
+
const { muxTokenId, muxTokenSecret } = getMuxCredentialsFromEnv();
|
|
2382
2376
|
const mux = new Mux4({
|
|
2383
|
-
tokenId:
|
|
2384
|
-
tokenSecret:
|
|
2377
|
+
tokenId: muxTokenId,
|
|
2378
|
+
tokenSecret: muxTokenSecret
|
|
2385
2379
|
});
|
|
2386
2380
|
const trackResponse = await mux.video.assets.createTrack(assetId, {
|
|
2387
2381
|
type: "text",
|
|
@@ -2403,15 +2397,13 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2403
2397
|
s3Endpoint: providedS3Endpoint,
|
|
2404
2398
|
s3Region: providedS3Region,
|
|
2405
2399
|
s3Bucket: providedS3Bucket,
|
|
2406
|
-
s3AccessKeyId: providedS3AccessKeyId,
|
|
2407
|
-
s3SecretAccessKey: providedS3SecretAccessKey,
|
|
2408
2400
|
uploadToMux: uploadToMuxOption
|
|
2409
2401
|
} = options;
|
|
2410
2402
|
const s3Endpoint = providedS3Endpoint ?? env_default.S3_ENDPOINT;
|
|
2411
2403
|
const s3Region = providedS3Region ?? env_default.S3_REGION ?? "auto";
|
|
2412
2404
|
const s3Bucket = providedS3Bucket ?? env_default.S3_BUCKET;
|
|
2413
|
-
const s3AccessKeyId =
|
|
2414
|
-
const s3SecretAccessKey =
|
|
2405
|
+
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
2406
|
+
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
2415
2407
|
const uploadToMux = uploadToMuxOption !== false;
|
|
2416
2408
|
const config = await createWorkflowConfig(
|
|
2417
2409
|
{ ...options, model },
|
|
@@ -2420,8 +2412,8 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2420
2412
|
if (uploadToMux && (!s3Endpoint || !s3Bucket || !s3AccessKeyId || !s3SecretAccessKey)) {
|
|
2421
2413
|
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.");
|
|
2422
2414
|
}
|
|
2423
|
-
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(
|
|
2424
|
-
const signingContext =
|
|
2415
|
+
const { asset: assetData, playbackId, policy } = await getPlaybackIdForAsset(assetId);
|
|
2416
|
+
const signingContext = getMuxSigningContextFromEnv();
|
|
2425
2417
|
if (policy === "signed" && !signingContext) {
|
|
2426
2418
|
throw new Error(
|
|
2427
2419
|
"Signed playback ID requires signing credentials. Provide muxSigningKey and muxPrivateKey in options or set MUX_SIGNING_KEY and MUX_PRIVATE_KEY environment variables."
|
|
@@ -2455,7 +2447,6 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2455
2447
|
toLanguageCode,
|
|
2456
2448
|
provider: config.provider,
|
|
2457
2449
|
modelId: config.modelId,
|
|
2458
|
-
credentials: config.credentials,
|
|
2459
2450
|
abortSignal: options.abortSignal
|
|
2460
2451
|
});
|
|
2461
2452
|
translatedVtt = result.translatedVtt;
|
|
@@ -2486,9 +2477,7 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2486
2477
|
toLanguageCode,
|
|
2487
2478
|
s3Endpoint,
|
|
2488
2479
|
s3Region,
|
|
2489
|
-
s3Bucket
|
|
2490
|
-
s3AccessKeyId,
|
|
2491
|
-
s3SecretAccessKey
|
|
2480
|
+
s3Bucket
|
|
2492
2481
|
});
|
|
2493
2482
|
} catch (error) {
|
|
2494
2483
|
throw new Error(`Failed to upload VTT to S3: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
@@ -2497,7 +2486,7 @@ async function translateCaptions(assetId, fromLanguageCode, toLanguageCode, opti
|
|
|
2497
2486
|
try {
|
|
2498
2487
|
const languageName = getLanguageName(toLanguageCode);
|
|
2499
2488
|
const trackName = `${languageName} (auto-translated)`;
|
|
2500
|
-
uploadedTrackId = await createTextTrackOnMux(
|
|
2489
|
+
uploadedTrackId = await createTextTrackOnMux(assetId, toLanguageCode, trackName, presignedUrl);
|
|
2501
2490
|
} catch (error) {
|
|
2502
2491
|
console.warn(`Failed to add track to Mux asset: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2503
2492
|
}
|