cerevox 2.7.0 → 2.8.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/core/ai.d.ts +9 -0
- package/dist/core/ai.d.ts.map +1 -1
- package/dist/core/ai.js +73 -0
- package/dist/core/ai.js.map +1 -1
- package/dist/mcp/servers/prompts/rules/anime-series.md +2 -2
- package/dist/mcp/servers/prompts/rules/creative-ad.md +110 -0
- package/dist/mcp/servers/prompts/rules/general-video.md +2 -2
- package/dist/mcp/servers/prompts/rules/music-video.md +1 -1
- package/dist/mcp/servers/prompts/rules/stage-play.md +1 -1
- package/dist/mcp/servers/prompts/rules/story-telling.md +67 -0
- package/dist/mcp/servers/prompts/zerocut-core.md +11 -8
- package/dist/mcp/servers/zerocut.d.ts.map +1 -1
- package/dist/mcp/servers/zerocut.js +319 -39
- package/dist/mcp/servers/zerocut.js.map +1 -1
- package/dist/utils/storyboard-schema.json +7 -3
- package/package.json +1 -1
- package/dist/mcp/servers/prompts/zerocut-guideline.md +0 -403
- package/dist/mcp/servers/prompts/zerodancer-guideline.md +0 -302
- package/dist/mcp/servers/prompts/zerosinger-guideline.md +0 -187
|
@@ -170,35 +170,41 @@ async function sendProgress(context, progress, total, message) {
|
|
|
170
170
|
params: { progressToken: token, progress, total, message },
|
|
171
171
|
});
|
|
172
172
|
}
|
|
173
|
-
async function
|
|
173
|
+
async function updateMediaLogs(session, fileName, result, mediaType = 'video') {
|
|
174
174
|
try {
|
|
175
|
-
const
|
|
176
|
-
let
|
|
175
|
+
const mediaLogsPath = node_path_1.default.resolve(projectLocalDir, 'media_logs.json');
|
|
176
|
+
let mediaLogs = [];
|
|
177
177
|
// 尝试读取现有文件
|
|
178
178
|
try {
|
|
179
|
-
const existingContent = await (0, promises_1.readFile)(
|
|
180
|
-
|
|
179
|
+
const existingContent = await (0, promises_1.readFile)(mediaLogsPath, 'utf-8');
|
|
180
|
+
mediaLogs = JSON.parse(existingContent);
|
|
181
181
|
}
|
|
182
182
|
catch (error) {
|
|
183
183
|
// 文件不存在或格式错误,使用空数组
|
|
184
|
-
|
|
184
|
+
mediaLogs = [];
|
|
185
185
|
}
|
|
186
186
|
// 检查是否已存在相同文件名的记录
|
|
187
|
-
const existingIndex =
|
|
187
|
+
const existingIndex = mediaLogs.findIndex(item => item.fileName === fileName);
|
|
188
|
+
const logEntry = {
|
|
189
|
+
fileName,
|
|
190
|
+
mediaType,
|
|
191
|
+
timestamp: new Date().toISOString(),
|
|
192
|
+
result,
|
|
193
|
+
};
|
|
188
194
|
if (existingIndex >= 0) {
|
|
189
195
|
// 更新现有记录
|
|
190
|
-
|
|
196
|
+
mediaLogs[existingIndex] = logEntry;
|
|
191
197
|
}
|
|
192
198
|
else {
|
|
193
199
|
// 添加新记录
|
|
194
|
-
|
|
200
|
+
mediaLogs.push(logEntry);
|
|
195
201
|
}
|
|
196
202
|
// 写入文件
|
|
197
|
-
await (0, promises_1.writeFile)(
|
|
198
|
-
console.log(`Updated
|
|
203
|
+
await (0, promises_1.writeFile)(mediaLogsPath, JSON.stringify(mediaLogs, null, 2));
|
|
204
|
+
console.log(`Updated media_logs.json: ${fileName} -> ${JSON.stringify(result)} (${mediaType})`);
|
|
199
205
|
}
|
|
200
206
|
catch (error) {
|
|
201
|
-
console.warn(`Failed to update
|
|
207
|
+
console.warn(`Failed to update media_logs.json for ${fileName}:`, error);
|
|
202
208
|
}
|
|
203
209
|
}
|
|
204
210
|
async function listFiles(dir) {
|
|
@@ -216,6 +222,7 @@ const cerevox = new index_1.default({
|
|
|
216
222
|
});
|
|
217
223
|
let session = null;
|
|
218
224
|
let projectLocalDir = '.';
|
|
225
|
+
let syncSubtitles = false;
|
|
219
226
|
server.registerTool('zerocut-version', {
|
|
220
227
|
title: `ZeroCut Version`,
|
|
221
228
|
description: `ZeroCut: ${constants_1.VERSION}`,
|
|
@@ -265,6 +272,8 @@ server.registerTool('retrieve-rules-context', {
|
|
|
265
272
|
'music-video',
|
|
266
273
|
'stage-play',
|
|
267
274
|
'anime-series',
|
|
275
|
+
'story-telling',
|
|
276
|
+
'creative-ad',
|
|
268
277
|
'custom',
|
|
269
278
|
])
|
|
270
279
|
.default('general-video')
|
|
@@ -284,7 +293,9 @@ server.registerTool('retrieve-rules-context', {
|
|
|
284
293
|
else if (purpose !== 'general-video' &&
|
|
285
294
|
purpose !== 'music-video' &&
|
|
286
295
|
purpose !== 'stage-play' &&
|
|
287
|
-
purpose !== 'anime-series'
|
|
296
|
+
purpose !== 'anime-series' &&
|
|
297
|
+
purpose !== 'story-telling' &&
|
|
298
|
+
purpose !== 'creative-ad') {
|
|
288
299
|
return createErrorResponse(`Project rules file not found: ${projectRulesFile}`, 'retrieve-rules-context');
|
|
289
300
|
}
|
|
290
301
|
try {
|
|
@@ -347,6 +358,8 @@ server.registerTool('zerocut-project-open', {
|
|
|
347
358
|
}
|
|
348
359
|
console.log('Initializing project...');
|
|
349
360
|
const workDir = await initProject(session);
|
|
361
|
+
// Initialize syncSubtitles flag
|
|
362
|
+
syncSubtitles = false;
|
|
350
363
|
projectLocalDir = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), localDir || '.');
|
|
351
364
|
const syncDir = (0, node_path_1.resolve)(projectLocalDir, 'materials');
|
|
352
365
|
try {
|
|
@@ -551,8 +564,8 @@ server.registerTool('search-image', {
|
|
|
551
564
|
}
|
|
552
565
|
});
|
|
553
566
|
server.registerTool('generate-character-image', {
|
|
554
|
-
title: 'Generate
|
|
555
|
-
description: 'Generate a turnaround image for any character.',
|
|
567
|
+
title: 'Generate Character Image',
|
|
568
|
+
description: 'Generate a turnaround image or portrait for any character.',
|
|
556
569
|
inputSchema: {
|
|
557
570
|
name: zod_1.z.string().describe('The name of the character.'),
|
|
558
571
|
gender: zod_1.z
|
|
@@ -573,14 +586,23 @@ server.registerTool('generate-character-image', {
|
|
|
573
586
|
.string()
|
|
574
587
|
.default('形象参考[图1]的人物形象\n')
|
|
575
588
|
.describe('形象参考图的提示文本.'),
|
|
589
|
+
isTurnaround: zod_1.z
|
|
590
|
+
.boolean()
|
|
591
|
+
.default(true)
|
|
592
|
+
.describe('是否生成三视图。true: 生成1280x720的三视图,false: 生成720x1280的竖版人物正视图'),
|
|
576
593
|
saveToFileName: zod_1.z.string().describe('The filename to save.'),
|
|
577
594
|
},
|
|
578
|
-
}, async ({ name, gender, age, appearance, clothing, personality, style, saveToFileName, referenceImage, referenceImagePrompt, }) => {
|
|
595
|
+
}, async ({ name, gender, age, appearance, clothing, personality, style, saveToFileName, referenceImage, referenceImagePrompt, isTurnaround, }) => {
|
|
579
596
|
try {
|
|
580
597
|
// 验证session状态
|
|
581
598
|
const currentSession = await validateSession('generate-character-image');
|
|
582
599
|
const validatedFileName = validateFileName(saveToFileName);
|
|
583
|
-
|
|
600
|
+
// 根据 isTurnaround 参数生成不同的提示词和尺寸
|
|
601
|
+
let prompt;
|
|
602
|
+
let size;
|
|
603
|
+
if (isTurnaround) {
|
|
604
|
+
// 生成三视图
|
|
605
|
+
prompt = `
|
|
584
606
|
你是一个专业的角色设计师,请根据设定生成角色全身三视图,图片为白底,图中不带任何文字。设定为:
|
|
585
607
|
|
|
586
608
|
角色名称:${name}
|
|
@@ -592,7 +614,26 @@ server.registerTool('generate-character-image', {
|
|
|
592
614
|
构图风格:${style}
|
|
593
615
|
${referenceImagePrompt}
|
|
594
616
|
三视图分别是指侧视图、正视图和背视图,从左到右按照这个顺序生成,三者都必须是全身图。
|
|
595
|
-
|
|
617
|
+
`;
|
|
618
|
+
size = '1280x720';
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
// 生成竖版人物正视图
|
|
622
|
+
prompt = `
|
|
623
|
+
你是一个专业的角色设计师,请根据设定生成角色全身正视图,图片为白底,图中不带任何文字。设定为:
|
|
624
|
+
|
|
625
|
+
角色名称:${name}
|
|
626
|
+
角色性别:${gender}
|
|
627
|
+
角色年龄:${age}
|
|
628
|
+
角色外观:${appearance}
|
|
629
|
+
角色服装:${clothing}
|
|
630
|
+
角色性格:${personality}
|
|
631
|
+
构图风格:${style}
|
|
632
|
+
${referenceImagePrompt}
|
|
633
|
+
请生成一张完整的全身正视图,角色面向观众,展现完整的身体比例和服装细节。
|
|
634
|
+
`;
|
|
635
|
+
size = '720x1280';
|
|
636
|
+
}
|
|
596
637
|
let imageBase64Array;
|
|
597
638
|
if (referenceImage) {
|
|
598
639
|
try {
|
|
@@ -620,7 +661,6 @@ ${referenceImagePrompt}
|
|
|
620
661
|
}
|
|
621
662
|
}
|
|
622
663
|
const ai = currentSession.ai;
|
|
623
|
-
const size = '1280x720';
|
|
624
664
|
const res = await ai.generateImage({
|
|
625
665
|
prompt,
|
|
626
666
|
size,
|
|
@@ -637,6 +677,7 @@ ${referenceImagePrompt}
|
|
|
637
677
|
// source: res.url,
|
|
638
678
|
uri,
|
|
639
679
|
prompt,
|
|
680
|
+
isTurnaround,
|
|
640
681
|
size,
|
|
641
682
|
timestamp: new Date().toISOString(),
|
|
642
683
|
};
|
|
@@ -760,7 +801,7 @@ server.registerTool('generate-image', {
|
|
|
760
801
|
.describe('Whether this is a turnaround image.如果是三视图,这个参数务必传true'),
|
|
761
802
|
}))
|
|
762
803
|
.optional()
|
|
763
|
-
.describe(`Array of reference images with character or object names.如果stage_atmosphere中有角色
|
|
804
|
+
.describe(`Array of reference images with character or object names.如果stage_atmosphere中有角色apply_reference_image,那么必须要传这个参数生成角色图片
|
|
764
805
|
|
|
765
806
|
传参示例
|
|
766
807
|
\`\`\`
|
|
@@ -864,7 +905,7 @@ server.registerTool('generate-image', {
|
|
|
864
905
|
}
|
|
865
906
|
}
|
|
866
907
|
const turnaroundMessage = hasTurnaround
|
|
867
|
-
? '⚠️ 注意**三视图**是指**同一个**人或物体的不同视角合成图,三部分都表示同一个人或物体,只能参考其信息画出**一个人或一个物体**,不要画成多个人或多个物体!\n
|
|
908
|
+
? '⚠️ 注意**三视图**是指**同一个**人或物体的不同视角合成图,三部分都表示同一个人或物体,只能参考其信息画出**一个人或一个物体**,不要画成多个人或多个物体!\n'
|
|
868
909
|
: '';
|
|
869
910
|
if (objectPrefix.length > 0) {
|
|
870
911
|
processedPrompt = `${objectPrefix.join('\n')}
|
|
@@ -924,6 +965,231 @@ ${processedPrompt}`.trim();
|
|
|
924
965
|
return createErrorResponse(error, 'generate-image');
|
|
925
966
|
}
|
|
926
967
|
});
|
|
968
|
+
server.registerTool('generate-image-series', {
|
|
969
|
+
title: 'Generate Image Series',
|
|
970
|
+
description: 'Generate a series of images based on prompts.',
|
|
971
|
+
inputSchema: {
|
|
972
|
+
prompts: zod_1.z
|
|
973
|
+
.union([zod_1.z.string(), zod_1.z.array(zod_1.z.string())])
|
|
974
|
+
.describe('The prompts to generate images. Can be a single string or array of strings.'),
|
|
975
|
+
size: zod_1.z
|
|
976
|
+
.enum([
|
|
977
|
+
'1024x1024',
|
|
978
|
+
'864x1152',
|
|
979
|
+
'1152x864',
|
|
980
|
+
'1280x720',
|
|
981
|
+
'720x1280',
|
|
982
|
+
'832x1248',
|
|
983
|
+
'1248x832',
|
|
984
|
+
'1512x648',
|
|
985
|
+
])
|
|
986
|
+
.optional()
|
|
987
|
+
.describe('The size of the images.'),
|
|
988
|
+
watermark: zod_1.z
|
|
989
|
+
.boolean()
|
|
990
|
+
.optional()
|
|
991
|
+
.default(false)
|
|
992
|
+
.describe('Whether to add watermark to the images.'),
|
|
993
|
+
max_count: zod_1.z
|
|
994
|
+
.number()
|
|
995
|
+
.min(1)
|
|
996
|
+
.max(15)
|
|
997
|
+
.optional()
|
|
998
|
+
.default(15)
|
|
999
|
+
.describe('Maximum number of images to generate (1-15).'),
|
|
1000
|
+
referenceImages: zod_1.z
|
|
1001
|
+
.array(zod_1.z.object({
|
|
1002
|
+
image: zod_1.z.string().describe('Local image file path'),
|
|
1003
|
+
type: zod_1.z
|
|
1004
|
+
.enum(['character', 'object', 'background'])
|
|
1005
|
+
.describe('Type of the reference image. 必须传,如果是参考角色三视图,传character,如果是参考背景图,传background,否则传object'),
|
|
1006
|
+
name: zod_1.z.string().describe('Name for this reference image'),
|
|
1007
|
+
isTurnaround: zod_1.z
|
|
1008
|
+
.boolean()
|
|
1009
|
+
.describe('Whether this is a turnaround image.如果是三视图,这个参数务必传true'),
|
|
1010
|
+
}))
|
|
1011
|
+
.optional()
|
|
1012
|
+
.describe(`Array of reference images with character or object names.如果stage_atmosphere中有角色apply_reference_image,那么必须要传这个参数生成角色图片
|
|
1013
|
+
|
|
1014
|
+
传参示例
|
|
1015
|
+
\`\`\`
|
|
1016
|
+
{
|
|
1017
|
+
"image": "latiao.jpeg",
|
|
1018
|
+
"type": "object",
|
|
1019
|
+
"name": "卫龙辣条",
|
|
1020
|
+
}
|
|
1021
|
+
\`\`\`
|
|
1022
|
+
`),
|
|
1023
|
+
saveToFileNames: zod_1.z
|
|
1024
|
+
.array(zod_1.z.string())
|
|
1025
|
+
.describe('Array of filenames to save the generated images.'),
|
|
1026
|
+
},
|
|
1027
|
+
}, async ({ prompts, size, watermark, max_count, referenceImages, saveToFileNames }, context) => {
|
|
1028
|
+
try {
|
|
1029
|
+
const currentSession = await validateSession('generate-image-series');
|
|
1030
|
+
// Validate file names
|
|
1031
|
+
const validatedFileNames = saveToFileNames.map(fileName => validateFileName(fileName));
|
|
1032
|
+
if (typeof prompts === 'string' && prompts.startsWith('[')) {
|
|
1033
|
+
try {
|
|
1034
|
+
prompts = JSON.parse(prompts);
|
|
1035
|
+
}
|
|
1036
|
+
catch (ex) {
|
|
1037
|
+
// 解析失败,保持原始字符串
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
console.log(`Generating image series with ${Array.isArray(prompts) ? prompts.length : 1} prompts...`);
|
|
1041
|
+
// 处理参考图片
|
|
1042
|
+
let imageBase64Array;
|
|
1043
|
+
let refPrefix = '';
|
|
1044
|
+
if (referenceImages && referenceImages.length > 0) {
|
|
1045
|
+
imageBase64Array = [];
|
|
1046
|
+
const objectPrefix = [];
|
|
1047
|
+
let index = 0;
|
|
1048
|
+
let hasTurnaround = false;
|
|
1049
|
+
for (const refImage of referenceImages) {
|
|
1050
|
+
const imagePath = (0, node_path_1.dirname)(refImage.image) !== '.'
|
|
1051
|
+
? refImage.image
|
|
1052
|
+
: `./materials/${refImage.image}`;
|
|
1053
|
+
// 需要得到当前项目的绝对路径
|
|
1054
|
+
const imageFilePath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), projectLocalDir, imagePath);
|
|
1055
|
+
try {
|
|
1056
|
+
// 直接读取本地文件
|
|
1057
|
+
if (!(0, node_fs_1.existsSync)(imageFilePath)) {
|
|
1058
|
+
return createErrorResponse(`Reference image not found: ${imageFilePath}`, 'generate-image-series');
|
|
1059
|
+
}
|
|
1060
|
+
const imageBuffer = await (0, promises_1.readFile)(imageFilePath);
|
|
1061
|
+
const fileName = (0, node_path_1.basename)(imagePath);
|
|
1062
|
+
const mimeType = fileName.toLowerCase().endsWith('.png')
|
|
1063
|
+
? 'image/png'
|
|
1064
|
+
: fileName.toLowerCase().endsWith('.jpg') ||
|
|
1065
|
+
fileName.toLowerCase().endsWith('.jpeg')
|
|
1066
|
+
? 'image/jpeg'
|
|
1067
|
+
: 'image/png';
|
|
1068
|
+
const base64String = `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
|
|
1069
|
+
imageBase64Array.push(base64String);
|
|
1070
|
+
console.log(`Loaded reference image: ${imagePath} for character: ${refImage.name}`);
|
|
1071
|
+
if (refImage.type === 'character') {
|
|
1072
|
+
if (refImage.isTurnaround) {
|
|
1073
|
+
objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的人物角色三视图`);
|
|
1074
|
+
hasTurnaround = true;
|
|
1075
|
+
}
|
|
1076
|
+
else {
|
|
1077
|
+
objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的人物角色形象`);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
else if (refImage.type === 'object') {
|
|
1081
|
+
if (refImage.isTurnaround) {
|
|
1082
|
+
objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的物件三视图`);
|
|
1083
|
+
hasTurnaround = true;
|
|
1084
|
+
}
|
|
1085
|
+
else {
|
|
1086
|
+
objectPrefix.push(`[参考图${++index}]是名为"${refImage.name}"的物件`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
else if (refImage.type === 'background') {
|
|
1090
|
+
objectPrefix.push(`[参考图${++index}]是背景图,描述了"${refImage.name}"`);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
catch (error) {
|
|
1094
|
+
console.error(`Failed to load reference image ${imageFilePath} for ${refImage.name}:`, error);
|
|
1095
|
+
return createErrorResponse(`Failed to load reference image ${imageFilePath} for ${refImage.name}: ${error}`, 'generate-image-series');
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
const turnaroundMessage = hasTurnaround
|
|
1099
|
+
? '\n\n⚠️ 注意**三视图**是指**同一个**人或物体的不同视角合成图,三部分都表示同一个人或物体,只能参考其信息画出**一个人或一个物体**,不要画成多个人或多个物体!\n'
|
|
1100
|
+
: '';
|
|
1101
|
+
if (objectPrefix.length > 0) {
|
|
1102
|
+
refPrefix = `${objectPrefix.join('\n')}${turnaroundMessage}`;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
const ai = currentSession.ai;
|
|
1106
|
+
let progress = 0;
|
|
1107
|
+
const res = await ai.generateImageSeries({
|
|
1108
|
+
prompts,
|
|
1109
|
+
refPrefix,
|
|
1110
|
+
size,
|
|
1111
|
+
watermark,
|
|
1112
|
+
image: imageBase64Array,
|
|
1113
|
+
max_count,
|
|
1114
|
+
onProgress: async (metaData) => {
|
|
1115
|
+
// 心跳机制,每分钟调用一次,传递空的 metaData
|
|
1116
|
+
try {
|
|
1117
|
+
await sendProgress(context, ++progress, undefined, JSON.stringify(metaData));
|
|
1118
|
+
}
|
|
1119
|
+
catch (progressError) {
|
|
1120
|
+
console.warn('Failed to send progress update:', progressError);
|
|
1121
|
+
}
|
|
1122
|
+
},
|
|
1123
|
+
});
|
|
1124
|
+
if (!res) {
|
|
1125
|
+
throw new Error('Failed to generate image series: no response from AI service');
|
|
1126
|
+
}
|
|
1127
|
+
if (res.urls && Array.isArray(res.urls)) {
|
|
1128
|
+
console.log(`Image series generated successfully (${res.urls.length} images), saving to materials...`);
|
|
1129
|
+
const results = [];
|
|
1130
|
+
const maxImages = Math.min(res.urls.length, validatedFileNames.length);
|
|
1131
|
+
for (let i = 0; i < maxImages; i++) {
|
|
1132
|
+
const url = res.urls[i];
|
|
1133
|
+
const fileName = validatedFileNames[i];
|
|
1134
|
+
try {
|
|
1135
|
+
const uri = await saveMaterial(currentSession, url, fileName);
|
|
1136
|
+
results.push({
|
|
1137
|
+
success: true,
|
|
1138
|
+
uri,
|
|
1139
|
+
fileName,
|
|
1140
|
+
index: i,
|
|
1141
|
+
timestamp: new Date().toISOString(),
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
catch (error) {
|
|
1145
|
+
console.error(`Failed to save image ${i + 1}:`, error);
|
|
1146
|
+
results.push({
|
|
1147
|
+
success: false,
|
|
1148
|
+
error: `Failed to save image ${i + 1}: ${error}`,
|
|
1149
|
+
fileName,
|
|
1150
|
+
index: i,
|
|
1151
|
+
timestamp: new Date().toISOString(),
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
const result = {
|
|
1156
|
+
success: true,
|
|
1157
|
+
prompt: res.prompt,
|
|
1158
|
+
totalGenerated: res.urls.length,
|
|
1159
|
+
totalSaved: results.filter(r => r.success).length,
|
|
1160
|
+
results,
|
|
1161
|
+
timestamp: new Date().toISOString(),
|
|
1162
|
+
};
|
|
1163
|
+
return {
|
|
1164
|
+
content: [
|
|
1165
|
+
{
|
|
1166
|
+
type: 'text',
|
|
1167
|
+
text: JSON.stringify(result),
|
|
1168
|
+
},
|
|
1169
|
+
],
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
console.warn('Image series generation completed but no URLs returned');
|
|
1174
|
+
return {
|
|
1175
|
+
content: [
|
|
1176
|
+
{
|
|
1177
|
+
type: 'text',
|
|
1178
|
+
text: JSON.stringify({
|
|
1179
|
+
success: false,
|
|
1180
|
+
error: 'No image URLs returned from AI service',
|
|
1181
|
+
response: res,
|
|
1182
|
+
timestamp: new Date().toISOString(),
|
|
1183
|
+
}),
|
|
1184
|
+
},
|
|
1185
|
+
],
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
catch (error) {
|
|
1190
|
+
return createErrorResponse(error, 'generate-image-series');
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
927
1193
|
server.registerTool('edit-image', {
|
|
928
1194
|
title: 'Edit Image',
|
|
929
1195
|
description: 'Edit the image.',
|
|
@@ -1145,12 +1411,12 @@ server.registerTool('generate-video', {
|
|
|
1145
1411
|
timestamp: new Date().toISOString(),
|
|
1146
1412
|
...opts,
|
|
1147
1413
|
};
|
|
1148
|
-
// Update
|
|
1414
|
+
// Update media_logs.json
|
|
1149
1415
|
try {
|
|
1150
|
-
await
|
|
1416
|
+
await updateMediaLogs(currentSession, validatedFileName, result);
|
|
1151
1417
|
}
|
|
1152
1418
|
catch (error) {
|
|
1153
|
-
console.warn(`Failed to update
|
|
1419
|
+
console.warn(`Failed to update media_logs.json for ${validatedFileName}:`, error);
|
|
1154
1420
|
}
|
|
1155
1421
|
return {
|
|
1156
1422
|
content: [
|
|
@@ -1360,13 +1626,12 @@ server.registerTool('generate-sound-effect', {
|
|
|
1360
1626
|
});
|
|
1361
1627
|
if (res.url) {
|
|
1362
1628
|
const uri = await saveMaterial(currentSession, res.url, saveToFileName);
|
|
1363
|
-
// Update
|
|
1629
|
+
// Update media_logs.json
|
|
1364
1630
|
try {
|
|
1365
|
-
|
|
1366
|
-
await updateMediaDurations(currentSession, saveToFileName, durationMs);
|
|
1631
|
+
await updateMediaLogs(currentSession, saveToFileName, res, 'audio');
|
|
1367
1632
|
}
|
|
1368
1633
|
catch (error) {
|
|
1369
|
-
console.warn(`Failed to update
|
|
1634
|
+
console.warn(`Failed to update media_logs.json for ${saveToFileName}:`, error);
|
|
1370
1635
|
}
|
|
1371
1636
|
return {
|
|
1372
1637
|
content: [
|
|
@@ -1559,12 +1824,12 @@ server.registerTool('generate-song', {
|
|
|
1559
1824
|
timestamp: new Date().toISOString(),
|
|
1560
1825
|
...opts,
|
|
1561
1826
|
};
|
|
1562
|
-
// Update
|
|
1827
|
+
// Update media_logs.json
|
|
1563
1828
|
try {
|
|
1564
|
-
await
|
|
1829
|
+
await updateMediaLogs(currentSession, validatedFileName, result, 'audio');
|
|
1565
1830
|
}
|
|
1566
1831
|
catch (error) {
|
|
1567
|
-
console.warn(`Failed to update
|
|
1832
|
+
console.warn(`Failed to update media_logs.json for ${validatedFileName}:`, error);
|
|
1568
1833
|
}
|
|
1569
1834
|
return {
|
|
1570
1835
|
content: [
|
|
@@ -1645,12 +1910,12 @@ server.registerTool('generate-bgm', {
|
|
|
1645
1910
|
timestamp: new Date().toISOString(),
|
|
1646
1911
|
...opts,
|
|
1647
1912
|
};
|
|
1648
|
-
// Update
|
|
1913
|
+
// Update media_logs.json
|
|
1649
1914
|
try {
|
|
1650
|
-
await
|
|
1915
|
+
await updateMediaLogs(currentSession, validatedFileName, result, 'audio');
|
|
1651
1916
|
}
|
|
1652
1917
|
catch (error) {
|
|
1653
|
-
console.warn('Failed to update
|
|
1918
|
+
console.warn('Failed to update media_logs.json:', error);
|
|
1654
1919
|
}
|
|
1655
1920
|
return {
|
|
1656
1921
|
content: [
|
|
@@ -1806,12 +2071,12 @@ server.registerTool('generate-scene-tts', {
|
|
|
1806
2071
|
timestamp: new Date().toISOString(),
|
|
1807
2072
|
...opts,
|
|
1808
2073
|
};
|
|
1809
|
-
// Update
|
|
2074
|
+
// Update media_logs.json
|
|
1810
2075
|
try {
|
|
1811
|
-
await
|
|
2076
|
+
await updateMediaLogs(currentSession, validatedFileName, result, 'audio');
|
|
1812
2077
|
}
|
|
1813
2078
|
catch (error) {
|
|
1814
|
-
console.warn(`Failed to update
|
|
2079
|
+
console.warn(`Failed to update media_logs.json for ${validatedFileName}:`, error);
|
|
1815
2080
|
}
|
|
1816
2081
|
return {
|
|
1817
2082
|
content: [
|
|
@@ -2018,6 +2283,10 @@ server.registerTool('get-video-project-schema', {
|
|
|
2018
2283
|
inputSchema: {},
|
|
2019
2284
|
}, async () => {
|
|
2020
2285
|
try {
|
|
2286
|
+
// Check if syncSubtitles is false and show warning
|
|
2287
|
+
if (!syncSubtitles) {
|
|
2288
|
+
console.warn('Warning: syncSubtitles is false. Please call get-subtitle-contents first to sync subtitles.');
|
|
2289
|
+
}
|
|
2021
2290
|
const schemaPath = (0, node_path_1.resolve)(__dirname, '../../utils/videoproject-schema.json');
|
|
2022
2291
|
const schemaContent = await (0, promises_1.readFile)(schemaPath, 'utf-8');
|
|
2023
2292
|
const schema = JSON.parse(schemaContent);
|
|
@@ -2028,6 +2297,9 @@ server.registerTool('get-video-project-schema', {
|
|
|
2028
2297
|
text: JSON.stringify({
|
|
2029
2298
|
success: true,
|
|
2030
2299
|
schema,
|
|
2300
|
+
warning: !syncSubtitles
|
|
2301
|
+
? '⚠️ Please call get-subtitle-contents to sync subtitles before creating the draft_content.json file.'
|
|
2302
|
+
: undefined,
|
|
2031
2303
|
timestamp: new Date().toISOString(),
|
|
2032
2304
|
}),
|
|
2033
2305
|
},
|
|
@@ -2047,6 +2319,7 @@ server.registerTool('get-subtitle-contents', {
|
|
|
2047
2319
|
.describe('The file path of the story_board.json file.'),
|
|
2048
2320
|
},
|
|
2049
2321
|
}, async ({ storyBoardFile }) => {
|
|
2322
|
+
syncSubtitles = true;
|
|
2050
2323
|
try {
|
|
2051
2324
|
const filePath = (0, node_path_1.resolve)(process.env.ZEROCUT_PROJECT_CWD || process.cwd(), storyBoardFile);
|
|
2052
2325
|
if (!(0, node_fs_1.existsSync)(filePath)) {
|
|
@@ -2223,7 +2496,7 @@ server.registerTool('lip-sync', {
|
|
|
2223
2496
|
.string()
|
|
2224
2497
|
.describe('The filename to save the lip-synced video.'),
|
|
2225
2498
|
},
|
|
2226
|
-
}, async ({ videoFileName, audioFileName, saveToFileName }) => {
|
|
2499
|
+
}, async ({ videoFileName, audioFileName, saveToFileName }, context) => {
|
|
2227
2500
|
try {
|
|
2228
2501
|
// 验证session状态
|
|
2229
2502
|
const currentSession = await validateSession('lip-sync');
|
|
@@ -2241,11 +2514,18 @@ server.registerTool('lip-sync', {
|
|
|
2241
2514
|
console.log(`Video URL: ${videoUrl}`);
|
|
2242
2515
|
console.log(`Audio URL: ${audioUrl}`);
|
|
2243
2516
|
// 调用AI的lipSync方法,使用处理后的音频
|
|
2517
|
+
let progress = 0;
|
|
2244
2518
|
const result = await currentSession.ai.lipSync({
|
|
2245
2519
|
videoUrl,
|
|
2246
2520
|
audioUrl,
|
|
2247
|
-
onProgress: metaData => {
|
|
2521
|
+
onProgress: async (metaData) => {
|
|
2248
2522
|
console.log('Lip sync progress:', metaData);
|
|
2523
|
+
try {
|
|
2524
|
+
await sendProgress(context, ++progress, undefined, JSON.stringify(metaData));
|
|
2525
|
+
}
|
|
2526
|
+
catch (progressError) {
|
|
2527
|
+
console.warn('Failed to send progress update:', progressError);
|
|
2528
|
+
}
|
|
2249
2529
|
},
|
|
2250
2530
|
});
|
|
2251
2531
|
if (result.url) {
|