cerevox 2.27.1 → 2.28.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 +3 -0
- package/dist/core/ai.d.ts.map +1 -1
- package/dist/core/ai.js +14 -1
- 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/general-video.md +2 -2
- package/dist/mcp/servers/prompts/rules/professional.md +1 -1
- package/dist/mcp/servers/zerocut.d.ts.map +1 -1
- package/dist/mcp/servers/zerocut.js +396 -147
- package/dist/mcp/servers/zerocut.js.map +1 -1
- package/package.json +1 -1
|
@@ -58,6 +58,7 @@ const node_child_process_1 = require("node:child_process");
|
|
|
58
58
|
const node_util_1 = require("node:util");
|
|
59
59
|
const os = __importStar(require("node:os"));
|
|
60
60
|
const mp3_duration_1 = __importDefault(require("mp3-duration"));
|
|
61
|
+
const image_size_1 = __importDefault(require("image-size"));
|
|
61
62
|
const execAsync = (0, node_util_1.promisify)(node_child_process_1.exec);
|
|
62
63
|
// 错误处理工具函数
|
|
63
64
|
const handleError = (error) => {
|
|
@@ -1569,6 +1570,7 @@ server.registerTool('edit-image', {
|
|
|
1569
1570
|
return createErrorResponse(error, 'edit-image');
|
|
1570
1571
|
}
|
|
1571
1572
|
});
|
|
1573
|
+
let lastEffect = '';
|
|
1572
1574
|
server.registerTool('generate-video', {
|
|
1573
1575
|
title: 'Generate Video',
|
|
1574
1576
|
description: `生成视频`,
|
|
@@ -1607,6 +1609,7 @@ server.registerTool('generate-video', {
|
|
|
1607
1609
|
'kling',
|
|
1608
1610
|
'kling-pro',
|
|
1609
1611
|
'pixv',
|
|
1612
|
+
'kenburns',
|
|
1610
1613
|
])
|
|
1611
1614
|
.default('lite')
|
|
1612
1615
|
.describe('除非用户明确提出使用其他模型,否则一律用lite模型'),
|
|
@@ -1773,6 +1776,208 @@ server.registerTool('generate-video', {
|
|
|
1773
1776
|
console.error('Failed to optimize prompt:', error);
|
|
1774
1777
|
}
|
|
1775
1778
|
}
|
|
1779
|
+
if (type === 'kenburns') {
|
|
1780
|
+
// 实现 getImageSize 函数
|
|
1781
|
+
async function getImageSize(imageUri) {
|
|
1782
|
+
// imageUri 是一个可以 http 访问的图片
|
|
1783
|
+
// eslint-disable-next-line custom/no-fetch-in-src
|
|
1784
|
+
const response = await fetch(imageUri);
|
|
1785
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1786
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
1787
|
+
const image = (0, image_size_1.default)(buffer);
|
|
1788
|
+
return [image.width, image.height];
|
|
1789
|
+
}
|
|
1790
|
+
const [imageWidth, imageHeight] = await getImageSize(startFrameUri);
|
|
1791
|
+
const kenburnsPrompt = `根据用户描述和配置,生成适合的 kenburns 动画参数。
|
|
1792
|
+
|
|
1793
|
+
## 配置信息
|
|
1794
|
+
- 视频时长:${duration}秒
|
|
1795
|
+
- 分辨率:${resolution}
|
|
1796
|
+
- 保存最后一帧:${saveLastFrameAs ? '是' : '否'}
|
|
1797
|
+
- 图片宽高:${imageWidth}x${imageHeight}
|
|
1798
|
+
- 上一次选择的特效:${lastEffect || '无'}
|
|
1799
|
+
|
|
1800
|
+
## camera_motion 可选特效
|
|
1801
|
+
- zoom_in
|
|
1802
|
+
- zoom_out
|
|
1803
|
+
- zoom_in_left_top
|
|
1804
|
+
- zoom_out_left_top
|
|
1805
|
+
- zoom_in_right_top
|
|
1806
|
+
- zoom_out_right_top
|
|
1807
|
+
- zoom_in_left_bottom
|
|
1808
|
+
- zoom_out_left_bottom
|
|
1809
|
+
- zoom_in_right_bottom
|
|
1810
|
+
- zoom_out_right_bottom
|
|
1811
|
+
- pan_up
|
|
1812
|
+
- pan_down
|
|
1813
|
+
- pan_left
|
|
1814
|
+
- pan_right
|
|
1815
|
+
- static
|
|
1816
|
+
|
|
1817
|
+
## 特效选用规则
|
|
1818
|
+
- 视频时长6秒内:优先选择zoom类型(zoom_in, zoom_out)
|
|
1819
|
+
- 视频时长6秒以上:优先选择pan类型(pan_left, pan_right, pan_up, pan_down)
|
|
1820
|
+
- 除非用户指定,否则不要主动使用 static 效果
|
|
1821
|
+
- 除非用户指定,否则不要和上一次选择的效果重复
|
|
1822
|
+
`;
|
|
1823
|
+
const schema = {
|
|
1824
|
+
name: 'kenburns_effect',
|
|
1825
|
+
schema: {
|
|
1826
|
+
type: 'object',
|
|
1827
|
+
properties: {
|
|
1828
|
+
camera_motion: {
|
|
1829
|
+
type: 'string',
|
|
1830
|
+
description: 'kenburns 特效类型',
|
|
1831
|
+
enum: [
|
|
1832
|
+
'zoom_in',
|
|
1833
|
+
'zoom_out',
|
|
1834
|
+
'zoom_in_left_top',
|
|
1835
|
+
'zoom_out_left_top',
|
|
1836
|
+
'zoom_in_right_top',
|
|
1837
|
+
'zoom_out_right_top',
|
|
1838
|
+
'zoom_in_left_bottom',
|
|
1839
|
+
'zoom_out_left_bottom',
|
|
1840
|
+
'zoom_in_right_bottom',
|
|
1841
|
+
'zoom_out_right_bottom',
|
|
1842
|
+
'pan_up',
|
|
1843
|
+
'pan_down',
|
|
1844
|
+
'pan_left',
|
|
1845
|
+
'pan_right',
|
|
1846
|
+
'static',
|
|
1847
|
+
],
|
|
1848
|
+
},
|
|
1849
|
+
size: {
|
|
1850
|
+
type: 'string',
|
|
1851
|
+
description: 'kenburns 视频 宽x高',
|
|
1852
|
+
},
|
|
1853
|
+
},
|
|
1854
|
+
duration: {
|
|
1855
|
+
type: 'number',
|
|
1856
|
+
description: 'kenburns 视频 时长',
|
|
1857
|
+
},
|
|
1858
|
+
required: ['camera_motion', 'size', 'duration'],
|
|
1859
|
+
},
|
|
1860
|
+
};
|
|
1861
|
+
const analysisPayload = {
|
|
1862
|
+
model: 'Doubao-Seed-1.6-flash',
|
|
1863
|
+
messages: [
|
|
1864
|
+
{
|
|
1865
|
+
role: 'system',
|
|
1866
|
+
content: kenburnsPrompt,
|
|
1867
|
+
},
|
|
1868
|
+
{
|
|
1869
|
+
role: 'user',
|
|
1870
|
+
content: prompt.trim(),
|
|
1871
|
+
},
|
|
1872
|
+
],
|
|
1873
|
+
response_format: {
|
|
1874
|
+
type: 'json_schema',
|
|
1875
|
+
json_schema: schema,
|
|
1876
|
+
},
|
|
1877
|
+
};
|
|
1878
|
+
console.log(JSON.stringify(analysisPayload, null, 2));
|
|
1879
|
+
const completion = await ai.getCompletions(analysisPayload);
|
|
1880
|
+
const analysisResult = completion.choices[0]?.message?.content;
|
|
1881
|
+
if (!analysisResult) {
|
|
1882
|
+
throw new Error('Failed to generate kenburns effect');
|
|
1883
|
+
}
|
|
1884
|
+
try {
|
|
1885
|
+
const kenburnsEffect = JSON.parse(analysisResult);
|
|
1886
|
+
const { camera_motion, size, duration } = kenburnsEffect;
|
|
1887
|
+
if (!camera_motion || !size || !duration) {
|
|
1888
|
+
throw new Error('Invalid kenburns effect parameters');
|
|
1889
|
+
}
|
|
1890
|
+
lastEffect = camera_motion;
|
|
1891
|
+
const files = currentSession.files;
|
|
1892
|
+
const terminal = currentSession.terminal;
|
|
1893
|
+
start_frame = (0, node_path_1.resolve)('/home/user/cerevox-zerocut/projects', terminal.id, 'materials', (0, node_path_1.basename)(start_frame));
|
|
1894
|
+
const saveToPath = `/home/user/cerevox-zerocut/projects/${terminal.id}/materials/${validatedFileName}`;
|
|
1895
|
+
const saveLocalPath = (0, node_path_1.resolve)(projectLocalDir, 'materials', validatedFileName);
|
|
1896
|
+
// 解析尺寸参数
|
|
1897
|
+
const sizeArray = size.split('x');
|
|
1898
|
+
if (sizeArray.length !== 2) {
|
|
1899
|
+
throw new Error(`Invalid size format: ${size}. Expected format: WIDTHxHEIGHT`);
|
|
1900
|
+
}
|
|
1901
|
+
const [widthStr, heightStr] = sizeArray;
|
|
1902
|
+
const width = Number(widthStr);
|
|
1903
|
+
const height = Number(heightStr);
|
|
1904
|
+
if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) {
|
|
1905
|
+
throw new Error(`Invalid dimensions: ${width}x${height}. Both width and height must be positive numbers`);
|
|
1906
|
+
}
|
|
1907
|
+
console.log(`Compiling Ken Burns motion command...`);
|
|
1908
|
+
let command;
|
|
1909
|
+
try {
|
|
1910
|
+
command = await (0, videokit_1.compileKenBurnsMotion)(start_frame, duration, camera_motion, {
|
|
1911
|
+
output: saveToPath,
|
|
1912
|
+
width,
|
|
1913
|
+
height,
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
catch (compileError) {
|
|
1917
|
+
console.error('Failed to compile Ken Burns motion command:', compileError);
|
|
1918
|
+
throw new Error(`Failed to compile Ken Burns motion: ${compileError}`);
|
|
1919
|
+
}
|
|
1920
|
+
console.log(`Executing FFmpeg command: ${command.substring(0, 100)}...`);
|
|
1921
|
+
const res = await terminal.run(command);
|
|
1922
|
+
const result = await res.json();
|
|
1923
|
+
if (result.exitCode !== 0) {
|
|
1924
|
+
console.error('FFmpeg command failed:', result);
|
|
1925
|
+
return {
|
|
1926
|
+
content: [
|
|
1927
|
+
{
|
|
1928
|
+
type: 'text',
|
|
1929
|
+
text: JSON.stringify({
|
|
1930
|
+
success: false,
|
|
1931
|
+
error: 'Ken Burns motion generation failed',
|
|
1932
|
+
exitCode: result.exitCode,
|
|
1933
|
+
stderr: result.stderr,
|
|
1934
|
+
command,
|
|
1935
|
+
timestamp: new Date().toISOString(),
|
|
1936
|
+
}),
|
|
1937
|
+
},
|
|
1938
|
+
],
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
console.log('Ken Burns motion generated successfully, downloading file...');
|
|
1942
|
+
try {
|
|
1943
|
+
await files.download(saveToPath, saveLocalPath);
|
|
1944
|
+
}
|
|
1945
|
+
catch (downloadError) {
|
|
1946
|
+
console.warn('Failed to download file to local:', downloadError);
|
|
1947
|
+
// 继续执行,因为远程文件已生成成功
|
|
1948
|
+
}
|
|
1949
|
+
const resultData = {
|
|
1950
|
+
success: true,
|
|
1951
|
+
uri: saveToPath,
|
|
1952
|
+
durationMs: Math.floor(duration * 1000),
|
|
1953
|
+
cameraMotion: camera_motion,
|
|
1954
|
+
imagePath: start_frame,
|
|
1955
|
+
size,
|
|
1956
|
+
dimensions: { width, height },
|
|
1957
|
+
timestamp: new Date().toISOString(),
|
|
1958
|
+
systemPrompt: kenburnsPrompt,
|
|
1959
|
+
};
|
|
1960
|
+
console.log(`Ken Burns motion completed: ${saveToPath}`);
|
|
1961
|
+
currentSession.track('KenBurns Video Generated', {
|
|
1962
|
+
durationMs: Math.floor(duration * 1000).toString(),
|
|
1963
|
+
cameraMotion: camera_motion,
|
|
1964
|
+
imagePath: start_frame,
|
|
1965
|
+
size,
|
|
1966
|
+
dimensions: size,
|
|
1967
|
+
});
|
|
1968
|
+
return {
|
|
1969
|
+
content: [
|
|
1970
|
+
{
|
|
1971
|
+
type: 'text',
|
|
1972
|
+
text: JSON.stringify(resultData),
|
|
1973
|
+
},
|
|
1974
|
+
],
|
|
1975
|
+
};
|
|
1976
|
+
}
|
|
1977
|
+
catch (error) {
|
|
1978
|
+
throw new Error('Failed to parse kenburns effect parameters');
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1776
1981
|
const res = await ai.framesToVideo({
|
|
1777
1982
|
prompt: prompt.trim(),
|
|
1778
1983
|
start_frame: startFrameUri,
|
|
@@ -1852,152 +2057,185 @@ server.registerTool('generate-video', {
|
|
|
1852
2057
|
return createErrorResponse(error, 'generate-video');
|
|
1853
2058
|
}
|
|
1854
2059
|
});
|
|
1855
|
-
server.registerTool(
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2060
|
+
// server.registerTool(
|
|
2061
|
+
// 'generate-video-kenburns',
|
|
2062
|
+
// {
|
|
2063
|
+
// title: 'Generate Ken Burns Motion Video',
|
|
2064
|
+
// description:
|
|
2065
|
+
// 'Generate Ken Burns motion video with customizable camera movements.',
|
|
2066
|
+
// inputSchema: {
|
|
2067
|
+
// image_path: z.string().describe('The start frame image path(not URL).'),
|
|
2068
|
+
// duration: z
|
|
2069
|
+
// .number()
|
|
2070
|
+
// .min(2)
|
|
2071
|
+
// .max(16)
|
|
2072
|
+
// .describe('The duration of the video.'),
|
|
2073
|
+
// camera_motion: z
|
|
2074
|
+
// .enum([
|
|
2075
|
+
// 'zoom_in',
|
|
2076
|
+
// 'zoom_out',
|
|
2077
|
+
// 'zoom_in_left_top',
|
|
2078
|
+
// 'zoom_out_left_top',
|
|
2079
|
+
// 'zoom_in_right_top',
|
|
2080
|
+
// 'zoom_out_right_top',
|
|
2081
|
+
// 'zoom_in_left_bottom',
|
|
2082
|
+
// 'zoom_out_left_bottom',
|
|
2083
|
+
// 'zoom_in_right_bottom',
|
|
2084
|
+
// 'zoom_out_right_bottom',
|
|
2085
|
+
// 'pan_up',
|
|
2086
|
+
// 'pan_down',
|
|
2087
|
+
// 'pan_left',
|
|
2088
|
+
// 'pan_right',
|
|
2089
|
+
// 'static',
|
|
2090
|
+
// ])
|
|
2091
|
+
// .optional()
|
|
2092
|
+
// .default('zoom_in')
|
|
2093
|
+
// .describe('The camera motion type for Ken Burns effect.'),
|
|
2094
|
+
// size: z
|
|
2095
|
+
// .enum([
|
|
2096
|
+
// '1024x1024',
|
|
2097
|
+
// '864x1152',
|
|
2098
|
+
// '1152x864',
|
|
2099
|
+
// '1280x720',
|
|
2100
|
+
// '720x1280',
|
|
2101
|
+
// '832x1248',
|
|
2102
|
+
// '1248x832',
|
|
2103
|
+
// '1512x648',
|
|
2104
|
+
// ])
|
|
2105
|
+
// .describe('The size of the image.'),
|
|
2106
|
+
// saveToFileName: z.string().describe('The filename to save.'),
|
|
2107
|
+
// },
|
|
2108
|
+
// },
|
|
2109
|
+
// async ({
|
|
2110
|
+
// image_path,
|
|
2111
|
+
// duration,
|
|
2112
|
+
// camera_motion = 'zoom_in',
|
|
2113
|
+
// size,
|
|
2114
|
+
// saveToFileName,
|
|
2115
|
+
// }) => {
|
|
2116
|
+
// try {
|
|
2117
|
+
// // 验证session状态
|
|
2118
|
+
// const currentSession = await validateSession('generate-video-kenburns');
|
|
2119
|
+
// const validatedFileName = validateFileName(saveToFileName);
|
|
2120
|
+
// const files = currentSession.files;
|
|
2121
|
+
// const terminal = currentSession.terminal;
|
|
2122
|
+
// // 如果 image_path 是文件名,需要拼接完整路径
|
|
2123
|
+
// if (dirname(image_path) === '.') {
|
|
2124
|
+
// image_path = resolve(
|
|
2125
|
+
// '/home/user/cerevox-zerocut/projects',
|
|
2126
|
+
// terminal.id,
|
|
2127
|
+
// 'materials',
|
|
2128
|
+
// image_path
|
|
2129
|
+
// );
|
|
2130
|
+
// }
|
|
2131
|
+
// console.log(
|
|
2132
|
+
// `Generating Ken Burns motion: ${image_path} (${duration}s, ${size}, ${camera_motion})`
|
|
2133
|
+
// );
|
|
2134
|
+
// if (!terminal) {
|
|
2135
|
+
// throw new Error('Terminal not available in current session');
|
|
2136
|
+
// }
|
|
2137
|
+
// const saveToPath = `/home/user/cerevox-zerocut/projects/${terminal.id}/materials/${validatedFileName}`;
|
|
2138
|
+
// const saveLocalPath = resolve(
|
|
2139
|
+
// projectLocalDir,
|
|
2140
|
+
// 'materials',
|
|
2141
|
+
// validatedFileName
|
|
2142
|
+
// );
|
|
2143
|
+
// // 解析尺寸参数
|
|
2144
|
+
// const sizeArray = size.split('x');
|
|
2145
|
+
// if (sizeArray.length !== 2) {
|
|
2146
|
+
// throw new Error(
|
|
2147
|
+
// `Invalid size format: ${size}. Expected format: WIDTHxHEIGHT`
|
|
2148
|
+
// );
|
|
2149
|
+
// }
|
|
2150
|
+
// const [widthStr, heightStr] = sizeArray;
|
|
2151
|
+
// const width = Number(widthStr);
|
|
2152
|
+
// const height = Number(heightStr);
|
|
2153
|
+
// if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) {
|
|
2154
|
+
// throw new Error(
|
|
2155
|
+
// `Invalid dimensions: ${width}x${height}. Both width and height must be positive numbers`
|
|
2156
|
+
// );
|
|
2157
|
+
// }
|
|
2158
|
+
// console.log(`Compiling Ken Burns motion command...`);
|
|
2159
|
+
// let command: string;
|
|
2160
|
+
// try {
|
|
2161
|
+
// command = await compileKenBurnsMotion(
|
|
2162
|
+
// image_path,
|
|
2163
|
+
// duration,
|
|
2164
|
+
// camera_motion as CameraMotion,
|
|
2165
|
+
// {
|
|
2166
|
+
// output: saveToPath,
|
|
2167
|
+
// width,
|
|
2168
|
+
// height,
|
|
2169
|
+
// }
|
|
2170
|
+
// );
|
|
2171
|
+
// } catch (compileError) {
|
|
2172
|
+
// console.error(
|
|
2173
|
+
// 'Failed to compile Ken Burns motion command:',
|
|
2174
|
+
// compileError
|
|
2175
|
+
// );
|
|
2176
|
+
// throw new Error(`Failed to compile Ken Burns motion: ${compileError}`);
|
|
2177
|
+
// }
|
|
2178
|
+
// console.log(`Executing FFmpeg command: ${command.substring(0, 100)}...`);
|
|
2179
|
+
// const res = await terminal.run(command);
|
|
2180
|
+
// const result = await res.json();
|
|
2181
|
+
// if (result.exitCode !== 0) {
|
|
2182
|
+
// console.error('FFmpeg command failed:', result);
|
|
2183
|
+
// return {
|
|
2184
|
+
// content: [
|
|
2185
|
+
// {
|
|
2186
|
+
// type: 'text' as const,
|
|
2187
|
+
// text: JSON.stringify({
|
|
2188
|
+
// success: false,
|
|
2189
|
+
// error: 'Ken Burns motion generation failed',
|
|
2190
|
+
// exitCode: result.exitCode,
|
|
2191
|
+
// stderr: result.stderr,
|
|
2192
|
+
// command,
|
|
2193
|
+
// timestamp: new Date().toISOString(),
|
|
2194
|
+
// }),
|
|
2195
|
+
// },
|
|
2196
|
+
// ],
|
|
2197
|
+
// };
|
|
2198
|
+
// }
|
|
2199
|
+
// console.log(
|
|
2200
|
+
// 'Ken Burns motion generated successfully, downloading file...'
|
|
2201
|
+
// );
|
|
2202
|
+
// try {
|
|
2203
|
+
// await files.download(saveToPath, saveLocalPath);
|
|
2204
|
+
// } catch (downloadError) {
|
|
2205
|
+
// console.warn('Failed to download file to local:', downloadError);
|
|
2206
|
+
// // 继续执行,因为远程文件已生成成功
|
|
2207
|
+
// }
|
|
2208
|
+
// const resultData = {
|
|
2209
|
+
// success: true,
|
|
2210
|
+
// uri: saveToPath,
|
|
2211
|
+
// durationMs: Math.floor(duration * 1000),
|
|
2212
|
+
// cameraMotion: camera_motion,
|
|
2213
|
+
// imagePath: image_path,
|
|
2214
|
+
// size,
|
|
2215
|
+
// dimensions: { width, height },
|
|
2216
|
+
// timestamp: new Date().toISOString(),
|
|
2217
|
+
// };
|
|
2218
|
+
// console.log(`Ken Burns motion completed: ${saveToPath}`);
|
|
2219
|
+
// currentSession.track('KenBurns Video Generated', {
|
|
2220
|
+
// durationMs: Math.floor(duration * 1000).toString(),
|
|
2221
|
+
// cameraMotion: camera_motion,
|
|
2222
|
+
// imagePath: image_path,
|
|
2223
|
+
// size,
|
|
2224
|
+
// dimensions: size,
|
|
2225
|
+
// });
|
|
2226
|
+
// return {
|
|
2227
|
+
// content: [
|
|
2228
|
+
// {
|
|
2229
|
+
// type: 'text' as const,
|
|
2230
|
+
// text: JSON.stringify(resultData),
|
|
2231
|
+
// },
|
|
2232
|
+
// ],
|
|
2233
|
+
// };
|
|
2234
|
+
// } catch (error) {
|
|
2235
|
+
// return createErrorResponse(error, 'generate-video-kenburns');
|
|
2236
|
+
// }
|
|
2237
|
+
// }
|
|
2238
|
+
// );
|
|
2001
2239
|
server.registerTool('generate-sound-effect', {
|
|
2002
2240
|
title: 'Generate Sound Effect',
|
|
2003
2241
|
description: 'Generate sound effect with text prompt.',
|
|
@@ -3361,17 +3599,22 @@ server.registerTool('lip-sync', {
|
|
|
3361
3599
|
title: 'Lip Sync',
|
|
3362
3600
|
description: 'Generate lip-synced video by matching video with audio.',
|
|
3363
3601
|
inputSchema: {
|
|
3602
|
+
type: zod_1.z.enum(['vidu', 'basic', 'lite']).default('vidu'),
|
|
3364
3603
|
videoFileName: zod_1.z
|
|
3365
3604
|
.string()
|
|
3366
3605
|
.describe('The video file name in materials directory.'),
|
|
3367
3606
|
audioFileName: zod_1.z
|
|
3368
3607
|
.string()
|
|
3369
3608
|
.describe('The audio file name in materials directory.'),
|
|
3609
|
+
refPhotoFileName: zod_1.z
|
|
3610
|
+
.string()
|
|
3611
|
+
.optional()
|
|
3612
|
+
.describe('The reference photo face for lip sync.'),
|
|
3370
3613
|
saveToFileName: zod_1.z
|
|
3371
3614
|
.string()
|
|
3372
3615
|
.describe('The filename to save the lip-synced video.'),
|
|
3373
3616
|
},
|
|
3374
|
-
}, async ({ videoFileName, audioFileName, saveToFileName }, context) => {
|
|
3617
|
+
}, async ({ type, videoFileName, audioFileName, refPhotoFileName, saveToFileName }, context) => {
|
|
3375
3618
|
try {
|
|
3376
3619
|
// 验证session状态
|
|
3377
3620
|
const currentSession = await validateSession('lip-sync');
|
|
@@ -3386,13 +3629,19 @@ server.registerTool('lip-sync', {
|
|
|
3386
3629
|
const videoUrl = getMaterialUri(currentSession, videoFileName);
|
|
3387
3630
|
const audioUrl = getMaterialUri(currentSession, audioFileName // processedAudioFileName
|
|
3388
3631
|
);
|
|
3632
|
+
const refPhotoUrl = refPhotoFileName
|
|
3633
|
+
? getMaterialUri(currentSession, refPhotoFileName)
|
|
3634
|
+
: undefined;
|
|
3389
3635
|
console.log(`Video URL: ${videoUrl}`);
|
|
3390
3636
|
console.log(`Audio URL: ${audioUrl}`);
|
|
3637
|
+
console.log(`Ref Photo URL: ${refPhotoUrl}`);
|
|
3391
3638
|
// 调用AI的lipSync方法,使用处理后的音频
|
|
3392
3639
|
let progress = 0;
|
|
3393
3640
|
const result = await currentSession.ai.lipSync({
|
|
3641
|
+
type,
|
|
3394
3642
|
videoUrl,
|
|
3395
3643
|
audioUrl,
|
|
3644
|
+
ref_photo_url: refPhotoUrl,
|
|
3396
3645
|
onProgress: async (metaData) => {
|
|
3397
3646
|
console.log('Lip sync progress:', metaData);
|
|
3398
3647
|
try {
|