



制作动态视频,会用到多个子工作流:
主工作流:文生图 + 串联图生视频 + 生成剪映草稿
子工作流1:批量提交图生视频任务 + 循环查询任务
子工作流2:通过taskidList批量查询任务状态 + 输出视频地址
一、搭建子工作流2: search_video
我们先来搭建子工作流2:通过taskidList批量查询任务状态
子工作流2:入参task_id_list 和 api_key

代码节点之前是用于生成video_url失败的情况下,这个做一个特殊的占位标识,可以在主流程中用静态图片等替代,比如失败后将video_url固定复制一个字符串,外层工作流进行代码判断做特殊处理,这里没有用到, 只是预留

结束节点输出视频地址:

二、搭建子工作流1:img_to_video
批量提交循环中,代码_3做了一个1s的等待,避免频率过高导致提交失败:

-
async function main({ params }: Args): Promise<Output> {
await sleep(1000); // 等待1秒
// 构建输出对象
const ret = {
"status": 0
};
return ret;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
在当前工作流中,调用我们上面创建的子工作流2:search_video,用于批量查询任务状态

循环查询任务状态,这里按次数 + 等待30s, 阿里通义-turbo一般3-5分钟可生成,循环次数够用,这里没有使用无限循环,防止进入死循环


async function main({ params }: Args): Promise<Output> {
await sleep(30000); // 等待30秒
let video_urls = params.video_urls;
const nonEmptyUrls = video_urls.filter(
item => item != null && item.trim() !== ""
);
const length = Array.isArray(params.taskid_list) ? params.taskid_list.length : 0;
// 构建输出对象
const ret = {
"video_urls": nonEmptyUrls,
"ori_length": length
};
return ret;
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
由于我们循环批量查询了很多次任务状态,但只有最后一次才全部成功,才会有全部的视频地址,所以增加代码_2节点获取最后一次批量查询到的视频地址集合;

-
async function main({ params }: Args): Promise<Output> {
var urls = params.urls;
var final_list = []
var list = urls[urls.length-1].video_urls;
for(let i=0;i<list.length;i++)
{
final_list.push(list[i]);
}
// 构建输出对象
const ret = {
"final_list":final_list
};
return ret;
}
输出视频地址:

到此,我们已经把 批量图生视频的 创建和查询工作流搭建完成
接下来就和之前分享的火柴人流程类似了,在主流程中把批量图生视频的子流程串起来就可以了。
三、搭建主流程


开始节点,支持自定义绘画提示词,视频长度和背景音乐:

增加 代码节点 计算图片数量
默认一张图片生成5s视频,最多支持20s,就是最大4张图片,视频时长支持5/10/15/20 三种,对应图片数量也就是1/2/3/4张 四种情况:

// 在这里,您可以通过 ‘params’ 获取节点中的输入变量,并通过 'ret' 输出结果
// 'params' 和 'ret' 已经被正确地注入到环境中
// 下面是一个示例,获取节点输入中参数名为‘input’的值:
// const input = params.input;
// 下面是一个示例,输出一个包含多种数据类型的 'ret' 对象:
// const ret = { "name": ‘小明’, "hobbies": [“看书”, “旅游”] };
async function main({ params }: Args): Promise<Output> {
var times = params.times;
var times_data = times/5;
if(times_data>4){
times_data=4;
}
// 构建输出对象
const ret = {
"times": times_data
};
return ret;
}
直接按代码的次数循环,生成指定数量的首帧图片:


增加代码_2组长 图生视频需要的数据格式:

-
// 在这里,您可以通过 ‘params’ 获取节点中的输入变量,并通过 'ret' 输出结果
// 'params' 和 'ret' 已经被正确地注入到环境中
// 下面是一个示例,获取节点输入中参数名为‘input’的值:
// const input = params.input;
// 下面是一个示例,输出一个包含多种数据类型的 'ret' 对象:
// const ret = { "name": ‘小明’, "hobbies": [“看书”, “旅游”] };
async function main({ params }: Args): Promise<Output> {
var prompt = params.prompt;
var imgList = params.imgList;
var data =[];
for(var img of imgList){
data.push({
img_url:img,
prompt:prompt
});
}
// 构建输出对象
const ret = {
"img_list": data
};
return ret;
}
最后,通过代码节点组装 背景音乐 和视频素材,生成剪映草稿:

-
// 在这里,您可以通过 ‘params’ 获取节点中的输入变量,并通过 'ret' 输出结果
// 'params' 和 'ret' 已经被正确地注入到环境中
// 下面是一个示例,获取节点输入中参数名为‘input’的值:
// const input = params.input;
// 下面是一个示例,输出一个包含多种数据类型的 'ret' 对象:
// const ret = { "name": ‘小明’, "hobbies": [“看书”, “旅游”] };
async function main({ params }: Args): Promise<Output> {
const { video_url_list} = params;
var bg_audio_url = params.bg_audio_url;
// 处理音频数据
let audioStartTime = 0;
let maxDuration = 0;
const videoTimelines = [];
const video_urls = [];
// 处理图片数据
const imageData = [];
let imageStartTime = 0;
// 特效 [{"effect_title":"金粉闪闪","end":5000000,"start":0}]
const eff_list =[];
const img_timelines = [];
const video_timelines =[];
for (let i = 0; i < video_url_list.length; i++) {
const duration = 5000000;
video_timelines.push({
start: imageStartTime,
end: imageStartTime + duration
});
imageStartTime += duration;
maxDuration = maxDuration + duration;
}
// 添加背景音乐
const bg_audio_list = [];
const bg_audio_timelines = [];
//var audio_url = "https://p9-bot-workflow-sign.byteimg.com/tos-cn-i-mdko3gqilj/d15a8f419716466f8c6976ded535332e.mp3~tplv-mdko3gqilj-image.image?rk3s=81d4c505&x-expires=1775488748&x-signature=r%2FLegsQ9uDw2FzTYk68fMGFMKOQ%3D&x-wf-file_name=%E7%BE%BD%E8%82%BF+-+Windy+Hill+%28%E9%A3%8E%E4%B9%8B%E8%B0%B7%29.mp3";
// 默认动漫
var audio_url ="https://p9-bot-workflow-sign.byteimg.com/tos-cn-i-mdko3gqilj/5f75fc94355e45178365b66a7fcfce90.mp3~tplv-mdko3gqilj-image.image?rk3s=81d4c505&x-expires=1775521513&x-signature=1wzdU%2BAgwe2c5m9LNDPPrui1Wts%3D&x-wf-file_name=%E6%B2%BB%E6%84%88%E7%B3%BB%E5%8A%A8%E6%BC%AB.mp3";
if(bg_audio_url && bg_audio_url != '默认'){
audio_url = bg_audio_url;
}
bg_audio_list.push(audio_url);
bg_audio_timelines.push({
start: 0,
end: maxDuration
});
// 构建输出对象
const result = {
bg_audio_list: bg_audio_list,
bg_autdo_timelines: bg_audio_timelines,
video_timelines: video_timelines,
video_urls : video_url_list
};
return result;
}
视频下载
-
下载剪映小助手(视频需要用小助手下载),下载地址:https://ts.fyshark.com/#/login?user_id=10299

-
将草稿链接复制到剪映小助手,点击创建草稿

将下载的文件夹复制到剪映草稿文件夹,也可以直接配置下载路径为剪映草稿路径:

打开剪映,查看和导出视频:(需要安装剪映:https://www.capcut.cn/)

本篇文章来源于微信公众号: CodeL
文章评论