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.
@@ -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('generate-video-kenburns', {
1856
- title: 'Generate Ken Burns Motion Video',
1857
- description: 'Generate Ken Burns motion video with customizable camera movements.',
1858
- inputSchema: {
1859
- image_path: zod_1.z.string().describe('The start frame image path(not URL).'),
1860
- duration: zod_1.z
1861
- .number()
1862
- .min(2)
1863
- .max(16)
1864
- .describe('The duration of the video.'),
1865
- camera_motion: zod_1.z
1866
- .enum([
1867
- 'zoom_in',
1868
- 'zoom_out',
1869
- 'zoom_in_left_top',
1870
- 'zoom_out_left_top',
1871
- 'zoom_in_right_top',
1872
- 'zoom_out_right_top',
1873
- 'zoom_in_left_bottom',
1874
- 'zoom_out_left_bottom',
1875
- 'zoom_in_right_bottom',
1876
- 'zoom_out_right_bottom',
1877
- 'pan_up',
1878
- 'pan_down',
1879
- 'pan_left',
1880
- 'pan_right',
1881
- 'static',
1882
- ])
1883
- .optional()
1884
- .default('zoom_in')
1885
- .describe('The camera motion type for Ken Burns effect.'),
1886
- size: zod_1.z
1887
- .enum([
1888
- '1024x1024',
1889
- '864x1152',
1890
- '1152x864',
1891
- '1280x720',
1892
- '720x1280',
1893
- '832x1248',
1894
- '1248x832',
1895
- '1512x648',
1896
- ])
1897
- .describe('The size of the image.'),
1898
- saveToFileName: zod_1.z.string().describe('The filename to save.'),
1899
- },
1900
- }, async ({ image_path, duration, camera_motion = 'zoom_in', size, saveToFileName, }) => {
1901
- try {
1902
- // 验证session状态
1903
- const currentSession = await validateSession('generate-video-kenburns');
1904
- const validatedFileName = validateFileName(saveToFileName);
1905
- const files = currentSession.files;
1906
- const terminal = currentSession.terminal;
1907
- // 如果 image_path 是文件名,需要拼接完整路径
1908
- if ((0, node_path_1.dirname)(image_path) === '.') {
1909
- image_path = (0, node_path_1.resolve)('/home/user/cerevox-zerocut/projects', terminal.id, 'materials', image_path);
1910
- }
1911
- console.log(`Generating Ken Burns motion: ${image_path} (${duration}s, ${size}, ${camera_motion})`);
1912
- if (!terminal) {
1913
- throw new Error('Terminal not available in current session');
1914
- }
1915
- const saveToPath = `/home/user/cerevox-zerocut/projects/${terminal.id}/materials/${validatedFileName}`;
1916
- const saveLocalPath = (0, node_path_1.resolve)(projectLocalDir, 'materials', validatedFileName);
1917
- // 解析尺寸参数
1918
- const sizeArray = size.split('x');
1919
- if (sizeArray.length !== 2) {
1920
- throw new Error(`Invalid size format: ${size}. Expected format: WIDTHxHEIGHT`);
1921
- }
1922
- const [widthStr, heightStr] = sizeArray;
1923
- const width = Number(widthStr);
1924
- const height = Number(heightStr);
1925
- if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) {
1926
- throw new Error(`Invalid dimensions: ${width}x${height}. Both width and height must be positive numbers`);
1927
- }
1928
- console.log(`Compiling Ken Burns motion command...`);
1929
- let command;
1930
- try {
1931
- command = await (0, videokit_1.compileKenBurnsMotion)(image_path, duration, camera_motion, {
1932
- output: saveToPath,
1933
- width,
1934
- height,
1935
- });
1936
- }
1937
- catch (compileError) {
1938
- console.error('Failed to compile Ken Burns motion command:', compileError);
1939
- throw new Error(`Failed to compile Ken Burns motion: ${compileError}`);
1940
- }
1941
- console.log(`Executing FFmpeg command: ${command.substring(0, 100)}...`);
1942
- const res = await terminal.run(command);
1943
- const result = await res.json();
1944
- if (result.exitCode !== 0) {
1945
- console.error('FFmpeg command failed:', result);
1946
- return {
1947
- content: [
1948
- {
1949
- type: 'text',
1950
- text: JSON.stringify({
1951
- success: false,
1952
- error: 'Ken Burns motion generation failed',
1953
- exitCode: result.exitCode,
1954
- stderr: result.stderr,
1955
- command,
1956
- timestamp: new Date().toISOString(),
1957
- }),
1958
- },
1959
- ],
1960
- };
1961
- }
1962
- console.log('Ken Burns motion generated successfully, downloading file...');
1963
- try {
1964
- await files.download(saveToPath, saveLocalPath);
1965
- }
1966
- catch (downloadError) {
1967
- console.warn('Failed to download file to local:', downloadError);
1968
- // 继续执行,因为远程文件已生成成功
1969
- }
1970
- const resultData = {
1971
- success: true,
1972
- uri: saveToPath,
1973
- durationMs: Math.floor(duration * 1000),
1974
- cameraMotion: camera_motion,
1975
- imagePath: image_path,
1976
- size,
1977
- dimensions: { width, height },
1978
- timestamp: new Date().toISOString(),
1979
- };
1980
- console.log(`Ken Burns motion completed: ${saveToPath}`);
1981
- currentSession.track('KenBurns Video Generated', {
1982
- durationMs: Math.floor(duration * 1000).toString(),
1983
- cameraMotion: camera_motion,
1984
- imagePath: image_path,
1985
- size,
1986
- dimensions: size,
1987
- });
1988
- return {
1989
- content: [
1990
- {
1991
- type: 'text',
1992
- text: JSON.stringify(resultData),
1993
- },
1994
- ],
1995
- };
1996
- }
1997
- catch (error) {
1998
- return createErrorResponse(error, 'generate-video-kenburns');
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 {