@contractspec/lib.video-gen 1.44.0 → 1.46.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 +37 -0
- package/dist/browser/docs/rendering.docblock.js +4 -4
- package/dist/browser/docs/video-gen.docblock.js +1 -1
- package/dist/browser/generators/index.js +601 -192
- package/dist/browser/generators/scene-planner.js +527 -23
- package/dist/browser/generators/script-generator.js +528 -18
- package/dist/browser/generators/video-generator.js +601 -191
- package/dist/browser/i18n/catalogs/en.js +160 -0
- package/dist/browser/i18n/catalogs/es.js +160 -0
- package/dist/browser/i18n/catalogs/fr.js +160 -0
- package/dist/browser/i18n/catalogs/index.js +462 -0
- package/dist/browser/i18n/index.js +534 -0
- package/dist/browser/i18n/keys.js +54 -0
- package/dist/browser/i18n/locale.js +21 -0
- package/dist/browser/i18n/messages.js +474 -0
- package/dist/browser/index.js +601 -192
- package/dist/docs/rendering.docblock.js +4 -4
- package/dist/docs/video-gen.docblock.js +1 -1
- package/dist/generators/index.d.ts +0 -2
- package/dist/generators/index.js +601 -192
- package/dist/generators/scene-planner.d.ts +2 -0
- package/dist/generators/scene-planner.js +527 -23
- package/dist/generators/script-generator.d.ts +2 -0
- package/dist/generators/script-generator.js +528 -18
- package/dist/generators/video-generator.d.ts +6 -5
- package/dist/generators/video-generator.js +601 -191
- package/dist/i18n/catalogs/en.d.ts +8 -0
- package/dist/i18n/catalogs/en.js +155 -0
- package/dist/i18n/catalogs/es.d.ts +6 -0
- package/dist/i18n/catalogs/es.js +155 -0
- package/dist/i18n/catalogs/fr.d.ts +6 -0
- package/dist/i18n/catalogs/fr.js +155 -0
- package/dist/i18n/catalogs/index.d.ts +8 -0
- package/dist/i18n/catalogs/index.js +457 -0
- package/dist/i18n/i18n.test.d.ts +1 -0
- package/dist/i18n/index.d.ts +22 -0
- package/dist/i18n/index.js +529 -0
- package/dist/i18n/keys.d.ts +116 -0
- package/dist/i18n/keys.js +49 -0
- package/dist/i18n/locale.d.ts +8 -0
- package/dist/i18n/locale.js +16 -0
- package/dist/i18n/messages.d.ts +14 -0
- package/dist/i18n/messages.js +469 -0
- package/dist/index.js +601 -192
- package/dist/node/docs/rendering.docblock.js +4 -4
- package/dist/node/docs/video-gen.docblock.js +1 -1
- package/dist/node/generators/index.js +601 -192
- package/dist/node/generators/scene-planner.js +527 -23
- package/dist/node/generators/script-generator.js +528 -18
- package/dist/node/generators/video-generator.js +601 -191
- package/dist/node/i18n/catalogs/en.js +155 -0
- package/dist/node/i18n/catalogs/es.js +155 -0
- package/dist/node/i18n/catalogs/fr.js +155 -0
- package/dist/node/i18n/catalogs/index.js +457 -0
- package/dist/node/i18n/index.js +529 -0
- package/dist/node/i18n/keys.js +49 -0
- package/dist/node/i18n/locale.js +16 -0
- package/dist/node/i18n/messages.js +469 -0
- package/dist/node/index.js +601 -192
- package/dist/types.d.ts +14 -3
- package/package.json +149 -6
package/dist/node/index.js
CHANGED
|
@@ -1115,17 +1115,528 @@ var TerminalDemo = ({
|
|
|
1115
1115
|
]
|
|
1116
1116
|
}, undefined, true, undefined, this);
|
|
1117
1117
|
};
|
|
1118
|
+
// src/i18n/catalogs/en.ts
|
|
1119
|
+
import { defineTranslation } from "@contractspec/lib.contracts-spec/translations";
|
|
1120
|
+
var enMessages = defineTranslation({
|
|
1121
|
+
meta: {
|
|
1122
|
+
key: "video-gen.messages",
|
|
1123
|
+
version: "1.0.0",
|
|
1124
|
+
domain: "video-gen",
|
|
1125
|
+
description: "All user-facing, LLM-facing, and developer-facing strings for the video-gen package",
|
|
1126
|
+
owners: ["platform"],
|
|
1127
|
+
stability: "experimental"
|
|
1128
|
+
},
|
|
1129
|
+
locale: "en",
|
|
1130
|
+
fallback: "en",
|
|
1131
|
+
messages: {
|
|
1132
|
+
"prompt.script.system": {
|
|
1133
|
+
value: `You are a video narration script writer.
|
|
1134
|
+
Write a narration script for a short video (30-60 seconds).
|
|
1135
|
+
{styleGuide}
|
|
1136
|
+
|
|
1137
|
+
Return JSON with shape:
|
|
1138
|
+
{
|
|
1139
|
+
"segments": [{ "sceneId": string, "text": string }],
|
|
1140
|
+
"fullText": string
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
Scene IDs should be: "intro", "problems", "solutions", "metrics", "cta".
|
|
1144
|
+
Only include segments that are relevant to the brief content.`,
|
|
1145
|
+
description: "Script generator LLM system prompt",
|
|
1146
|
+
placeholders: [{ name: "styleGuide", type: "string" }]
|
|
1147
|
+
},
|
|
1148
|
+
"prompt.scenePlanner.system": {
|
|
1149
|
+
value: `You are a video scene planner for ContractSpec marketing/documentation videos.
|
|
1150
|
+
Given a content brief, break it into video scenes.
|
|
1151
|
+
|
|
1152
|
+
Each scene must have:
|
|
1153
|
+
- compositionId: one of "ApiOverview", "SocialClip", "TerminalDemo"
|
|
1154
|
+
- props: the input props for that composition (see type definitions)
|
|
1155
|
+
- durationInFrames: duration at {fps}fps
|
|
1156
|
+
- narrationText: what the narrator says during this scene
|
|
1157
|
+
|
|
1158
|
+
Return a JSON object with shape:
|
|
1159
|
+
{
|
|
1160
|
+
"scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
|
|
1161
|
+
"narrationScript": string
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
Keep the total duration around {targetSeconds} seconds.
|
|
1165
|
+
Prioritize clarity and pacing. Each scene should communicate one idea.`,
|
|
1166
|
+
description: "Scene planner LLM system prompt",
|
|
1167
|
+
placeholders: [
|
|
1168
|
+
{ name: "fps", type: "number" },
|
|
1169
|
+
{ name: "targetSeconds", type: "number" }
|
|
1170
|
+
]
|
|
1171
|
+
},
|
|
1172
|
+
"prompt.style.professional": {
|
|
1173
|
+
value: "Use a clear, authoritative, professional tone. Be concise and direct.",
|
|
1174
|
+
description: "Style guide for professional narration"
|
|
1175
|
+
},
|
|
1176
|
+
"prompt.style.casual": {
|
|
1177
|
+
value: "Use a friendly, conversational tone. Be approachable and relatable.",
|
|
1178
|
+
description: "Style guide for casual narration"
|
|
1179
|
+
},
|
|
1180
|
+
"prompt.style.technical": {
|
|
1181
|
+
value: "Use precise technical language. Be detailed and accurate.",
|
|
1182
|
+
description: "Style guide for technical narration"
|
|
1183
|
+
},
|
|
1184
|
+
"script.segment.challenge": {
|
|
1185
|
+
value: "The challenge: {content}",
|
|
1186
|
+
description: "Narration segment prefix for problems",
|
|
1187
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1188
|
+
},
|
|
1189
|
+
"script.segment.solution": {
|
|
1190
|
+
value: "The solution: {content}",
|
|
1191
|
+
description: "Narration segment prefix for solutions",
|
|
1192
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1193
|
+
},
|
|
1194
|
+
"script.segment.results": {
|
|
1195
|
+
value: "The results: {content}",
|
|
1196
|
+
description: "Narration segment prefix for metrics",
|
|
1197
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1198
|
+
},
|
|
1199
|
+
"scene.cta.default": {
|
|
1200
|
+
value: "Learn more",
|
|
1201
|
+
description: "Default call-to-action text for scenes"
|
|
1202
|
+
},
|
|
1203
|
+
"scene.hook.problem": {
|
|
1204
|
+
value: "The Problem",
|
|
1205
|
+
description: "Scene hook label for problem statement"
|
|
1206
|
+
},
|
|
1207
|
+
"scene.narration.problem": {
|
|
1208
|
+
value: "The problem: {content}",
|
|
1209
|
+
description: "Scene narration for problem statement",
|
|
1210
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1211
|
+
},
|
|
1212
|
+
"scene.hook.solution": {
|
|
1213
|
+
value: "The Solution",
|
|
1214
|
+
description: "Scene hook label for solution"
|
|
1215
|
+
},
|
|
1216
|
+
"scene.narration.solution": {
|
|
1217
|
+
value: "The solution: {content}",
|
|
1218
|
+
description: "Scene narration for solution",
|
|
1219
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1220
|
+
},
|
|
1221
|
+
"scene.hook.results": {
|
|
1222
|
+
value: "Results",
|
|
1223
|
+
description: "Scene hook label for results/metrics"
|
|
1224
|
+
},
|
|
1225
|
+
"composition.apiOverview.generates": {
|
|
1226
|
+
value: "Generates:",
|
|
1227
|
+
description: "ApiOverview heading for generated outputs"
|
|
1228
|
+
},
|
|
1229
|
+
"composition.apiOverview.tagline": {
|
|
1230
|
+
value: "One spec. Every surface.",
|
|
1231
|
+
description: "ApiOverview default tagline"
|
|
1232
|
+
},
|
|
1233
|
+
"composition.apiOverview.output.rest": {
|
|
1234
|
+
value: "REST Endpoint",
|
|
1235
|
+
description: "Generated output label: REST"
|
|
1236
|
+
},
|
|
1237
|
+
"composition.apiOverview.output.graphql": {
|
|
1238
|
+
value: "GraphQL Mutation",
|
|
1239
|
+
description: "Generated output label: GraphQL"
|
|
1240
|
+
},
|
|
1241
|
+
"composition.apiOverview.output.prisma": {
|
|
1242
|
+
value: "Prisma Model",
|
|
1243
|
+
description: "Generated output label: Prisma"
|
|
1244
|
+
},
|
|
1245
|
+
"composition.apiOverview.output.typescript": {
|
|
1246
|
+
value: "TypeScript SDK",
|
|
1247
|
+
description: "Generated output label: TypeScript SDK"
|
|
1248
|
+
},
|
|
1249
|
+
"composition.apiOverview.output.mcp": {
|
|
1250
|
+
value: "MCP Tool",
|
|
1251
|
+
description: "Generated output label: MCP Tool"
|
|
1252
|
+
},
|
|
1253
|
+
"composition.apiOverview.output.openapi": {
|
|
1254
|
+
value: "OpenAPI Spec",
|
|
1255
|
+
description: "Generated output label: OpenAPI"
|
|
1256
|
+
},
|
|
1257
|
+
"composition.socialClip.cta": {
|
|
1258
|
+
value: "Learn more",
|
|
1259
|
+
description: "SocialClip default CTA"
|
|
1260
|
+
},
|
|
1261
|
+
"composition.terminal.title": {
|
|
1262
|
+
value: "Terminal",
|
|
1263
|
+
description: "TerminalDemo default window title"
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
});
|
|
1267
|
+
|
|
1268
|
+
// src/i18n/catalogs/fr.ts
|
|
1269
|
+
import { defineTranslation as defineTranslation2 } from "@contractspec/lib.contracts-spec/translations";
|
|
1270
|
+
var frMessages = defineTranslation2({
|
|
1271
|
+
meta: {
|
|
1272
|
+
key: "video-gen.messages",
|
|
1273
|
+
version: "1.0.0",
|
|
1274
|
+
domain: "video-gen",
|
|
1275
|
+
description: "French translations for the video-gen package",
|
|
1276
|
+
owners: ["platform"],
|
|
1277
|
+
stability: "experimental"
|
|
1278
|
+
},
|
|
1279
|
+
locale: "fr",
|
|
1280
|
+
fallback: "en",
|
|
1281
|
+
messages: {
|
|
1282
|
+
"prompt.script.system": {
|
|
1283
|
+
value: `Vous êtes un rédacteur de scripts de narration vidéo.
|
|
1284
|
+
Écrivez un script de narration pour une courte vidéo (30-60 secondes).
|
|
1285
|
+
{styleGuide}
|
|
1286
|
+
|
|
1287
|
+
Retournez du JSON avec la forme :
|
|
1288
|
+
{
|
|
1289
|
+
"segments": [{ "sceneId": string, "text": string }],
|
|
1290
|
+
"fullText": string
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
Les identifiants de scène doivent être : "intro", "problems", "solutions", "metrics", "cta".
|
|
1294
|
+
N'incluez que les segments pertinents au brief.`,
|
|
1295
|
+
description: "Script generator LLM system prompt",
|
|
1296
|
+
placeholders: [{ name: "styleGuide", type: "string" }]
|
|
1297
|
+
},
|
|
1298
|
+
"prompt.scenePlanner.system": {
|
|
1299
|
+
value: `Vous êtes un planificateur de scènes vidéo pour les vidéos marketing/documentation de ContractSpec.
|
|
1300
|
+
À partir d'un brief, décomposez-le en scènes vidéo.
|
|
1301
|
+
|
|
1302
|
+
Chaque scène doit avoir :
|
|
1303
|
+
- compositionId : un parmi "ApiOverview", "SocialClip", "TerminalDemo"
|
|
1304
|
+
- props : les propriétés d'entrée de cette composition
|
|
1305
|
+
- durationInFrames : durée à {fps} fps
|
|
1306
|
+
- narrationText : ce que le narrateur dit pendant cette scène
|
|
1307
|
+
|
|
1308
|
+
Retournez un objet JSON avec la forme :
|
|
1309
|
+
{
|
|
1310
|
+
"scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
|
|
1311
|
+
"narrationScript": string
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
Gardez la durée totale autour de {targetSeconds} secondes.
|
|
1315
|
+
Privilégiez la clarté et le rythme. Chaque scène doit communiquer une idée.`,
|
|
1316
|
+
description: "Scene planner LLM system prompt",
|
|
1317
|
+
placeholders: [
|
|
1318
|
+
{ name: "fps", type: "number" },
|
|
1319
|
+
{ name: "targetSeconds", type: "number" }
|
|
1320
|
+
]
|
|
1321
|
+
},
|
|
1322
|
+
"prompt.style.professional": {
|
|
1323
|
+
value: "Utilisez un ton clair, autoritaire et professionnel. Soyez concis et direct.",
|
|
1324
|
+
description: "Style guide for professional narration"
|
|
1325
|
+
},
|
|
1326
|
+
"prompt.style.casual": {
|
|
1327
|
+
value: "Utilisez un ton amical et conversationnel. Soyez accessible et proche.",
|
|
1328
|
+
description: "Style guide for casual narration"
|
|
1329
|
+
},
|
|
1330
|
+
"prompt.style.technical": {
|
|
1331
|
+
value: "Utilisez un langage technique précis. Soyez détaillé et exact.",
|
|
1332
|
+
description: "Style guide for technical narration"
|
|
1333
|
+
},
|
|
1334
|
+
"script.segment.challenge": {
|
|
1335
|
+
value: "Le défi : {content}",
|
|
1336
|
+
description: "Narration segment prefix for problems",
|
|
1337
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1338
|
+
},
|
|
1339
|
+
"script.segment.solution": {
|
|
1340
|
+
value: "La solution : {content}",
|
|
1341
|
+
description: "Narration segment prefix for solutions",
|
|
1342
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1343
|
+
},
|
|
1344
|
+
"script.segment.results": {
|
|
1345
|
+
value: "Les résultats : {content}",
|
|
1346
|
+
description: "Narration segment prefix for metrics",
|
|
1347
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1348
|
+
},
|
|
1349
|
+
"scene.cta.default": {
|
|
1350
|
+
value: "En savoir plus",
|
|
1351
|
+
description: "Default call-to-action text for scenes"
|
|
1352
|
+
},
|
|
1353
|
+
"scene.hook.problem": {
|
|
1354
|
+
value: "Le problème",
|
|
1355
|
+
description: "Scene hook label for problem statement"
|
|
1356
|
+
},
|
|
1357
|
+
"scene.narration.problem": {
|
|
1358
|
+
value: "Le problème : {content}",
|
|
1359
|
+
description: "Scene narration for problem statement",
|
|
1360
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1361
|
+
},
|
|
1362
|
+
"scene.hook.solution": {
|
|
1363
|
+
value: "La solution",
|
|
1364
|
+
description: "Scene hook label for solution"
|
|
1365
|
+
},
|
|
1366
|
+
"scene.narration.solution": {
|
|
1367
|
+
value: "La solution : {content}",
|
|
1368
|
+
description: "Scene narration for solution",
|
|
1369
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1370
|
+
},
|
|
1371
|
+
"scene.hook.results": {
|
|
1372
|
+
value: "Résultats",
|
|
1373
|
+
description: "Scene hook label for results/metrics"
|
|
1374
|
+
},
|
|
1375
|
+
"composition.apiOverview.generates": {
|
|
1376
|
+
value: "Génère :",
|
|
1377
|
+
description: "ApiOverview heading for generated outputs"
|
|
1378
|
+
},
|
|
1379
|
+
"composition.apiOverview.tagline": {
|
|
1380
|
+
value: "Un spec. Toutes les surfaces.",
|
|
1381
|
+
description: "ApiOverview default tagline"
|
|
1382
|
+
},
|
|
1383
|
+
"composition.apiOverview.output.rest": {
|
|
1384
|
+
value: "Endpoint REST",
|
|
1385
|
+
description: "Generated output label: REST"
|
|
1386
|
+
},
|
|
1387
|
+
"composition.apiOverview.output.graphql": {
|
|
1388
|
+
value: "Mutation GraphQL",
|
|
1389
|
+
description: "Generated output label: GraphQL"
|
|
1390
|
+
},
|
|
1391
|
+
"composition.apiOverview.output.prisma": {
|
|
1392
|
+
value: "Modèle Prisma",
|
|
1393
|
+
description: "Generated output label: Prisma"
|
|
1394
|
+
},
|
|
1395
|
+
"composition.apiOverview.output.typescript": {
|
|
1396
|
+
value: "SDK TypeScript",
|
|
1397
|
+
description: "Generated output label: TypeScript SDK"
|
|
1398
|
+
},
|
|
1399
|
+
"composition.apiOverview.output.mcp": {
|
|
1400
|
+
value: "Outil MCP",
|
|
1401
|
+
description: "Generated output label: MCP Tool"
|
|
1402
|
+
},
|
|
1403
|
+
"composition.apiOverview.output.openapi": {
|
|
1404
|
+
value: "Spec OpenAPI",
|
|
1405
|
+
description: "Generated output label: OpenAPI"
|
|
1406
|
+
},
|
|
1407
|
+
"composition.socialClip.cta": {
|
|
1408
|
+
value: "En savoir plus",
|
|
1409
|
+
description: "SocialClip default CTA"
|
|
1410
|
+
},
|
|
1411
|
+
"composition.terminal.title": {
|
|
1412
|
+
value: "Terminal",
|
|
1413
|
+
description: "TerminalDemo default window title"
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
// src/i18n/catalogs/es.ts
|
|
1419
|
+
import { defineTranslation as defineTranslation3 } from "@contractspec/lib.contracts-spec/translations";
|
|
1420
|
+
var esMessages = defineTranslation3({
|
|
1421
|
+
meta: {
|
|
1422
|
+
key: "video-gen.messages",
|
|
1423
|
+
version: "1.0.0",
|
|
1424
|
+
domain: "video-gen",
|
|
1425
|
+
description: "Spanish translations for the video-gen package",
|
|
1426
|
+
owners: ["platform"],
|
|
1427
|
+
stability: "experimental"
|
|
1428
|
+
},
|
|
1429
|
+
locale: "es",
|
|
1430
|
+
fallback: "en",
|
|
1431
|
+
messages: {
|
|
1432
|
+
"prompt.script.system": {
|
|
1433
|
+
value: `Eres un redactor de guiones de narración para vídeo.
|
|
1434
|
+
Escribe un guión de narración para un vídeo corto (30-60 segundos).
|
|
1435
|
+
{styleGuide}
|
|
1436
|
+
|
|
1437
|
+
Devuelve JSON con la forma:
|
|
1438
|
+
{
|
|
1439
|
+
"segments": [{ "sceneId": string, "text": string }],
|
|
1440
|
+
"fullText": string
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
Los identificadores de escena deben ser: "intro", "problems", "solutions", "metrics", "cta".
|
|
1444
|
+
Incluye solo los segmentos relevantes para el brief.`,
|
|
1445
|
+
description: "Script generator LLM system prompt",
|
|
1446
|
+
placeholders: [{ name: "styleGuide", type: "string" }]
|
|
1447
|
+
},
|
|
1448
|
+
"prompt.scenePlanner.system": {
|
|
1449
|
+
value: `Eres un planificador de escenas de vídeo para vídeos de marketing/documentación de ContractSpec.
|
|
1450
|
+
Dado un brief de contenido, divídelo en escenas de vídeo.
|
|
1451
|
+
|
|
1452
|
+
Cada escena debe tener:
|
|
1453
|
+
- compositionId: uno de "ApiOverview", "SocialClip", "TerminalDemo"
|
|
1454
|
+
- props: las propiedades de entrada de esa composición
|
|
1455
|
+
- durationInFrames: duración a {fps} fps
|
|
1456
|
+
- narrationText: lo que dice el narrador durante esta escena
|
|
1457
|
+
|
|
1458
|
+
Devuelve un objeto JSON con la forma:
|
|
1459
|
+
{
|
|
1460
|
+
"scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
|
|
1461
|
+
"narrationScript": string
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
Mantén la duración total alrededor de {targetSeconds} segundos.
|
|
1465
|
+
Prioriza la claridad y el ritmo. Cada escena debe comunicar una idea.`,
|
|
1466
|
+
description: "Scene planner LLM system prompt",
|
|
1467
|
+
placeholders: [
|
|
1468
|
+
{ name: "fps", type: "number" },
|
|
1469
|
+
{ name: "targetSeconds", type: "number" }
|
|
1470
|
+
]
|
|
1471
|
+
},
|
|
1472
|
+
"prompt.style.professional": {
|
|
1473
|
+
value: "Usa un tono claro, autoritario y profesional. Sé conciso y directo.",
|
|
1474
|
+
description: "Style guide for professional narration"
|
|
1475
|
+
},
|
|
1476
|
+
"prompt.style.casual": {
|
|
1477
|
+
value: "Usa un tono amigable y conversacional. Sé accesible y cercano.",
|
|
1478
|
+
description: "Style guide for casual narration"
|
|
1479
|
+
},
|
|
1480
|
+
"prompt.style.technical": {
|
|
1481
|
+
value: "Usa un lenguaje técnico preciso. Sé detallado y exacto.",
|
|
1482
|
+
description: "Style guide for technical narration"
|
|
1483
|
+
},
|
|
1484
|
+
"script.segment.challenge": {
|
|
1485
|
+
value: "El desafío: {content}",
|
|
1486
|
+
description: "Narration segment prefix for problems",
|
|
1487
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1488
|
+
},
|
|
1489
|
+
"script.segment.solution": {
|
|
1490
|
+
value: "La solución: {content}",
|
|
1491
|
+
description: "Narration segment prefix for solutions",
|
|
1492
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1493
|
+
},
|
|
1494
|
+
"script.segment.results": {
|
|
1495
|
+
value: "Los resultados: {content}",
|
|
1496
|
+
description: "Narration segment prefix for metrics",
|
|
1497
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1498
|
+
},
|
|
1499
|
+
"scene.cta.default": {
|
|
1500
|
+
value: "Más información",
|
|
1501
|
+
description: "Default call-to-action text for scenes"
|
|
1502
|
+
},
|
|
1503
|
+
"scene.hook.problem": {
|
|
1504
|
+
value: "El problema",
|
|
1505
|
+
description: "Scene hook label for problem statement"
|
|
1506
|
+
},
|
|
1507
|
+
"scene.narration.problem": {
|
|
1508
|
+
value: "El problema: {content}",
|
|
1509
|
+
description: "Scene narration for problem statement",
|
|
1510
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1511
|
+
},
|
|
1512
|
+
"scene.hook.solution": {
|
|
1513
|
+
value: "La solución",
|
|
1514
|
+
description: "Scene hook label for solution"
|
|
1515
|
+
},
|
|
1516
|
+
"scene.narration.solution": {
|
|
1517
|
+
value: "La solución: {content}",
|
|
1518
|
+
description: "Scene narration for solution",
|
|
1519
|
+
placeholders: [{ name: "content", type: "string" }]
|
|
1520
|
+
},
|
|
1521
|
+
"scene.hook.results": {
|
|
1522
|
+
value: "Resultados",
|
|
1523
|
+
description: "Scene hook label for results/metrics"
|
|
1524
|
+
},
|
|
1525
|
+
"composition.apiOverview.generates": {
|
|
1526
|
+
value: "Genera:",
|
|
1527
|
+
description: "ApiOverview heading for generated outputs"
|
|
1528
|
+
},
|
|
1529
|
+
"composition.apiOverview.tagline": {
|
|
1530
|
+
value: "Una spec. Todas las superficies.",
|
|
1531
|
+
description: "ApiOverview default tagline"
|
|
1532
|
+
},
|
|
1533
|
+
"composition.apiOverview.output.rest": {
|
|
1534
|
+
value: "Endpoint REST",
|
|
1535
|
+
description: "Generated output label: REST"
|
|
1536
|
+
},
|
|
1537
|
+
"composition.apiOverview.output.graphql": {
|
|
1538
|
+
value: "Mutación GraphQL",
|
|
1539
|
+
description: "Generated output label: GraphQL"
|
|
1540
|
+
},
|
|
1541
|
+
"composition.apiOverview.output.prisma": {
|
|
1542
|
+
value: "Modelo Prisma",
|
|
1543
|
+
description: "Generated output label: Prisma"
|
|
1544
|
+
},
|
|
1545
|
+
"composition.apiOverview.output.typescript": {
|
|
1546
|
+
value: "SDK TypeScript",
|
|
1547
|
+
description: "Generated output label: TypeScript SDK"
|
|
1548
|
+
},
|
|
1549
|
+
"composition.apiOverview.output.mcp": {
|
|
1550
|
+
value: "Herramienta MCP",
|
|
1551
|
+
description: "Generated output label: MCP Tool"
|
|
1552
|
+
},
|
|
1553
|
+
"composition.apiOverview.output.openapi": {
|
|
1554
|
+
value: "Spec OpenAPI",
|
|
1555
|
+
description: "Generated output label: OpenAPI"
|
|
1556
|
+
},
|
|
1557
|
+
"composition.socialClip.cta": {
|
|
1558
|
+
value: "Más información",
|
|
1559
|
+
description: "SocialClip default CTA"
|
|
1560
|
+
},
|
|
1561
|
+
"composition.terminal.title": {
|
|
1562
|
+
value: "Terminal",
|
|
1563
|
+
description: "TerminalDemo default window title"
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
|
|
1568
|
+
// src/i18n/messages.ts
|
|
1569
|
+
import {
|
|
1570
|
+
createI18nFactory
|
|
1571
|
+
} from "@contractspec/lib.contracts-spec/translations";
|
|
1572
|
+
var factory = createI18nFactory({
|
|
1573
|
+
specKey: "video-gen.messages",
|
|
1574
|
+
catalogs: [enMessages, frMessages, esMessages]
|
|
1575
|
+
});
|
|
1576
|
+
var createVideoGenI18n = factory.create;
|
|
1577
|
+
var getDefaultI18n = factory.getDefault;
|
|
1578
|
+
var resetI18nRegistry = factory.resetRegistry;
|
|
1579
|
+
|
|
1580
|
+
// src/i18n/locale.ts
|
|
1581
|
+
import {
|
|
1582
|
+
DEFAULT_LOCALE,
|
|
1583
|
+
SUPPORTED_LOCALES,
|
|
1584
|
+
resolveLocale,
|
|
1585
|
+
isSupportedLocale
|
|
1586
|
+
} from "@contractspec/lib.contracts-spec/translations";
|
|
1587
|
+
|
|
1588
|
+
// src/i18n/keys.ts
|
|
1589
|
+
var PROMPT_KEYS = {
|
|
1590
|
+
"prompt.script.system": "prompt.script.system",
|
|
1591
|
+
"prompt.scenePlanner.system": "prompt.scenePlanner.system",
|
|
1592
|
+
"prompt.style.professional": "prompt.style.professional",
|
|
1593
|
+
"prompt.style.casual": "prompt.style.casual",
|
|
1594
|
+
"prompt.style.technical": "prompt.style.technical"
|
|
1595
|
+
};
|
|
1596
|
+
var SCRIPT_KEYS = {
|
|
1597
|
+
"script.segment.challenge": "script.segment.challenge",
|
|
1598
|
+
"script.segment.solution": "script.segment.solution",
|
|
1599
|
+
"script.segment.results": "script.segment.results"
|
|
1600
|
+
};
|
|
1601
|
+
var SCENE_KEYS = {
|
|
1602
|
+
"scene.cta.default": "scene.cta.default",
|
|
1603
|
+
"scene.hook.problem": "scene.hook.problem",
|
|
1604
|
+
"scene.narration.problem": "scene.narration.problem",
|
|
1605
|
+
"scene.hook.solution": "scene.hook.solution",
|
|
1606
|
+
"scene.narration.solution": "scene.narration.solution",
|
|
1607
|
+
"scene.hook.results": "scene.hook.results"
|
|
1608
|
+
};
|
|
1609
|
+
var COMPOSITION_KEYS = {
|
|
1610
|
+
"composition.apiOverview.generates": "composition.apiOverview.generates",
|
|
1611
|
+
"composition.apiOverview.tagline": "composition.apiOverview.tagline",
|
|
1612
|
+
"composition.apiOverview.output.rest": "composition.apiOverview.output.rest",
|
|
1613
|
+
"composition.apiOverview.output.graphql": "composition.apiOverview.output.graphql",
|
|
1614
|
+
"composition.apiOverview.output.prisma": "composition.apiOverview.output.prisma",
|
|
1615
|
+
"composition.apiOverview.output.typescript": "composition.apiOverview.output.typescript",
|
|
1616
|
+
"composition.apiOverview.output.mcp": "composition.apiOverview.output.mcp",
|
|
1617
|
+
"composition.apiOverview.output.openapi": "composition.apiOverview.output.openapi",
|
|
1618
|
+
"composition.socialClip.cta": "composition.socialClip.cta",
|
|
1619
|
+
"composition.terminal.title": "composition.terminal.title"
|
|
1620
|
+
};
|
|
1621
|
+
var I18N_KEYS = {
|
|
1622
|
+
...PROMPT_KEYS,
|
|
1623
|
+
...SCRIPT_KEYS,
|
|
1624
|
+
...SCENE_KEYS,
|
|
1625
|
+
...COMPOSITION_KEYS
|
|
1626
|
+
};
|
|
1118
1627
|
// src/generators/scene-planner.ts
|
|
1119
1628
|
class ScenePlanner {
|
|
1120
1629
|
llm;
|
|
1121
1630
|
model;
|
|
1122
1631
|
temperature;
|
|
1123
1632
|
fps;
|
|
1633
|
+
i18n;
|
|
1124
1634
|
constructor(options) {
|
|
1125
1635
|
this.llm = options?.llm;
|
|
1126
1636
|
this.model = options?.model;
|
|
1127
1637
|
this.temperature = options?.temperature ?? 0.3;
|
|
1128
1638
|
this.fps = options?.fps ?? DEFAULT_FPS;
|
|
1639
|
+
this.i18n = createVideoGenI18n(options?.locale);
|
|
1129
1640
|
}
|
|
1130
1641
|
async plan(brief) {
|
|
1131
1642
|
if (this.llm) {
|
|
@@ -1135,6 +1646,7 @@ class ScenePlanner {
|
|
|
1135
1646
|
}
|
|
1136
1647
|
planDeterministic(brief) {
|
|
1137
1648
|
const { content } = brief;
|
|
1649
|
+
const { t } = this.i18n;
|
|
1138
1650
|
const scenes = [];
|
|
1139
1651
|
const fps = this.fps;
|
|
1140
1652
|
scenes.push({
|
|
@@ -1143,7 +1655,7 @@ class ScenePlanner {
|
|
|
1143
1655
|
hook: content.title,
|
|
1144
1656
|
message: content.summary,
|
|
1145
1657
|
points: content.solutions.slice(0, 3),
|
|
1146
|
-
cta: content.callToAction ?? "
|
|
1658
|
+
cta: content.callToAction ?? t("scene.cta.default")
|
|
1147
1659
|
},
|
|
1148
1660
|
durationInFrames: 3 * fps,
|
|
1149
1661
|
narrationText: `${content.title}. ${content.summary}`
|
|
@@ -1152,31 +1664,35 @@ class ScenePlanner {
|
|
|
1152
1664
|
scenes.push({
|
|
1153
1665
|
compositionId: "SocialClip",
|
|
1154
1666
|
props: {
|
|
1155
|
-
hook: "
|
|
1667
|
+
hook: t("scene.hook.problem"),
|
|
1156
1668
|
message: content.problems[0] ?? "",
|
|
1157
1669
|
points: content.problems.slice(1, 4)
|
|
1158
1670
|
},
|
|
1159
1671
|
durationInFrames: 4 * fps,
|
|
1160
|
-
narrationText:
|
|
1672
|
+
narrationText: t("scene.narration.problem", {
|
|
1673
|
+
content: content.problems.join(". ")
|
|
1674
|
+
})
|
|
1161
1675
|
});
|
|
1162
1676
|
}
|
|
1163
1677
|
if (content.solutions.length > 0) {
|
|
1164
1678
|
scenes.push({
|
|
1165
1679
|
compositionId: "SocialClip",
|
|
1166
1680
|
props: {
|
|
1167
|
-
hook: "
|
|
1681
|
+
hook: t("scene.hook.solution"),
|
|
1168
1682
|
message: content.solutions[0] ?? "",
|
|
1169
1683
|
points: content.solutions.slice(1, 4)
|
|
1170
1684
|
},
|
|
1171
1685
|
durationInFrames: 5 * fps,
|
|
1172
|
-
narrationText:
|
|
1686
|
+
narrationText: t("scene.narration.solution", {
|
|
1687
|
+
content: content.solutions.join(". ")
|
|
1688
|
+
})
|
|
1173
1689
|
});
|
|
1174
1690
|
}
|
|
1175
1691
|
if (content.metrics && content.metrics.length > 0) {
|
|
1176
1692
|
scenes.push({
|
|
1177
1693
|
compositionId: "SocialClip",
|
|
1178
1694
|
props: {
|
|
1179
|
-
hook: "
|
|
1695
|
+
hook: t("scene.hook.results"),
|
|
1180
1696
|
message: content.metrics[0] ?? "",
|
|
1181
1697
|
points: content.metrics.slice(1, 3)
|
|
1182
1698
|
},
|
|
@@ -1213,29 +1729,17 @@ class ScenePlanner {
|
|
|
1213
1729
|
};
|
|
1214
1730
|
}
|
|
1215
1731
|
async planWithLlm(brief) {
|
|
1732
|
+
const { t } = this.i18n;
|
|
1216
1733
|
const messages = [
|
|
1217
1734
|
{
|
|
1218
1735
|
role: "system",
|
|
1219
1736
|
content: [
|
|
1220
1737
|
{
|
|
1221
1738
|
type: "text",
|
|
1222
|
-
text:
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
- compositionId: one of "ApiOverview", "SocialClip", "TerminalDemo"
|
|
1227
|
-
- props: the input props for that composition (see type definitions)
|
|
1228
|
-
- durationInFrames: duration at ${this.fps}fps
|
|
1229
|
-
- narrationText: what the narrator says during this scene
|
|
1230
|
-
|
|
1231
|
-
Return a JSON object with shape:
|
|
1232
|
-
{
|
|
1233
|
-
"scenes": [{ "compositionId": string, "props": object, "durationInFrames": number, "narrationText": string }],
|
|
1234
|
-
"narrationScript": string
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
Keep the total duration around ${brief.targetDurationSeconds ?? 30} seconds.
|
|
1238
|
-
Prioritize clarity and pacing. Each scene should communicate one idea.`
|
|
1739
|
+
text: t("prompt.scenePlanner.system", {
|
|
1740
|
+
fps: this.fps,
|
|
1741
|
+
targetSeconds: brief.targetDurationSeconds ?? 30
|
|
1742
|
+
})
|
|
1239
1743
|
}
|
|
1240
1744
|
]
|
|
1241
1745
|
},
|
|
@@ -1275,190 +1779,29 @@ Prioritize clarity and pacing. Each scene should communicate one idea.`
|
|
|
1275
1779
|
}
|
|
1276
1780
|
}
|
|
1277
1781
|
|
|
1278
|
-
// src/generators/script-generator.ts
|
|
1279
|
-
class ScriptGenerator {
|
|
1280
|
-
llm;
|
|
1281
|
-
model;
|
|
1282
|
-
temperature;
|
|
1283
|
-
constructor(options) {
|
|
1284
|
-
this.llm = options?.llm;
|
|
1285
|
-
this.model = options?.model;
|
|
1286
|
-
this.temperature = options?.temperature ?? 0.5;
|
|
1287
|
-
}
|
|
1288
|
-
async generate(brief, config) {
|
|
1289
|
-
const style = config?.style ?? "professional";
|
|
1290
|
-
if (this.llm) {
|
|
1291
|
-
return this.generateWithLlm(brief, style);
|
|
1292
|
-
}
|
|
1293
|
-
return this.generateDeterministic(brief, style);
|
|
1294
|
-
}
|
|
1295
|
-
generateDeterministic(brief, style) {
|
|
1296
|
-
const segments = [];
|
|
1297
|
-
const intro = this.formatForStyle(`${brief.title}. ${brief.summary}`, style);
|
|
1298
|
-
segments.push({
|
|
1299
|
-
sceneId: "intro",
|
|
1300
|
-
text: intro,
|
|
1301
|
-
estimatedDurationSeconds: this.estimateDuration(intro)
|
|
1302
|
-
});
|
|
1303
|
-
if (brief.problems.length > 0) {
|
|
1304
|
-
const problemText = this.formatForStyle(`The challenge: ${brief.problems.join(". ")}`, style);
|
|
1305
|
-
segments.push({
|
|
1306
|
-
sceneId: "problems",
|
|
1307
|
-
text: problemText,
|
|
1308
|
-
estimatedDurationSeconds: this.estimateDuration(problemText)
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
if (brief.solutions.length > 0) {
|
|
1312
|
-
const solutionText = this.formatForStyle(`The solution: ${brief.solutions.join(". ")}`, style);
|
|
1313
|
-
segments.push({
|
|
1314
|
-
sceneId: "solutions",
|
|
1315
|
-
text: solutionText,
|
|
1316
|
-
estimatedDurationSeconds: this.estimateDuration(solutionText)
|
|
1317
|
-
});
|
|
1318
|
-
}
|
|
1319
|
-
if (brief.metrics && brief.metrics.length > 0) {
|
|
1320
|
-
const metricsText = this.formatForStyle(`The results: ${brief.metrics.join(". ")}`, style);
|
|
1321
|
-
segments.push({
|
|
1322
|
-
sceneId: "metrics",
|
|
1323
|
-
text: metricsText,
|
|
1324
|
-
estimatedDurationSeconds: this.estimateDuration(metricsText)
|
|
1325
|
-
});
|
|
1326
|
-
}
|
|
1327
|
-
if (brief.callToAction) {
|
|
1328
|
-
const ctaText = this.formatForStyle(brief.callToAction, style);
|
|
1329
|
-
segments.push({
|
|
1330
|
-
sceneId: "cta",
|
|
1331
|
-
text: ctaText,
|
|
1332
|
-
estimatedDurationSeconds: this.estimateDuration(ctaText)
|
|
1333
|
-
});
|
|
1334
|
-
}
|
|
1335
|
-
const fullText = segments.map((s) => s.text).join(" ");
|
|
1336
|
-
const totalDuration = segments.reduce((sum, s) => sum + s.estimatedDurationSeconds, 0);
|
|
1337
|
-
return {
|
|
1338
|
-
fullText,
|
|
1339
|
-
segments,
|
|
1340
|
-
estimatedDurationSeconds: totalDuration,
|
|
1341
|
-
style
|
|
1342
|
-
};
|
|
1343
|
-
}
|
|
1344
|
-
async generateWithLlm(brief, style) {
|
|
1345
|
-
const styleGuide = {
|
|
1346
|
-
professional: "Use a clear, authoritative, professional tone. Be concise and direct.",
|
|
1347
|
-
casual: "Use a friendly, conversational tone. Be approachable and relatable.",
|
|
1348
|
-
technical: "Use precise technical language. Be detailed and accurate."
|
|
1349
|
-
};
|
|
1350
|
-
const styleKey = style ?? "professional";
|
|
1351
|
-
const messages = [
|
|
1352
|
-
{
|
|
1353
|
-
role: "system",
|
|
1354
|
-
content: [
|
|
1355
|
-
{
|
|
1356
|
-
type: "text",
|
|
1357
|
-
text: `You are a video narration script writer.
|
|
1358
|
-
Write a narration script for a short video (30-60 seconds).
|
|
1359
|
-
${styleGuide[styleKey]}
|
|
1360
|
-
|
|
1361
|
-
Return JSON with shape:
|
|
1362
|
-
{
|
|
1363
|
-
"segments": [{ "sceneId": string, "text": string }],
|
|
1364
|
-
"fullText": string
|
|
1365
|
-
}
|
|
1366
|
-
|
|
1367
|
-
Scene IDs should be: "intro", "problems", "solutions", "metrics", "cta".
|
|
1368
|
-
Only include segments that are relevant to the brief content.`
|
|
1369
|
-
}
|
|
1370
|
-
]
|
|
1371
|
-
},
|
|
1372
|
-
{
|
|
1373
|
-
role: "user",
|
|
1374
|
-
content: [{ type: "text", text: JSON.stringify(brief) }]
|
|
1375
|
-
}
|
|
1376
|
-
];
|
|
1377
|
-
if (!this.llm) {
|
|
1378
|
-
return this.generateDeterministic(brief, style);
|
|
1379
|
-
}
|
|
1380
|
-
try {
|
|
1381
|
-
const response = await this.llm.chat(messages, {
|
|
1382
|
-
model: this.model,
|
|
1383
|
-
temperature: this.temperature,
|
|
1384
|
-
responseFormat: "json"
|
|
1385
|
-
});
|
|
1386
|
-
const text = response.message.content.find((p) => p.type === "text");
|
|
1387
|
-
if (!text || text.type !== "text") {
|
|
1388
|
-
return this.generateDeterministic(brief, style);
|
|
1389
|
-
}
|
|
1390
|
-
const parsed = JSON.parse(text.text);
|
|
1391
|
-
const segments = parsed.segments.map((s) => ({
|
|
1392
|
-
sceneId: s.sceneId,
|
|
1393
|
-
text: s.text,
|
|
1394
|
-
estimatedDurationSeconds: this.estimateDuration(s.text)
|
|
1395
|
-
}));
|
|
1396
|
-
return {
|
|
1397
|
-
fullText: parsed.fullText,
|
|
1398
|
-
segments,
|
|
1399
|
-
estimatedDurationSeconds: segments.reduce((sum, s) => sum + s.estimatedDurationSeconds, 0),
|
|
1400
|
-
style
|
|
1401
|
-
};
|
|
1402
|
-
} catch {
|
|
1403
|
-
return this.generateDeterministic(brief, style);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
formatForStyle(text, _style) {
|
|
1407
|
-
return text;
|
|
1408
|
-
}
|
|
1409
|
-
estimateDuration(text) {
|
|
1410
|
-
const wordCount = text.split(/\s+/).length;
|
|
1411
|
-
return Math.ceil(wordCount / 150 * 60);
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
|
|
1415
1782
|
// src/generators/video-generator.ts
|
|
1416
1783
|
import { VIDEO_FORMATS as VIDEO_FORMATS2 } from "@contractspec/lib.contracts-integrations/integrations/providers/video";
|
|
1417
1784
|
class VideoGenerator {
|
|
1418
1785
|
scenePlanner;
|
|
1419
|
-
scriptGenerator;
|
|
1420
1786
|
voice;
|
|
1421
|
-
|
|
1787
|
+
transcriber;
|
|
1788
|
+
image;
|
|
1422
1789
|
fps;
|
|
1423
1790
|
constructor(options) {
|
|
1424
1791
|
this.fps = options?.fps ?? DEFAULT_FPS;
|
|
1425
1792
|
this.voice = options?.voice;
|
|
1426
|
-
this.
|
|
1793
|
+
this.transcriber = options?.transcriber;
|
|
1794
|
+
this.image = options?.image;
|
|
1427
1795
|
this.scenePlanner = new ScenePlanner({
|
|
1428
1796
|
llm: options?.llm,
|
|
1429
1797
|
model: options?.model,
|
|
1430
1798
|
temperature: options?.temperature,
|
|
1431
|
-
fps: this.fps
|
|
1432
|
-
|
|
1433
|
-
this.scriptGenerator = new ScriptGenerator({
|
|
1434
|
-
llm: options?.llm,
|
|
1435
|
-
model: options?.model,
|
|
1436
|
-
temperature: options?.temperature
|
|
1799
|
+
fps: this.fps,
|
|
1800
|
+
locale: options?.locale
|
|
1437
1801
|
});
|
|
1438
1802
|
}
|
|
1439
1803
|
async generate(brief) {
|
|
1440
1804
|
const scenePlan = await this.scenePlanner.plan(brief);
|
|
1441
|
-
let narrationAudio;
|
|
1442
|
-
if (brief.narration?.enabled && this.voice) {
|
|
1443
|
-
const script = await this.scriptGenerator.generate(brief.content, brief.narration);
|
|
1444
|
-
const voiceId = brief.narration.voiceId ?? this.defaultVoiceId;
|
|
1445
|
-
if (voiceId && script.fullText) {
|
|
1446
|
-
try {
|
|
1447
|
-
const result = await this.voice.synthesize({
|
|
1448
|
-
text: script.fullText,
|
|
1449
|
-
voiceId,
|
|
1450
|
-
format: "mp3"
|
|
1451
|
-
});
|
|
1452
|
-
narrationAudio = {
|
|
1453
|
-
data: result.audio,
|
|
1454
|
-
format: "mp3",
|
|
1455
|
-
durationSeconds: result.durationSeconds ?? script.estimatedDurationSeconds,
|
|
1456
|
-
volume: 1
|
|
1457
|
-
};
|
|
1458
|
-
} catch {}
|
|
1459
|
-
}
|
|
1460
|
-
}
|
|
1461
|
-
const format = brief.format ?? VIDEO_FORMATS2.landscape;
|
|
1462
1805
|
const scenes = scenePlan.scenes.map((planned, i) => ({
|
|
1463
1806
|
id: `scene-${i}`,
|
|
1464
1807
|
compositionId: planned.compositionId,
|
|
@@ -1466,6 +1809,70 @@ class VideoGenerator {
|
|
|
1466
1809
|
durationInFrames: planned.durationInFrames,
|
|
1467
1810
|
narrationText: planned.narrationText
|
|
1468
1811
|
}));
|
|
1812
|
+
let ttsProject;
|
|
1813
|
+
let narrationAudio;
|
|
1814
|
+
if (brief.narration?.enabled && this.voice) {
|
|
1815
|
+
try {
|
|
1816
|
+
ttsProject = await this.voice.synthesizeForVideo({
|
|
1817
|
+
content: brief.content,
|
|
1818
|
+
scenePlan: {
|
|
1819
|
+
scenes: scenes.map((s) => ({
|
|
1820
|
+
id: s.id,
|
|
1821
|
+
compositionId: s.compositionId,
|
|
1822
|
+
durationInFrames: s.durationInFrames,
|
|
1823
|
+
narrationText: s.narrationText
|
|
1824
|
+
})),
|
|
1825
|
+
estimatedDurationSeconds: scenePlan.estimatedDurationSeconds
|
|
1826
|
+
},
|
|
1827
|
+
voice: { voiceId: brief.narration.voiceId ?? "" },
|
|
1828
|
+
pacing: { strategy: "scene-matched" },
|
|
1829
|
+
fps: this.fps
|
|
1830
|
+
});
|
|
1831
|
+
if (ttsProject.timingMap) {
|
|
1832
|
+
for (const seg of ttsProject.timingMap.segments) {
|
|
1833
|
+
const scene = scenes.find((s) => s.id === seg.sceneId);
|
|
1834
|
+
if (scene) {
|
|
1835
|
+
scene.durationInFrames = seg.recommendedSceneDurationInFrames;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if (ttsProject.assembledAudio) {
|
|
1840
|
+
narrationAudio = {
|
|
1841
|
+
data: ttsProject.assembledAudio.data,
|
|
1842
|
+
format: ttsProject.assembledAudio.format === "wav" ? "wav" : "mp3",
|
|
1843
|
+
durationSeconds: (ttsProject.assembledAudio.durationMs ?? 0) / 1000,
|
|
1844
|
+
volume: 1
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
} catch {}
|
|
1848
|
+
}
|
|
1849
|
+
let subtitles;
|
|
1850
|
+
if (this.transcriber && ttsProject?.assembledAudio) {
|
|
1851
|
+
try {
|
|
1852
|
+
const transcription = await this.transcriber.transcribe({
|
|
1853
|
+
audio: ttsProject.assembledAudio,
|
|
1854
|
+
subtitleFormat: "vtt"
|
|
1855
|
+
});
|
|
1856
|
+
subtitles = transcription.subtitles;
|
|
1857
|
+
} catch {}
|
|
1858
|
+
}
|
|
1859
|
+
let thumbnail;
|
|
1860
|
+
if (this.image) {
|
|
1861
|
+
try {
|
|
1862
|
+
const thumbProject = await this.image.generate({
|
|
1863
|
+
content: brief.content,
|
|
1864
|
+
purpose: "video-thumbnail",
|
|
1865
|
+
format: "png",
|
|
1866
|
+
style: "photorealistic"
|
|
1867
|
+
});
|
|
1868
|
+
const imageUrl = thumbProject.results?.images[0]?.url;
|
|
1869
|
+
thumbnail = {
|
|
1870
|
+
prompt: thumbProject.prompt.text,
|
|
1871
|
+
...imageUrl ? { imageUrl } : {}
|
|
1872
|
+
};
|
|
1873
|
+
} catch {}
|
|
1874
|
+
}
|
|
1875
|
+
const format = brief.format ?? VIDEO_FORMATS2.landscape;
|
|
1469
1876
|
const totalDurationInFrames = scenes.reduce((sum, s) => sum + s.durationInFrames, 0);
|
|
1470
1877
|
const project = {
|
|
1471
1878
|
id: generateProjectId(),
|
|
@@ -1473,7 +1880,10 @@ class VideoGenerator {
|
|
|
1473
1880
|
totalDurationInFrames,
|
|
1474
1881
|
fps: this.fps,
|
|
1475
1882
|
format,
|
|
1476
|
-
audio: narrationAudio ? { narration: narrationAudio } : undefined
|
|
1883
|
+
audio: narrationAudio ? { narration: narrationAudio } : undefined,
|
|
1884
|
+
subtitles,
|
|
1885
|
+
voiceTimingMap: ttsProject?.timingMap,
|
|
1886
|
+
thumbnail
|
|
1477
1887
|
};
|
|
1478
1888
|
return project;
|
|
1479
1889
|
}
|
|
@@ -1533,7 +1943,6 @@ export {
|
|
|
1533
1943
|
TerminalDemo,
|
|
1534
1944
|
Terminal,
|
|
1535
1945
|
SocialClip,
|
|
1536
|
-
ScriptGenerator,
|
|
1537
1946
|
SceneTransitionWrapper,
|
|
1538
1947
|
ScenePlanner,
|
|
1539
1948
|
ProgressBar,
|