@onedeepai/video_generator
Version:
阿里云文生视频MCP服务器
318 lines (268 loc) • 9.8 kB
text/typescript
import axios from 'axios';
import * as readline from 'readline';
import * as fs from 'fs';
import * as path from 'path';
// 视频生成模型配置 - 醒目位置
const MODEL = 'wanx2.1-t2v-turbo';
// 配置信息
interface Config {
apiKey: string;
videoGenerationApi: string;
taskStatusApi: string;
}
// 从环境变量和配置文件读取配置
let config: Config = {
apiKey: process.env.DASHSCOPE_API_KEY || '', // 从环境变量读取API密钥
videoGenerationApi: 'https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis',
taskStatusApi: 'https://dashscope.aliyuncs.com/api/v1/tasks/'
};
// 检查是否存在配置文件并读取
const configPath = path.join(__dirname, 'config.json');
if (fs.existsSync(configPath)) {
try {
const configData = JSON.parse(fs.readFileSync(configPath, 'utf8'));
config = { ...config, ...configData };
console.log('已加载配置文件');
} catch (error) {
console.warn('配置文件读取失败,使用默认配置', error);
}
}
// API常量
const API_KEY = config.apiKey;
const VIDEO_GENERATION_API = config.videoGenerationApi;
const TASK_STATUS_API = config.taskStatusApi;
// API响应类型定义
interface TaskResponse {
task_id?: string;
output?: {
task_id?: string;
task_status?: string;
video_url?: string;
result?: {
video_url?: string;
};
actual_prompt?: string;
};
task_status?: string;
status?: string;
message?: string;
}
// 创建命令行接口
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// 视频参数接口
interface VideoParams {
prompt: string;
size?: string;
duration?: number;
}
// 创建视频生成任务
async function generateVideo(params: VideoParams): Promise<string> {
console.log('正在发送视频生成请求...');
try {
const { prompt, size = '1280*720', duration = 5 } = params;
// 验证提示词不能为空
if (!prompt || prompt.trim() === '') {
throw new Error('提示词不能为空');
}
// 创建视频生成任务
const response = await axios.post(
VIDEO_GENERATION_API,
{
model: MODEL,
input: {
prompt,
},
parameters: {
size,
duration,
prompt_extend: true, // 启用提示词扩展
},
},
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'X-DashScope-Async': 'enable',
},
}
);
const responseData = response.data as TaskResponse;
// 检查是否有扩展提示词
if (responseData.output?.actual_prompt) {
console.log('系统扩展生成的提示词:');
console.log(responseData.output.actual_prompt);
}
console.log('视频生成请求已发送,获取任务ID:');
console.log(JSON.stringify(responseData, null, 2));
// 检查响应格式并提取任务ID
if (responseData.task_id) {
return responseData.task_id;
} else if (responseData.output?.task_id) {
return responseData.output.task_id;
} else {
throw new Error('无法从响应中获取任务ID');
}
} catch (error: any) {
console.error('视频生成请求失败:');
if (axios.isAxiosError(error) && error.response) {
console.error('API错误响应:', error.response.data);
throw new Error(`视频生成失败: ${error.response.data.message || '未知错误'}`);
}
throw new Error(`视频生成失败: ${error.message || '未知错误'}`);
}
}
// 查询视频生成任务状态
async function checkVideoStatus(taskId: string): Promise<any> {
// 不输出查询日志,保持界面简洁
try {
// 查询任务状态
const response = await axios.get(
`${TASK_STATUS_API}${taskId}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
}
);
return response.data;
} catch (error: any) {
// 简化错误输出
throw new Error(`查询失败: ${error.message || '未知错误'}`);
}
}
// 轮询任务状态直到完成
async function pollTaskUntilComplete(taskId: string, intervalMs: number = 5000, maxAttempts: number = 30): Promise<TaskResponse> {
console.log(`开始轮询任务状态...`);
let attempts = 0;
let errorCount = 0; // 连续错误计数
const startTime = Date.now();
const maxWaitTimeMs = 6 * 60 * 1000; // 最多等待6分钟
// 轮询任务状态
while (attempts < maxAttempts) {
attempts++;
// 检查总体超时
if (Date.now() - startTime > maxWaitTimeMs) {
throw new Error(`任务等待超时(${maxWaitTimeMs/60000}分钟),请稍后手动查询结果`);
}
try {
const statusResult = await checkVideoStatus(taskId) as TaskResponse;
// 重置错误计数
errorCount = 0;
// 根据API响应结构获取状态
const status = statusResult.task_status ||
(statusResult.output && statusResult.output.task_status) ||
statusResult.status;
// 计算已等待时间(秒)
const waitedSeconds = Math.floor((Date.now() - startTime) / 1000);
// 简化的状态输出
console.log(`等待中,已等待${waitedSeconds}秒...状态: ${status}`);
// 如果任务完成或失败,返回结果
if (status === 'SUCCEEDED') {
console.log('任务已成功完成!');
return statusResult;
} else if (status === 'FAILED') {
console.log('任务失败');
throw new Error(`任务失败: ${statusResult.message || '未知错误'}`);
} else if (!status) {
console.log('无法获取任务状态');
}
// 等待指定时间后再次查询
await new Promise(resolve => setTimeout(resolve, intervalMs));
} catch (error: any) {
errorCount++;
const waitedSeconds = Math.floor((Date.now() - startTime) / 1000);
console.log(`等待中,已等待${waitedSeconds}秒...状态: ERROR (错误 ${errorCount}/3)`);
// 连续多次查询失败,抛出异常
if (errorCount >= 3) {
throw new Error(`连续查询失败${errorCount}次,请检查网络或任务ID`);
}
await new Promise(resolve => setTimeout(resolve, intervalMs));
}
}
throw new Error(`达到最大轮询次数(${maxAttempts}),任务尚未完成`);
}
// 用户交互函数
function promptUser(question: string): Promise<string> {
return new Promise((resolve) => {
rl.question(question, (answer) => {
resolve(answer);
});
});
}
// 主函数
async function main() {
try {
console.log('=== 阿里云视频生成工具 ===');
console.log(`当前使用的API密钥: ${API_KEY.substring(0, 5)}***${API_KEY.substring(API_KEY.length - 4)}`);
// 获取用户输入并验证
let prompt = '';
while (!prompt || prompt.trim() === '') {
prompt = await promptUser('请输入视频生成提示词: ');
if (!prompt || prompt.trim() === '') {
console.log('提示词不能为空,请重新输入');
}
}
// 验证视频尺寸
let size = await promptUser('请输入视频尺寸 (默认 1280*720): ') || '1280*720';
// 检查尺寸格式
if (!/^\d+\*\d+$/.test(size)) {
console.log(`尺寸格式不正确,将使用默认大小 1280*720`);
size = '1280*720';
}
// 验证视频时长
const durationStr = await promptUser('请输入视频时长(秒) (默认 5秒): ') || '5';
let duration = parseInt(durationStr);
// 检查时长是否有效
if (isNaN(duration) || duration <= 0 || duration > 60) {
console.log(`输入的时长无效,使用默认时长 5秒`);
duration = 5;
}
console.log(`\n生成视频参数:\n- 提示词: ${prompt}\n- 尺寸: ${size}\n- 时长: ${duration}秒`);
// 创建视频生成任务
const taskId = await generateVideo({ prompt, size, duration });
if (!taskId) {
throw new Error('未能获取有效的任务ID,请检查API响应');
}
console.log(`任务已创建,ID: ${taskId}`);
// 默认执行轮询任务状态
try {
// 设置轮询间隔为5秒,最多30次尝试
const result = await pollTaskUntilComplete(taskId, 5000, 30);
// 显示生成的视频URL
if (result.output?.video_url) {
console.log(`\n视频生成成功!\n视频URL: ${result.output.video_url}`);
} else if (result.output?.result?.video_url) {
console.log(`\n视频生成成功!\n视频URL: ${result.output.result.video_url}`);
} else {
console.log('\n视频生成成功,但未获取到视频URL');
console.log('完整响应数据:');
console.log(JSON.stringify(result, null, 2));
}
// 显示扩展提示词(如果有)
if (result.output?.actual_prompt) {
console.log('\n系统扩展生成的完整提示词:');
console.log(result.output.actual_prompt);
}
} catch (error: any) {
console.error(`\n轮询任务状态失败: ${error.message}`);
console.error('请记下任务ID,稍后使用API手动查询结果');
console.log(`任务ID: ${taskId}`);
}
} catch (error: any) {
console.error(`\n错误: ${error.message}`);
if (error.response?.data) {
console.error('API错误详情:');
console.error(JSON.stringify(error.response.data, null, 2));
}
} finally {
// 关闭readline接口
rl.close();
console.log('\n程序执行完毕');
}
}
// 运行主函数
main();