@mux/ai 0.39.0 → 0.41.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/dist/{index-Wjbq-_Z0.d.ts → index-C3PW73qH.d.ts} +206 -12
- package/dist/index.d.ts +2 -2
- package/dist/index.js +435 -122
- package/dist/index.js.map +1 -1
- package/dist/workflows/index.d.ts +1 -1
- package/dist/workflows/index.js +397 -97
- package/dist/workflows/index.js.map +1 -1
- package/package.json +17 -3
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// package.json
|
|
8
|
-
var version = "0.
|
|
8
|
+
var version = "0.41.0";
|
|
9
9
|
|
|
10
10
|
// src/lib/language-codes.ts
|
|
11
11
|
var ISO639_1_TO_3 = {
|
|
@@ -1113,6 +1113,234 @@ async function resolveMuxSigningContext(credentials) {
|
|
|
1113
1113
|
return { keyId, keySecret };
|
|
1114
1114
|
}
|
|
1115
1115
|
|
|
1116
|
+
// src/lib/mux-tracks.ts
|
|
1117
|
+
var MUX_TRACK_PASSTHROUGH_MAX_CHARS = 255;
|
|
1118
|
+
function normalizeTrackLanguageCode(code) {
|
|
1119
|
+
if (!code) {
|
|
1120
|
+
return void 0;
|
|
1121
|
+
}
|
|
1122
|
+
const primarySubtag = code.trim().toLowerCase().split(/[-_]/)[0];
|
|
1123
|
+
if (!primarySubtag || primarySubtag === "auto" || isUndeterminedLanguageCode(primarySubtag)) {
|
|
1124
|
+
return void 0;
|
|
1125
|
+
}
|
|
1126
|
+
return toISO639_1(primarySubtag);
|
|
1127
|
+
}
|
|
1128
|
+
function normalizeTrackName(name) {
|
|
1129
|
+
const normalized = name?.trim().toLowerCase();
|
|
1130
|
+
return normalized || void 0;
|
|
1131
|
+
}
|
|
1132
|
+
function isMuxGeneratedTextTrack(track) {
|
|
1133
|
+
return track.text_source === "generated_vod" || track.text_source === "generated_live" || track.text_source === "generated_live_final";
|
|
1134
|
+
}
|
|
1135
|
+
function targetType(target) {
|
|
1136
|
+
return target.type ?? "text";
|
|
1137
|
+
}
|
|
1138
|
+
function listTracks(asset, type) {
|
|
1139
|
+
return (asset.tracks ?? []).filter((track) => track.type === type && track.status !== "deleted" && !!track.id);
|
|
1140
|
+
}
|
|
1141
|
+
function summarizeTextTrack(track) {
|
|
1142
|
+
return {
|
|
1143
|
+
id: track.id,
|
|
1144
|
+
type: track.type === "audio" ? "audio" : "text",
|
|
1145
|
+
name: track.name,
|
|
1146
|
+
languageCode: track.language_code,
|
|
1147
|
+
status: track.status,
|
|
1148
|
+
textSource: track.text_source,
|
|
1149
|
+
passthrough: track.passthrough,
|
|
1150
|
+
...track.type === "audio" ? { primary: track.primary === true } : {}
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
function findSameLanguageTextTracks(asset, target) {
|
|
1154
|
+
const targetLanguage = normalizeTrackLanguageCode(target.languageCode);
|
|
1155
|
+
if (!targetLanguage) {
|
|
1156
|
+
return [];
|
|
1157
|
+
}
|
|
1158
|
+
const type = targetType(target);
|
|
1159
|
+
return listTracks(asset, type).filter(
|
|
1160
|
+
(track) => (type === "audio" || track.text_type === "subtitles") && normalizeTrackLanguageCode(track.language_code) === targetLanguage
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
function findNameCollisionTextTracks(asset, target) {
|
|
1164
|
+
const targetName = normalizeTrackName(target.name);
|
|
1165
|
+
if (!targetName) {
|
|
1166
|
+
return [];
|
|
1167
|
+
}
|
|
1168
|
+
return listTracks(asset, targetType(target)).filter((track) => normalizeTrackName(track.name) === targetName);
|
|
1169
|
+
}
|
|
1170
|
+
function describeTracks(tracks) {
|
|
1171
|
+
return tracks.map((track) => `${track.name ?? "(unnamed)"} [${track.language_code ?? "?"}, ${track.type === "audio" ? track.primary ? "primary audio" : "audio" : track.text_source ?? "unknown source"}, ${track.status ?? "ready"}]`).join("; ");
|
|
1172
|
+
}
|
|
1173
|
+
function planTextTrackReplacement(asset, target, policy, options = {}) {
|
|
1174
|
+
const label = targetType(target) === "audio" ? "Audio" : "Text";
|
|
1175
|
+
const keep = new Set(options.keepTrackIds ?? []);
|
|
1176
|
+
const conflicts = /* @__PURE__ */ new Map();
|
|
1177
|
+
for (const track of [...findSameLanguageTextTracks(asset, target), ...findNameCollisionTextTracks(asset, target)]) {
|
|
1178
|
+
if (!keep.has(track.id)) {
|
|
1179
|
+
conflicts.set(track.id, track);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
const conflicting = [...conflicts.values()];
|
|
1183
|
+
if (conflicting.length === 0) {
|
|
1184
|
+
return { kind: "clear" };
|
|
1185
|
+
}
|
|
1186
|
+
if (policy === "fail") {
|
|
1187
|
+
return {
|
|
1188
|
+
kind: "blocked",
|
|
1189
|
+
reason: `${label} track(s) already exist for language '${target.languageCode}' or name '${target.name}': ${describeTracks(conflicting)}. Set replaceExistingTracks to replace them.`,
|
|
1190
|
+
tracks: conflicting.map(summarizeTextTrack)
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
const primary = conflicting.filter((track) => track.type === "audio" && track.primary === true);
|
|
1194
|
+
if (primary.length > 0) {
|
|
1195
|
+
return {
|
|
1196
|
+
kind: "blocked",
|
|
1197
|
+
reason: `The asset's primary audio track is in the way for language '${target.languageCode}' or name '${target.name}': ${describeTracks(primary)}. Primary audio cannot be replaced; choose a different trackName.`,
|
|
1198
|
+
tracks: primary.map(summarizeTextTrack)
|
|
1199
|
+
};
|
|
1200
|
+
}
|
|
1201
|
+
if (policy === "replace_generated") {
|
|
1202
|
+
const notGenerated = conflicting.filter((track) => track.type !== "text" || !isMuxGeneratedTextTrack(track));
|
|
1203
|
+
if (notGenerated.length > 0) {
|
|
1204
|
+
return {
|
|
1205
|
+
kind: "blocked",
|
|
1206
|
+
reason: `${label} track(s) that are not Mux-generated exist for language '${target.languageCode}' or name '${target.name}': ${describeTracks(notGenerated)}. Use replaceExistingTracks: "replace_all" to replace them.`,
|
|
1207
|
+
tracks: notGenerated.map(summarizeTextTrack)
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
return { kind: "replace", toDelete: conflicting.map(summarizeTextTrack) };
|
|
1212
|
+
}
|
|
1213
|
+
function isDuplicateTrackNameError(error) {
|
|
1214
|
+
if (typeof error !== "object" || error === null) {
|
|
1215
|
+
return false;
|
|
1216
|
+
}
|
|
1217
|
+
const record = error;
|
|
1218
|
+
if (record.status !== 400) {
|
|
1219
|
+
return false;
|
|
1220
|
+
}
|
|
1221
|
+
const messages = Array.isArray(record.error?.messages) ? record.error.messages : [];
|
|
1222
|
+
const haystack = [...messages, record.message].filter((value) => typeof value === "string").map((value) => value.toLowerCase());
|
|
1223
|
+
return haystack.some((value) => value.includes("is not unique"));
|
|
1224
|
+
}
|
|
1225
|
+
function isNotFoundError(error) {
|
|
1226
|
+
return typeof error === "object" && error !== null && error.status === 404;
|
|
1227
|
+
}
|
|
1228
|
+
function buildMuxAiTrackPassthrough(workflow) {
|
|
1229
|
+
return JSON.stringify({ mux_ai: { workflow } });
|
|
1230
|
+
}
|
|
1231
|
+
function validateTrackPassthrough(passthrough) {
|
|
1232
|
+
if (passthrough !== void 0 && passthrough.length > MUX_TRACK_PASSTHROUGH_MAX_CHARS) {
|
|
1233
|
+
throw new MuxAiError(
|
|
1234
|
+
`Track passthrough must be at most ${MUX_TRACK_PASSTHROUGH_MAX_CHARS} characters (received ${passthrough.length}).`,
|
|
1235
|
+
{ type: "validation_error" }
|
|
1236
|
+
);
|
|
1237
|
+
}
|
|
1238
|
+
return passthrough;
|
|
1239
|
+
}
|
|
1240
|
+
async function fetchVttFromMux(vttUrl) {
|
|
1241
|
+
"use step";
|
|
1242
|
+
const vttResponse = await fetch(vttUrl);
|
|
1243
|
+
if (!vttResponse.ok) {
|
|
1244
|
+
throw new Error(`Failed to fetch VTT file: ${vttResponse.statusText}`);
|
|
1245
|
+
}
|
|
1246
|
+
return vttResponse.text();
|
|
1247
|
+
}
|
|
1248
|
+
async function createTextTrackOnMux(assetId, languageCode, trackName, presignedUrl, credentials, options = {}) {
|
|
1249
|
+
"use step";
|
|
1250
|
+
const muxClient = await resolveMuxClient(credentials);
|
|
1251
|
+
const mux = await muxClient.createClient();
|
|
1252
|
+
const trackResponse = await mux.video.assets.createTrack(assetId, {
|
|
1253
|
+
type: "text",
|
|
1254
|
+
text_type: "subtitles",
|
|
1255
|
+
language_code: languageCode,
|
|
1256
|
+
name: trackName,
|
|
1257
|
+
url: presignedUrl,
|
|
1258
|
+
closed_captions: options.closedCaptions,
|
|
1259
|
+
passthrough: options.passthrough
|
|
1260
|
+
});
|
|
1261
|
+
if (!trackResponse.id) {
|
|
1262
|
+
throw new Error("Failed to create text track: no track ID returned from Mux");
|
|
1263
|
+
}
|
|
1264
|
+
return trackResponse.id;
|
|
1265
|
+
}
|
|
1266
|
+
async function replaceAndCreateTrack(input) {
|
|
1267
|
+
"use step";
|
|
1268
|
+
const muxClient = await resolveMuxClient(input.credentials);
|
|
1269
|
+
const mux = await muxClient.createClient();
|
|
1270
|
+
const type = targetType(input.target);
|
|
1271
|
+
const deleted = [];
|
|
1272
|
+
const failed = (error) => ({
|
|
1273
|
+
kind: "create_failed",
|
|
1274
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
1275
|
+
deleted
|
|
1276
|
+
});
|
|
1277
|
+
const clearConflicts = async () => {
|
|
1278
|
+
let asset;
|
|
1279
|
+
try {
|
|
1280
|
+
asset = await mux.video.assets.retrieve(input.assetId);
|
|
1281
|
+
} catch (error) {
|
|
1282
|
+
if (deleted.length === 0) {
|
|
1283
|
+
throw error;
|
|
1284
|
+
}
|
|
1285
|
+
return failed(error);
|
|
1286
|
+
}
|
|
1287
|
+
const plan = planTextTrackReplacement(asset, input.target, input.policy, { keepTrackIds: input.keepTrackIds });
|
|
1288
|
+
if (plan.kind === "blocked") {
|
|
1289
|
+
return { ...plan, deleted };
|
|
1290
|
+
}
|
|
1291
|
+
if (plan.kind === "replace") {
|
|
1292
|
+
for (const track of plan.toDelete) {
|
|
1293
|
+
try {
|
|
1294
|
+
await mux.video.assets.deleteTrack(input.assetId, track.id);
|
|
1295
|
+
deleted.push(track);
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
if (!isNotFoundError(error)) {
|
|
1298
|
+
return failed(error);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
return void 0;
|
|
1304
|
+
};
|
|
1305
|
+
const create = async () => {
|
|
1306
|
+
const track = await mux.video.assets.createTrack(input.assetId, {
|
|
1307
|
+
type,
|
|
1308
|
+
...type === "text" ? { text_type: "subtitles", closed_captions: input.closedCaptions } : {},
|
|
1309
|
+
language_code: input.target.languageCode,
|
|
1310
|
+
name: input.target.name.trim(),
|
|
1311
|
+
url: input.presignedUrl,
|
|
1312
|
+
passthrough: input.passthrough
|
|
1313
|
+
});
|
|
1314
|
+
if (!track.id) {
|
|
1315
|
+
throw new Error(`Failed to create ${type} track: no track ID returned from Mux`);
|
|
1316
|
+
}
|
|
1317
|
+
return track.id;
|
|
1318
|
+
};
|
|
1319
|
+
const stopped = await clearConflicts();
|
|
1320
|
+
if (stopped) {
|
|
1321
|
+
return stopped;
|
|
1322
|
+
}
|
|
1323
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1324
|
+
try {
|
|
1325
|
+
return { kind: "created", trackId: await create(), deleted };
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
if (isDuplicateTrackNameError(error) && attempt === 0) {
|
|
1328
|
+
const stoppedOnRetry = await clearConflicts();
|
|
1329
|
+
if (stoppedOnRetry) {
|
|
1330
|
+
return stoppedOnRetry;
|
|
1331
|
+
}
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
return failed(error);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
return { kind: "create_failed", reason: "Mux rejected the track name as not unique after retrying.", deleted };
|
|
1338
|
+
}
|
|
1339
|
+
async function replaceAndCreateTextTrack(input) {
|
|
1340
|
+
"use step";
|
|
1341
|
+
return replaceAndCreateTrack({ ...input, target: { ...input.target, type: "text" } });
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1116
1344
|
// node_modules/@workflow/serde/dist/index.js
|
|
1117
1345
|
var WORKFLOW_SERIALIZE = /* @__PURE__ */ Symbol.for("workflow-serialize");
|
|
1118
1346
|
var WORKFLOW_DESERIALIZE = /* @__PURE__ */ Symbol.for("workflow-deserialize");
|
|
@@ -3897,11 +4125,8 @@ async function analyzeQuestions({
|
|
|
3897
4125
|
maxRetries: 0,
|
|
3898
4126
|
output: Output.object({ schema: responseSchema }),
|
|
3899
4127
|
experimental_telemetry: { isEnabled: true },
|
|
4128
|
+
system: systemPrompt,
|
|
3900
4129
|
messages: [
|
|
3901
|
-
{
|
|
3902
|
-
role: "system",
|
|
3903
|
-
content: systemPrompt
|
|
3904
|
-
},
|
|
3905
4130
|
{
|
|
3906
4131
|
role: "user",
|
|
3907
4132
|
content: imageDataUrl ? [
|
|
@@ -4315,11 +4540,8 @@ async function analyzeStoryboard({
|
|
|
4315
4540
|
maxRetries: 0,
|
|
4316
4541
|
output: Output2.object({ schema: burnedInCaptionsSchema }),
|
|
4317
4542
|
experimental_telemetry: { isEnabled: true },
|
|
4543
|
+
system: systemPrompt,
|
|
4318
4544
|
messages: [
|
|
4319
|
-
{
|
|
4320
|
-
role: "system",
|
|
4321
|
-
content: systemPrompt
|
|
4322
|
-
},
|
|
4323
4545
|
{
|
|
4324
4546
|
role: "user",
|
|
4325
4547
|
content: [
|
|
@@ -4464,11 +4686,8 @@ async function generateChaptersWithAI({
|
|
|
4464
4686
|
model,
|
|
4465
4687
|
maxRetries: 0,
|
|
4466
4688
|
output: Output3.object({ schema: chaptersSchema }),
|
|
4689
|
+
system: systemPrompt,
|
|
4467
4690
|
messages: [
|
|
4468
|
-
{
|
|
4469
|
-
role: "system",
|
|
4470
|
-
content: systemPrompt
|
|
4471
|
-
},
|
|
4472
4691
|
{
|
|
4473
4692
|
role: "user",
|
|
4474
4693
|
content: userPrompt
|
|
@@ -4756,32 +4975,6 @@ async function generateChaptersInternal(assetId, options, collectedUsage) {
|
|
|
4756
4975
|
import { generateText as generateText4, Output as Output4 } from "ai";
|
|
4757
4976
|
import { z as z6 } from "zod";
|
|
4758
4977
|
|
|
4759
|
-
// src/lib/mux-tracks.ts
|
|
4760
|
-
async function fetchVttFromMux(vttUrl) {
|
|
4761
|
-
"use step";
|
|
4762
|
-
const vttResponse = await fetch(vttUrl);
|
|
4763
|
-
if (!vttResponse.ok) {
|
|
4764
|
-
throw new Error(`Failed to fetch VTT file: ${vttResponse.statusText}`);
|
|
4765
|
-
}
|
|
4766
|
-
return vttResponse.text();
|
|
4767
|
-
}
|
|
4768
|
-
async function createTextTrackOnMux(assetId, languageCode, trackName, presignedUrl, credentials) {
|
|
4769
|
-
"use step";
|
|
4770
|
-
const muxClient = await resolveMuxClient(credentials);
|
|
4771
|
-
const mux = await muxClient.createClient();
|
|
4772
|
-
const trackResponse = await mux.video.assets.createTrack(assetId, {
|
|
4773
|
-
type: "text",
|
|
4774
|
-
text_type: "subtitles",
|
|
4775
|
-
language_code: languageCode,
|
|
4776
|
-
name: trackName,
|
|
4777
|
-
url: presignedUrl
|
|
4778
|
-
});
|
|
4779
|
-
if (!trackResponse.id) {
|
|
4780
|
-
throw new Error("Failed to create text track: no track ID returned from Mux");
|
|
4781
|
-
}
|
|
4782
|
-
return trackResponse.id;
|
|
4783
|
-
}
|
|
4784
|
-
|
|
4785
4978
|
// src/lib/storage-adapter.ts
|
|
4786
4979
|
function requireCredentials(accessKeyId, secretAccessKey) {
|
|
4787
4980
|
if (!accessKeyId || !secretAccessKey) {
|
|
@@ -5028,11 +5221,8 @@ async function identifyProfanityWithAI({
|
|
|
5028
5221
|
model,
|
|
5029
5222
|
maxRetries: 0,
|
|
5030
5223
|
output: Output4.object({ schema: profanityDetectionSchema }),
|
|
5224
|
+
system: SYSTEM_PROMPT3,
|
|
5031
5225
|
messages: [
|
|
5032
|
-
{
|
|
5033
|
-
role: "system",
|
|
5034
|
-
content: SYSTEM_PROMPT3
|
|
5035
|
-
},
|
|
5036
5226
|
{
|
|
5037
5227
|
role: "user",
|
|
5038
5228
|
content: `Identify all profane words and phrases in the following subtitle transcript. Return each unique profane word or phrase exactly as it appears in the text.
|
|
@@ -5063,6 +5253,7 @@ async function uploadEditedVttToS3({
|
|
|
5063
5253
|
editedVtt,
|
|
5064
5254
|
assetId,
|
|
5065
5255
|
trackId,
|
|
5256
|
+
variant = "edited",
|
|
5066
5257
|
s3Endpoint,
|
|
5067
5258
|
s3Region,
|
|
5068
5259
|
s3Bucket,
|
|
@@ -5072,7 +5263,7 @@ async function uploadEditedVttToS3({
|
|
|
5072
5263
|
"use step";
|
|
5073
5264
|
const s3AccessKeyId = env_default.S3_ACCESS_KEY_ID;
|
|
5074
5265
|
const s3SecretAccessKey = env_default.S3_SECRET_ACCESS_KEY;
|
|
5075
|
-
const vttKey = `edited/${assetId}/${trackId}
|
|
5266
|
+
const vttKey = `edited/${assetId}/${trackId}-${variant}-${Date.now()}.vtt`;
|
|
5076
5267
|
await putObjectWithStorageAdapter({
|
|
5077
5268
|
accessKeyId: s3AccessKeyId,
|
|
5078
5269
|
secretAccessKey: s3SecretAccessKey,
|
|
@@ -5122,6 +5313,9 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
|
|
|
5122
5313
|
s3Region: providedS3Region,
|
|
5123
5314
|
s3Bucket: providedS3Bucket,
|
|
5124
5315
|
trackNameSuffix,
|
|
5316
|
+
replaceExistingTracks: replaceExistingTracksOption,
|
|
5317
|
+
trackName: providedTrackName,
|
|
5318
|
+
trackPassthrough: providedTrackPassthrough,
|
|
5125
5319
|
storageAdapter,
|
|
5126
5320
|
credentials
|
|
5127
5321
|
} = options;
|
|
@@ -5134,6 +5328,21 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
|
|
|
5134
5328
|
if (autoCensorOption && !provider) {
|
|
5135
5329
|
throw new MuxAiError("provider is required when using autoCensorProfanity.", { type: "validation_error" });
|
|
5136
5330
|
}
|
|
5331
|
+
const legacyTrackNaming = deleteOriginalTrack !== void 0 || trackNameSuffix !== void 0;
|
|
5332
|
+
if (legacyTrackNaming && (replaceExistingTracksOption !== void 0 || providedTrackName !== void 0)) {
|
|
5333
|
+
throw new MuxAiError(
|
|
5334
|
+
"deleteOriginalTrack and trackNameSuffix are deprecated and cannot be combined with replaceExistingTracks or trackName.",
|
|
5335
|
+
{ type: "validation_error" }
|
|
5336
|
+
);
|
|
5337
|
+
}
|
|
5338
|
+
const replaceExistingTracks = replaceExistingTracksOption ?? "replace_all";
|
|
5339
|
+
if (!legacyTrackNaming && replaceExistingTracks === "fail" && !providedTrackName) {
|
|
5340
|
+
throw new MuxAiError(
|
|
5341
|
+
`trackName is required when replaceExistingTracks is "fail": the edited track cannot reuse the source track's name while the source is kept.`,
|
|
5342
|
+
{ type: "validation_error" }
|
|
5343
|
+
);
|
|
5344
|
+
}
|
|
5345
|
+
const trackPassthrough = validateTrackPassthrough(providedTrackPassthrough) ?? buildMuxAiTrackPassthrough("edit-captions");
|
|
5137
5346
|
const deleteOriginal = deleteOriginalTrack !== false;
|
|
5138
5347
|
const uploadToMux = uploadToMuxOption !== false;
|
|
5139
5348
|
const uploadToS3 = uploadToS3Option || uploadToMux;
|
|
@@ -5176,6 +5385,25 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
|
|
|
5176
5385
|
{ type: "validation_error" }
|
|
5177
5386
|
);
|
|
5178
5387
|
}
|
|
5388
|
+
const sourceLanguageCode = sourceTrack.language_code || "en";
|
|
5389
|
+
const sourceName = sourceTrack.name || "Subtitles";
|
|
5390
|
+
const outputTrack = {
|
|
5391
|
+
languageCode: sourceLanguageCode,
|
|
5392
|
+
name: providedTrackName ?? sourceName
|
|
5393
|
+
};
|
|
5394
|
+
const keepTrackIds = replaceExistingTracks === "fail" ? [trackId] : [];
|
|
5395
|
+
if (replaceExistingTracks === "fail" && normalizeTrackName(outputTrack.name) === normalizeTrackName(sourceName)) {
|
|
5396
|
+
throw new MuxAiError(
|
|
5397
|
+
`trackName "${outputTrack.name}" matches the source track's name. Mux track names must be unique; choose a different trackName or use replaceExistingTracks: "replace_all" to replace the source.`,
|
|
5398
|
+
{ type: "validation_error" }
|
|
5399
|
+
);
|
|
5400
|
+
}
|
|
5401
|
+
if (uploadToMux && !legacyTrackNaming) {
|
|
5402
|
+
const plan = planTextTrackReplacement(assetData, outputTrack, replaceExistingTracks, { keepTrackIds });
|
|
5403
|
+
if (plan.kind === "blocked") {
|
|
5404
|
+
throw new MuxAiError(plan.reason, { type: "validation_error" });
|
|
5405
|
+
}
|
|
5406
|
+
}
|
|
5179
5407
|
const vttUrl = await buildTranscriptUrl(playbackId, trackId, policy === "signed", credentials);
|
|
5180
5408
|
let vttContent;
|
|
5181
5409
|
try {
|
|
@@ -5253,32 +5481,32 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
|
|
|
5253
5481
|
} : void 0;
|
|
5254
5482
|
let presignedUrl;
|
|
5255
5483
|
let uploadedTrackId;
|
|
5484
|
+
let replacedTracks;
|
|
5256
5485
|
if (uploadToS3) {
|
|
5486
|
+
const s3Config = {
|
|
5487
|
+
assetId,
|
|
5488
|
+
trackId,
|
|
5489
|
+
s3Endpoint,
|
|
5490
|
+
s3Region,
|
|
5491
|
+
s3Bucket,
|
|
5492
|
+
storageAdapter,
|
|
5493
|
+
s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds
|
|
5494
|
+
};
|
|
5257
5495
|
try {
|
|
5258
|
-
presignedUrl = await uploadEditedVttToS3({
|
|
5259
|
-
editedVtt,
|
|
5260
|
-
assetId,
|
|
5261
|
-
trackId,
|
|
5262
|
-
s3Endpoint,
|
|
5263
|
-
s3Region,
|
|
5264
|
-
s3Bucket,
|
|
5265
|
-
storageAdapter,
|
|
5266
|
-
s3SignedUrlExpirySeconds: options.s3SignedUrlExpirySeconds
|
|
5267
|
-
});
|
|
5496
|
+
presignedUrl = await uploadEditedVttToS3({ editedVtt, ...s3Config });
|
|
5268
5497
|
} catch (error) {
|
|
5269
5498
|
wrapError(error, "Failed to upload VTT to S3");
|
|
5270
5499
|
}
|
|
5271
|
-
if (uploadToMux) {
|
|
5500
|
+
if (uploadToMux && legacyTrackNaming) {
|
|
5272
5501
|
try {
|
|
5273
|
-
const
|
|
5274
|
-
const suffix = trackNameSuffix ?? "edited";
|
|
5275
|
-
const trackName = `${sourceTrack.name || "Subtitles"} (${suffix})`;
|
|
5502
|
+
const trackName = `${sourceName} (${trackNameSuffix ?? "edited"})`;
|
|
5276
5503
|
uploadedTrackId = await createTextTrackOnMux(
|
|
5277
5504
|
assetId,
|
|
5278
|
-
|
|
5505
|
+
sourceLanguageCode,
|
|
5279
5506
|
trackName,
|
|
5280
5507
|
presignedUrl,
|
|
5281
|
-
credentials
|
|
5508
|
+
credentials,
|
|
5509
|
+
{ closedCaptions: sourceTrack.closed_captions, passthrough: trackPassthrough }
|
|
5282
5510
|
);
|
|
5283
5511
|
} catch (error) {
|
|
5284
5512
|
wrapError(error, "Failed to add edited track to Mux asset");
|
|
@@ -5290,6 +5518,48 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
|
|
|
5290
5518
|
wrapError(error, "Failed to delete original track");
|
|
5291
5519
|
}
|
|
5292
5520
|
}
|
|
5521
|
+
} else if (uploadToMux) {
|
|
5522
|
+
let outcome;
|
|
5523
|
+
try {
|
|
5524
|
+
outcome = await replaceAndCreateTextTrack({
|
|
5525
|
+
assetId,
|
|
5526
|
+
target: outputTrack,
|
|
5527
|
+
policy: replaceExistingTracks,
|
|
5528
|
+
presignedUrl,
|
|
5529
|
+
closedCaptions: sourceTrack.closed_captions,
|
|
5530
|
+
passthrough: trackPassthrough,
|
|
5531
|
+
keepTrackIds,
|
|
5532
|
+
credentials
|
|
5533
|
+
});
|
|
5534
|
+
} catch (error) {
|
|
5535
|
+
wrapError(error, "Failed to add edited track to Mux asset");
|
|
5536
|
+
}
|
|
5537
|
+
if (outcome.kind !== "created") {
|
|
5538
|
+
const sourceWasDeleted = outcome.deleted.some((track) => track.id === trackId);
|
|
5539
|
+
let restoreNote = "";
|
|
5540
|
+
if (sourceWasDeleted) {
|
|
5541
|
+
try {
|
|
5542
|
+
const originalUrl = await uploadEditedVttToS3({ editedVtt: vttContent, variant: "original", ...s3Config });
|
|
5543
|
+
const restoredTrackId = await createTextTrackOnMux(
|
|
5544
|
+
assetId,
|
|
5545
|
+
sourceLanguageCode,
|
|
5546
|
+
sourceName,
|
|
5547
|
+
originalUrl,
|
|
5548
|
+
credentials,
|
|
5549
|
+
{ closedCaptions: sourceTrack.closed_captions, passthrough: sourceTrack.passthrough }
|
|
5550
|
+
);
|
|
5551
|
+
restoreNote = ` The source track was restored as ${restoredTrackId}.`;
|
|
5552
|
+
} catch (restoreError) {
|
|
5553
|
+
restoreNote = ` Restoring the source track also failed: ${restoreError instanceof Error ? restoreError.message : String(restoreError)}.`;
|
|
5554
|
+
}
|
|
5555
|
+
}
|
|
5556
|
+
if (outcome.kind === "blocked") {
|
|
5557
|
+
throw new MuxAiError(`${outcome.reason}${restoreNote}`, { type: "validation_error" });
|
|
5558
|
+
}
|
|
5559
|
+
throw new Error(`Failed to add edited track to Mux asset: ${outcome.reason}.${restoreNote}`);
|
|
5560
|
+
}
|
|
5561
|
+
uploadedTrackId = outcome.trackId;
|
|
5562
|
+
replacedTracks = outcome.deleted;
|
|
5293
5563
|
}
|
|
5294
5564
|
}
|
|
5295
5565
|
return {
|
|
@@ -5302,6 +5572,7 @@ async function editCaptionsInternal(assetId, trackId, options, collectedUsage) {
|
|
|
5302
5572
|
replacements: replacementsResult,
|
|
5303
5573
|
speakerReplacements: speakerReplacementsResult,
|
|
5304
5574
|
uploadedTrackId,
|
|
5575
|
+
replacedTracks,
|
|
5305
5576
|
presignedUrl,
|
|
5306
5577
|
usage: usageWithMetadata,
|
|
5307
5578
|
safety: safety.report()
|
|
@@ -5761,11 +6032,8 @@ async function generateInsightsWithAI(provider, modelId, systemPrompt, userPromp
|
|
|
5761
6032
|
maxRetries: 0,
|
|
5762
6033
|
output: Output5.object({ schema: engagementInsightsSchema }),
|
|
5763
6034
|
experimental_telemetry: { isEnabled: true },
|
|
6035
|
+
system: systemPrompt,
|
|
5764
6036
|
messages: [
|
|
5765
|
-
{
|
|
5766
|
-
role: "system",
|
|
5767
|
-
content: systemPrompt
|
|
5768
|
-
},
|
|
5769
6037
|
{
|
|
5770
6038
|
role: "user",
|
|
5771
6039
|
content: [
|
|
@@ -7027,11 +7295,8 @@ async function analyzeStoryboard2(imageDataUrl, provider, modelId, userPrompt, s
|
|
|
7027
7295
|
description: "Structured summary with title, description, and keywords.",
|
|
7028
7296
|
schema
|
|
7029
7297
|
}),
|
|
7298
|
+
system: systemPrompt,
|
|
7030
7299
|
messages: [
|
|
7031
|
-
{
|
|
7032
|
-
role: "system",
|
|
7033
|
-
content: systemPrompt
|
|
7034
|
-
},
|
|
7035
7300
|
{
|
|
7036
7301
|
role: "user",
|
|
7037
7302
|
content: [
|
|
@@ -7075,11 +7340,8 @@ async function analyzeAudioOnly(provider, modelId, userPrompt, systemPrompt, des
|
|
|
7075
7340
|
description: "Structured summary with title, description, and keywords.",
|
|
7076
7341
|
schema
|
|
7077
7342
|
}),
|
|
7343
|
+
system: systemPrompt,
|
|
7078
7344
|
messages: [
|
|
7079
|
-
{
|
|
7080
|
-
role: "system",
|
|
7081
|
-
content: systemPrompt
|
|
7082
|
-
},
|
|
7083
7345
|
{
|
|
7084
7346
|
role: "user",
|
|
7085
7347
|
content: userPrompt
|
|
@@ -7571,23 +7833,6 @@ async function downloadAndUploadDubTranscript({
|
|
|
7571
7833
|
console.warn(`\u2705 Dub transcript uploaded successfully to: ${vttKey}`);
|
|
7572
7834
|
return presignedUrl;
|
|
7573
7835
|
}
|
|
7574
|
-
async function createAudioTrackOnMux(assetId, languageCode, presignedUrl, credentials) {
|
|
7575
|
-
"use step";
|
|
7576
|
-
const muxClient = await resolveMuxClient(credentials);
|
|
7577
|
-
const mux = await muxClient.createClient();
|
|
7578
|
-
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(languageCode) || languageCode.toUpperCase();
|
|
7579
|
-
const trackName = `${languageName} (auto-dubbed)`;
|
|
7580
|
-
const trackResponse = await mux.video.assets.createTrack(assetId, {
|
|
7581
|
-
type: "audio",
|
|
7582
|
-
language_code: languageCode,
|
|
7583
|
-
name: trackName,
|
|
7584
|
-
url: presignedUrl
|
|
7585
|
-
});
|
|
7586
|
-
if (!trackResponse.id) {
|
|
7587
|
-
throw new Error("Failed to create audio track: no track ID returned from Mux");
|
|
7588
|
-
}
|
|
7589
|
-
return trackResponse.id;
|
|
7590
|
-
}
|
|
7591
7836
|
async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
7592
7837
|
"use workflow";
|
|
7593
7838
|
const {
|
|
@@ -7600,15 +7845,23 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
7600
7845
|
uploadCaptionsToMux = false,
|
|
7601
7846
|
staticRenditionCleanup: staticRenditionCleanupOption = "delete",
|
|
7602
7847
|
storageAdapter,
|
|
7603
|
-
credentials: providedCredentials
|
|
7848
|
+
credentials: providedCredentials,
|
|
7849
|
+
replaceExistingTracks = "fail",
|
|
7850
|
+
trackName: providedTrackName,
|
|
7851
|
+
trackPassthrough: providedTrackPassthrough
|
|
7604
7852
|
} = options;
|
|
7605
7853
|
if (provider !== "elevenlabs") {
|
|
7606
7854
|
throw new MuxAiError("Only ElevenLabs provider is currently supported for audio translation.", { type: "validation_error" });
|
|
7607
7855
|
}
|
|
7608
7856
|
const credentials = providedCredentials;
|
|
7609
7857
|
const effectiveStorageAdapter = storageAdapter;
|
|
7858
|
+
const trackPassthrough = validateTrackPassthrough(providedTrackPassthrough) ?? buildMuxAiTrackPassthrough("translate-audio");
|
|
7610
7859
|
const uploadToMux = uploadToMuxOption !== false;
|
|
7611
7860
|
const uploadToS3 = uploadToS3Option || uploadToMux || uploadCaptionsToMux;
|
|
7861
|
+
const muxLangCode = toISO639_1(toLanguageCode);
|
|
7862
|
+
const trackName = providedTrackName ?? `${getLanguageName(muxLangCode) ?? muxLangCode.toUpperCase()} (Auto-dubbed)`;
|
|
7863
|
+
const audioTrackTarget = { type: "audio", languageCode: muxLangCode, name: trackName };
|
|
7864
|
+
const captionsTrackTarget = { type: "text", languageCode: muxLangCode, name: trackName };
|
|
7612
7865
|
const s3Endpoint = options.s3Endpoint ?? env_default.S3_ENDPOINT;
|
|
7613
7866
|
const s3Region = options.s3Region ?? env_default.S3_REGION ?? "auto";
|
|
7614
7867
|
const s3Bucket = options.s3Bucket ?? env_default.S3_BUCKET;
|
|
@@ -7619,6 +7872,15 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
7619
7872
|
}
|
|
7620
7873
|
const { asset: initialAsset, playbackId, policy } = await getPlaybackIdForAsset(assetId, credentials, options.assetSnapshot);
|
|
7621
7874
|
const assetDurationSeconds = getAssetDurationSecondsFromAsset(initialAsset);
|
|
7875
|
+
for (const target of [
|
|
7876
|
+
...uploadToMux ? [audioTrackTarget] : [],
|
|
7877
|
+
...uploadCaptionsToMux ? [captionsTrackTarget] : []
|
|
7878
|
+
]) {
|
|
7879
|
+
const plan = planTextTrackReplacement(initialAsset, target, replaceExistingTracks);
|
|
7880
|
+
if (plan.kind === "blocked") {
|
|
7881
|
+
throw new MuxAiError(plan.reason, { type: "validation_error" });
|
|
7882
|
+
}
|
|
7883
|
+
}
|
|
7622
7884
|
let currentAsset = initialAsset;
|
|
7623
7885
|
let createdStaticRenditionId;
|
|
7624
7886
|
if (!hasReadyAudioStaticRendition(currentAsset)) {
|
|
@@ -7634,6 +7896,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
7634
7896
|
let dubbingId;
|
|
7635
7897
|
let presignedUrl;
|
|
7636
7898
|
let uploadedTrackId;
|
|
7899
|
+
let replacedTracks;
|
|
7637
7900
|
let captionsPresignedUrl;
|
|
7638
7901
|
let captionsTrackId;
|
|
7639
7902
|
try {
|
|
@@ -7724,18 +7987,29 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
7724
7987
|
}
|
|
7725
7988
|
if (uploadToMux) {
|
|
7726
7989
|
console.warn("\u{1F4F9} Adding dubbed audio track to Mux asset...");
|
|
7727
|
-
|
|
7990
|
+
let outcome;
|
|
7728
7991
|
try {
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
|
|
7732
|
-
|
|
7733
|
-
|
|
7992
|
+
outcome = await replaceAndCreateTrack({
|
|
7993
|
+
assetId,
|
|
7994
|
+
target: audioTrackTarget,
|
|
7995
|
+
policy: replaceExistingTracks,
|
|
7996
|
+
presignedUrl,
|
|
7997
|
+
passthrough: trackPassthrough,
|
|
7998
|
+
credentials
|
|
7999
|
+
});
|
|
7734
8000
|
} catch (error) {
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
8001
|
+
wrapError(error, `Failed to add dubbed audio track to Mux asset (dubbed audio is staged at: ${presignedUrl})`);
|
|
8002
|
+
}
|
|
8003
|
+
if (outcome.kind === "blocked") {
|
|
8004
|
+
throw new MuxAiError(`${outcome.reason} Dubbed audio is staged at: ${presignedUrl}`, { type: "validation_error" });
|
|
8005
|
+
}
|
|
8006
|
+
if (outcome.kind === "create_failed") {
|
|
8007
|
+
throw new Error(`Failed to add dubbed audio track to Mux asset: ${outcome.reason}. Dubbed audio is staged at: ${presignedUrl}`);
|
|
7738
8008
|
}
|
|
8009
|
+
uploadedTrackId = outcome.trackId;
|
|
8010
|
+
replacedTracks = outcome.deleted;
|
|
8011
|
+
console.warn(`\u2705 Track added to Mux asset with ID: ${uploadedTrackId}`);
|
|
8012
|
+
console.warn(`\u{1F4CB} Track name: "${trackName}"`);
|
|
7739
8013
|
}
|
|
7740
8014
|
try {
|
|
7741
8015
|
console.warn("\u{1F4E5} Downloading dub transcript from ElevenLabs and staging to S3...");
|
|
@@ -7753,15 +8027,21 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
7753
8027
|
});
|
|
7754
8028
|
if (uploadCaptionsToMux) {
|
|
7755
8029
|
console.warn("\u{1F4F9} Adding dubbed captions track to Mux asset...");
|
|
7756
|
-
const
|
|
7757
|
-
const languageName = new Intl.DisplayNames(["en"], { type: "language" }).of(muxLangCode) || muxLangCode.toUpperCase();
|
|
7758
|
-
captionsTrackId = await createTextTrackOnMux(
|
|
8030
|
+
const captionsOutcome = await replaceAndCreateTrack({
|
|
7759
8031
|
assetId,
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
captionsPresignedUrl,
|
|
8032
|
+
target: captionsTrackTarget,
|
|
8033
|
+
policy: replaceExistingTracks,
|
|
8034
|
+
presignedUrl: captionsPresignedUrl,
|
|
8035
|
+
passthrough: trackPassthrough,
|
|
7763
8036
|
credentials
|
|
7764
|
-
);
|
|
8037
|
+
});
|
|
8038
|
+
if (captionsOutcome.deleted.length > 0) {
|
|
8039
|
+
replacedTracks = [...replacedTracks ?? [], ...captionsOutcome.deleted];
|
|
8040
|
+
}
|
|
8041
|
+
if (captionsOutcome.kind !== "created") {
|
|
8042
|
+
throw new Error(captionsOutcome.reason);
|
|
8043
|
+
}
|
|
8044
|
+
captionsTrackId = captionsOutcome.trackId;
|
|
7765
8045
|
console.warn(`\u2705 Captions track added to Mux asset with ID: ${captionsTrackId}`);
|
|
7766
8046
|
}
|
|
7767
8047
|
} catch (error) {
|
|
@@ -7792,6 +8072,7 @@ async function translateAudio(assetId, toLanguageCode, options = {}) {
|
|
|
7792
8072
|
dubbingId,
|
|
7793
8073
|
uploadedTrackId,
|
|
7794
8074
|
presignedUrl,
|
|
8075
|
+
replacedTracks,
|
|
7795
8076
|
captionsTrackId,
|
|
7796
8077
|
captionsPresignedUrl,
|
|
7797
8078
|
createdStaticRenditionId,
|
|
@@ -8143,11 +8424,8 @@ async function translateVttWithAI({
|
|
|
8143
8424
|
model,
|
|
8144
8425
|
maxRetries: 0,
|
|
8145
8426
|
output: Output7.object({ schema: translationSchema }),
|
|
8427
|
+
system: SYSTEM_PROMPT6,
|
|
8146
8428
|
messages: [
|
|
8147
|
-
{
|
|
8148
|
-
role: "system",
|
|
8149
|
-
content: SYSTEM_PROMPT6
|
|
8150
|
-
},
|
|
8151
8429
|
{
|
|
8152
8430
|
role: "user",
|
|
8153
8431
|
content: `Translate from ${fromLanguageCode} to ${toLanguageCode}:${buildNeverTranslateSection(neverTranslate)}
|
|
@@ -8211,11 +8489,8 @@ async function translateCueChunkWithAI({
|
|
|
8211
8489
|
model,
|
|
8212
8490
|
maxRetries: 0,
|
|
8213
8491
|
output: Output7.object({ schema }),
|
|
8492
|
+
system: CUE_TRANSLATION_SYSTEM_PROMPT,
|
|
8214
8493
|
messages: [
|
|
8215
|
-
{
|
|
8216
|
-
role: "system",
|
|
8217
|
-
content: CUE_TRANSLATION_SYSTEM_PROMPT
|
|
8218
|
-
},
|
|
8219
8494
|
{
|
|
8220
8495
|
role: "user",
|
|
8221
8496
|
content: `Translate from ${fromLanguageCode} to ${toLanguageCode}.
|
|
@@ -8467,11 +8742,15 @@ async function translateCaptionsInternal(assetId, trackId, toLanguageCode, optio
|
|
|
8467
8742
|
storageAdapter,
|
|
8468
8743
|
credentials: providedCredentials,
|
|
8469
8744
|
chunking,
|
|
8470
|
-
neverTranslate: neverTranslateOption
|
|
8745
|
+
neverTranslate: neverTranslateOption,
|
|
8746
|
+
replaceExistingTracks = "fail",
|
|
8747
|
+
trackName: providedTrackName,
|
|
8748
|
+
trackPassthrough: providedTrackPassthrough
|
|
8471
8749
|
} = options;
|
|
8472
8750
|
const credentials = providedCredentials;
|
|
8473
8751
|
const effectiveStorageAdapter = storageAdapter;
|
|
8474
8752
|
const neverTranslateTerms = neverTranslateOption ? validateNeverTranslateTerms(neverTranslateOption) : [];
|
|
8753
|
+
const trackPassthrough = validateTrackPassthrough(providedTrackPassthrough) ?? buildMuxAiTrackPassthrough("translate-captions");
|
|
8475
8754
|
const s3Endpoint = providedS3Endpoint ?? env_default.S3_ENDPOINT;
|
|
8476
8755
|
const s3Region = providedS3Region ?? env_default.S3_REGION ?? "auto";
|
|
8477
8756
|
const s3Bucket = providedS3Bucket ?? env_default.S3_BUCKET;
|
|
@@ -8512,6 +8791,16 @@ async function translateCaptionsInternal(assetId, trackId, toLanguageCode, optio
|
|
|
8512
8791
|
{ type: "validation_error" }
|
|
8513
8792
|
);
|
|
8514
8793
|
}
|
|
8794
|
+
const outputTrack = {
|
|
8795
|
+
languageCode: toLanguageCode,
|
|
8796
|
+
name: providedTrackName ?? `${getLanguageName(toLanguageCode) ?? toLanguageCode.toUpperCase()} (Auto-translated)`
|
|
8797
|
+
};
|
|
8798
|
+
if (uploadToMux) {
|
|
8799
|
+
const plan = planTextTrackReplacement(assetData, outputTrack, replaceExistingTracks);
|
|
8800
|
+
if (plan.kind === "blocked") {
|
|
8801
|
+
throw new MuxAiError(plan.reason, { type: "validation_error" });
|
|
8802
|
+
}
|
|
8803
|
+
}
|
|
8515
8804
|
const vttUrl = await buildTranscriptUrl(playbackId, trackId, policy === "signed", credentials);
|
|
8516
8805
|
let vttContent;
|
|
8517
8806
|
try {
|
|
@@ -8579,6 +8868,7 @@ async function translateCaptionsInternal(assetId, trackId, toLanguageCode, optio
|
|
|
8579
8868
|
const targetLanguage = getLanguageCodePair(toLanguageCode);
|
|
8580
8869
|
let presignedUrl;
|
|
8581
8870
|
let uploadedTrackId;
|
|
8871
|
+
let replacedTracks;
|
|
8582
8872
|
if (uploadToS3) {
|
|
8583
8873
|
try {
|
|
8584
8874
|
presignedUrl = await uploadVttToS3({
|
|
@@ -8596,19 +8886,28 @@ async function translateCaptionsInternal(assetId, trackId, toLanguageCode, optio
|
|
|
8596
8886
|
wrapError(error, "Failed to upload VTT to S3");
|
|
8597
8887
|
}
|
|
8598
8888
|
if (uploadToMux) {
|
|
8889
|
+
let outcome;
|
|
8599
8890
|
try {
|
|
8600
|
-
|
|
8601
|
-
const trackName = `${languageName} (auto-translated)`;
|
|
8602
|
-
uploadedTrackId = await createTextTrackOnMux(
|
|
8891
|
+
outcome = await replaceAndCreateTextTrack({
|
|
8603
8892
|
assetId,
|
|
8604
|
-
|
|
8605
|
-
|
|
8893
|
+
target: outputTrack,
|
|
8894
|
+
policy: replaceExistingTracks,
|
|
8606
8895
|
presignedUrl,
|
|
8896
|
+
passthrough: trackPassthrough,
|
|
8607
8897
|
credentials
|
|
8608
|
-
);
|
|
8898
|
+
});
|
|
8609
8899
|
} catch (error) {
|
|
8610
|
-
|
|
8900
|
+
wrapError(error, "Failed to add translated track to Mux asset");
|
|
8901
|
+
}
|
|
8902
|
+
if (outcome.kind !== "created") {
|
|
8903
|
+
const deletedNote = outcome.deleted.length > 0 ? ` Tracks already deleted before the failure: ${outcome.deleted.map((track) => `${track.name ?? track.id} (${track.id})`).join(", ")}.` : "";
|
|
8904
|
+
if (outcome.kind === "blocked") {
|
|
8905
|
+
throw new MuxAiError(`${outcome.reason}${deletedNote}`, { type: "validation_error" });
|
|
8906
|
+
}
|
|
8907
|
+
throw new Error(`Failed to add translated track to Mux asset: ${outcome.reason}.${deletedNote}`);
|
|
8611
8908
|
}
|
|
8909
|
+
uploadedTrackId = outcome.trackId;
|
|
8910
|
+
replacedTracks = outcome.deleted;
|
|
8612
8911
|
}
|
|
8613
8912
|
}
|
|
8614
8913
|
return {
|
|
@@ -8621,6 +8920,7 @@ async function translateCaptionsInternal(assetId, trackId, toLanguageCode, optio
|
|
|
8621
8920
|
originalVtt: vttContent,
|
|
8622
8921
|
translatedVtt,
|
|
8623
8922
|
uploadedTrackId,
|
|
8923
|
+
replacedTracks,
|
|
8624
8924
|
presignedUrl,
|
|
8625
8925
|
usage: usageWithMetadata,
|
|
8626
8926
|
safety,
|
|
@@ -8628,20 +8928,33 @@ async function translateCaptionsInternal(assetId, trackId, toLanguageCode, optio
|
|
|
8628
8928
|
};
|
|
8629
8929
|
}
|
|
8630
8930
|
export {
|
|
8931
|
+
MUX_TRACK_PASSTHROUGH_MAX_CHARS,
|
|
8631
8932
|
MuxAiError,
|
|
8632
8933
|
WorkflowStorageClient,
|
|
8934
|
+
buildMuxAiTrackPassthrough,
|
|
8633
8935
|
createWorkflowStorageClient,
|
|
8634
8936
|
decryptFromWorkflow,
|
|
8635
8937
|
encryptForWorkflow,
|
|
8938
|
+
findNameCollisionTextTracks,
|
|
8939
|
+
findSameLanguageTextTracks,
|
|
8636
8940
|
getLanguageCodePair,
|
|
8637
8941
|
getLanguageName,
|
|
8942
|
+
isDuplicateTrackNameError,
|
|
8943
|
+
isMuxGeneratedTextTrack,
|
|
8638
8944
|
isUndeterminedLanguageCode,
|
|
8639
8945
|
isValidISO639_1,
|
|
8640
8946
|
isValidISO639_3,
|
|
8947
|
+
normalizeTrackLanguageCode,
|
|
8948
|
+
normalizeTrackName,
|
|
8949
|
+
planTextTrackReplacement,
|
|
8641
8950
|
primitives_exports as primitives,
|
|
8951
|
+
replaceAndCreateTextTrack,
|
|
8952
|
+
replaceAndCreateTrack,
|
|
8642
8953
|
setWorkflowCredentialsProvider,
|
|
8954
|
+
summarizeTextTrack,
|
|
8643
8955
|
toISO639_1,
|
|
8644
8956
|
toISO639_3,
|
|
8957
|
+
validateTrackPassthrough,
|
|
8645
8958
|
version,
|
|
8646
8959
|
workflows_exports as workflows,
|
|
8647
8960
|
wrapError
|