ADD_VIDEOS API 接口文档
接口信息
POST /api/drafts/add_videos
功能描述
批量向现有草稿中添加视频素材。该接口是一个功能强大的视频添加工具,支持多个视频的批量处理,包括时间范围控制、透明度调整、遮罩效果、转场动画、音量控制、缩放变换等高级功能。特别适合创建复杂的多视频组合场景,如画中画效果、视频拼接、过渡动画等。
请求参数
{
"draft_url": "https://ts.fyshark.com/#/cozeToJianyin?drafId=https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/2025-05-28/draft/2f52a63b-8c6a-4417-8b01-1b2a569ccb6c.json",
"video_infos": "[{\"video_url\":\"https://fy-oss-docker.oss-cn-shenzhen.aliyuncs.com/c7228c81-1fb1-480d-b356-5dcbf3c40fc8.mp4\",\"width\":1024,\"height\":1024,\"start\":0,\"end\":5000000,\"duration\":5000000,\"mask\":\"圆形\",\"transition\":\"淡入淡出\",\"transition_duration\":500000,\"volume\":0.8}]",
"alpha": 0.5,
"scale_x": 1.0,
"scale_y": 1.0,
"transform_x": 100,
"transform_y": 200
}
主要参数
参数名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
draft_url | string | ✅ | - | 目标草稿的完整URL |
video_infos | string | ✅ | - | 视频信息数组的JSON字符串 |
alpha | number | ❌ | 1.0 | 全局透明度(0-1) |
scale_x | number | ❌ | 1.0 | X轴缩放比例 |
scale_y | number | ❌ | 1.0 | Y轴缩放比例 |
transform_x | number | ❌ | 0 | X轴位置偏移(像素) |
transform_y | number | ❌ | 0 | Y轴位置偏移(像素) |
video_infos 数组结构
video_infos是一个JSON字符串,解析后为数组,每个元素包含以下字段:
字段名 | 类型 | 必填 | 默认值 | 说明 |
---|---|---|---|---|
video_url | string | ✅ | - | 视频文件的URL地址 |
width | number | ✅ | - | 视频宽度(像素) |
height | number | ✅ | - | 视频高度(像素) |
start | number | ✅ | - | 视频开始播放时间(微秒) |
end | number | ✅ | - | 视频结束播放时间(微秒) |
duration | number | ✅ | - | 视频总时长(微秒) |
mask | string | ❌ | - | 遮罩类型 |
transition | string | ❌ | - | 转场效果名称 |
transition_duration | number | ❌ | 500000 | 转场持续时间(微秒) |
volume | number | ❌ | 1.0 | 音量大小(0-1) |
参数详解
时间参数
- start: 视频在时间轴上的开始时间,单位为微秒(1秒 = 1,000,000微秒)
- end: 视频在时间轴上的结束时间,单位为微秒
- duration: 视频文件的总时长,用于素材创建,单位为微秒
- 播放时长: 实际播放时长 = end - start
透明度参数
- alpha: 全局透明度,应用于所有添加的视频
- 1.0 = 完全不透明
- 0.5 = 半透明
- 0.0 = 完全透明
- 范围:0.0 - 1.0
缩放参数
- scale_x: X轴方向的缩放比例
- scale_y: Y轴方向的缩放比例
- 1.0 = 原始大小,0.5 = 缩小一半,2.0 = 放大两倍
- 建议范围:0.1 - 5.0
位置参数
- transform_x: X轴方向的位置偏移,单位为像素
- transform_y: Y轴方向的位置偏移,单位为像素
- 正值向右/下移动,负值向左/上移动
- 以画布中心为原点
遮罩类型
支持的遮罩类型:
圆形
- 圆形遮罩效果爱心
- 爱心形状遮罩星形
- 星形遮罩矩形
- 矩形遮罩线性
- 线性渐变遮罩镜面
- 镜面反射遮罩
转场效果
- transition: 转场效果名称(具体支持的转场类型需要参考jy_draft文档)
- transition_duration: 转场持续时间
- 最小值:100,000微秒(0.1秒)
- 最大值:2,500,000微秒(2.5秒)
- 推荐值:500,000微秒(0.5秒)
- 超出范围将自动设置为默认值
音量控制
- volume: 视频音量大小
- 1.0 = 原始音量
- 0.5 = 一半音量
- 0.0 = 静音
- 范围:0.0 - 1.0
响应格式
成功响应 (200)
{
"status": "success",
"message": "视频批量添加成功",
"data": {
"draft_url": "https://ts.fyshark.com/#/cozeToJianyin?drafId=...",
"track_id": "video-track-uuid",
"video_ids": ["video1-uuid", "video2-uuid", "video3-uuid"],
"segment_ids": ["segment1-uuid", "segment2-uuid", "segment3-uuid"],
"videos_count": 3,
"total_duration": 15000000
}
}
错误响应 (4xx/5xx)
{
"status": "error",
"message": "错误信息",
"error": "详细错误描述"
}
使用示例
cURL 示例
1. 基本视频添加
curl -X POST https://jy-api.fyshark.com/api/drafts/add_videos \
-H "Content-Type: application/json" \
-d '{
"draft_url": "YOUR_DRAFT_URL",
"video_infos": "[{\"video_url\":\"https://example.com/video1.mp4\",\"width\":1920,\"height\":1080,\"start\":0,\"end\":5000000,\"duration\":10000000}]"
}'
2. 多视频批量添加
curl -X POST https://jy-api.fyshark.com/api/drafts/add_videos \
-H "Content-Type: application/json" \
-d '{
"draft_url": "YOUR_DRAFT_URL",
"video_infos": "[{\"video_url\":\"https://example.com/video1.mp4\",\"width\":1920,\"height\":1080,\"start\":0,\"end\":5000000,\"duration\":10000000},{\"video_url\":\"https://example.com/video2.mp4\",\"width\":1280,\"height\":720,\"start\":5000000,\"end\":10000000,\"duration\":8000000}]",
"alpha": 0.8
}'
3. 带遮罩和转场的视频
curl -X POST https://jy-api.fyshark.com/api/drafts/add_videos \
-H "Content-Type: application/json" \
-d '{
"draft_url": "YOUR_DRAFT_URL",
"video_infos": "[{\"video_url\":\"https://example.com/video1.mp4\",\"width\":1024,\"height\":1024,\"start\":0,\"end\":5000000,\"duration\":10000000,\"mask\":\"圆形\",\"transition\":\"淡入淡出\",\"transition_duration\":500000,\"volume\":0.8}]",
"alpha": 1.0,
"scale_x": 1.2,
"scale_y": 1.2
}'
4. 画中画效果
curl -X POST https://jy-api.fyshark.com/api/drafts/add_videos \
-H "Content-Type: application/json" \
-d '{
"draft_url": "YOUR_DRAFT_URL",
"video_infos": "[{\"video_url\":\"https://example.com/main.mp4\",\"width\":1920,\"height\":1080,\"start\":0,\"end\":10000000,\"duration\":15000000},{\"video_url\":\"https://example.com/pip.mp4\",\"width\":640,\"height\":360,\"start\":2000000,\"end\":8000000,\"duration\":10000000}]",
"alpha": 1.0,
"transform_x": 300,
"transform_y": -200,
"scale_x": 0.3,
"scale_y": 0.3
}'
JavaScript 示例
const addVideos = async (draftUrl, videoConfig) => {
const response = await fetch('/api/drafts/add_videos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
draft_url: draftUrl,
...videoConfig
})
});
return response.json();
};
// 基本视频添加
const basicVideos = {
video_infos: JSON.stringify([
{
video_url: "https://example.com/video1.mp4",
width: 1920,
height: 1080,
start: 0,
end: 5000000,
duration: 10000000
}
])
};
// 多视频序列
const videoSequence = {
video_infos: JSON.stringify([
{
video_url: "https://example.com/intro.mp4",
width: 1920,
height: 1080,
start: 0,
end: 3000000,
duration: 5000000,
transition: "淡入淡出",
transition_duration: 500000
},
{
video_url: "https://example.com/main.mp4",
width: 1920,
height: 1080,
start: 3000000,
end: 15000000,
duration: 20000000,
volume: 0.8
},
{
video_url: "https://example.com/outro.mp4",
width: 1920,
height: 1080,
start: 15000000,
end: 18000000,
duration: 5000000,
mask: "圆形"
}
]),
alpha: 1.0
};
// 画中画效果
const pipEffect = {
video_infos: JSON.stringify([
{
video_url: "https://example.com/background.mp4",
width: 1920,
height: 1080,
start: 0,
end: 10000000,
duration: 15000000
},
{
video_url: "https://example.com/overlay.mp4",
width: 640,
height: 360,
start: 2000000,
end: 8000000,
duration: 10000000,
mask: "圆形"
}
]),
scale_x: 0.8,
scale_y: 0.8,
transform_x: 200,
transform_y: -150,
alpha: 0.9
};
try {
const result1 = await addVideos(draftUrl, basicVideos);
const result2 = await addVideos(draftUrl, videoSequence);
const result3 = await addVideos(draftUrl, pipEffect);
console.log('视频添加成功:', {
basic: result1.data,
sequence: result2.data,
pip: result3.data
});
} catch (error) {
console.error('添加失败:', error);
}
高级JavaScript示例
class VideoManager {
constructor(baseUrl = 'https://jy-api.fyshark.com') {
this.baseUrl = baseUrl;
}
async addVideos(draftUrl, videoConfig) {
const response = await fetch(`${this.baseUrl}/api/drafts/add_videos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
draft_url: draftUrl,
...videoConfig
})
});
return response.json();
}
// 创建视频序列
createVideoSequence(videos, transitionType = "淡入淡出", transitionDuration = 500000) {
let currentTime = 0;
const videoInfos = videos.map((video, index) => {
const videoInfo = {
video_url: video.url,
width: video.width,
height: video.height,
start: currentTime,
end: currentTime + video.playDuration,
duration: video.totalDuration,
volume: video.volume || 1.0
};
// 添加转场效果(除了最后一个视频)
if (index < videos.length - 1) {
videoInfo.transition = transitionType;
videoInfo.transition_duration = transitionDuration;
}
// 添加遮罩效果
if (video.mask) {
videoInfo.mask = video.mask;
}
currentTime += video.playDuration;
return videoInfo;
});
return {
video_infos: JSON.stringify(videoInfos)
};
}
// 创建画中画效果
createPictureInPicture(mainVideo, overlayVideo, position = { x: 300, y: -200 }, scale = 0.3) {
const videoInfos = [
{
video_url: mainVideo.url,
width: mainVideo.width,
height: mainVideo.height,
start: 0,
end: mainVideo.duration,
duration: mainVideo.duration
},
{
video_url: overlayVideo.url,
width: overlayVideo.width,
height: overlayVideo.height,
start: overlayVideo.start || 0,
end: overlayVideo.end || overlayVideo.duration,
duration: overlayVideo.duration,
mask: overlayVideo.mask || "圆形"
}
];
return {
video_infos: JSON.stringify(videoInfos),
transform_x: position.x,
transform_y: position.y,
scale_x: scale,
scale_y: scale
};
}
// 创建分屏效果
createSplitScreen(videos, layout = "horizontal") {
const videoInfos = videos.map((video, index) => {
let transform_x = 0, transform_y = 0, scale_x = 1, scale_y = 1;
if (layout === "horizontal") {
// 水平分屏
const sectionWidth = 1 / videos.length;
transform_x = (index - (videos.length - 1) / 2) * sectionWidth * video.width;
scale_x = sectionWidth;
} else if (layout === "vertical") {
// 垂直分屏
const sectionHeight = 1 / videos.length;
transform_y = (index - (videos.length - 1) / 2) * sectionHeight * video.height;
scale_y = sectionHeight;
}
return {
video_url: video.url,
width: video.width,
height: video.height,
start: video.start || 0,
end: video.end || video.duration,
duration: video.duration
};
});
return {
video_infos: JSON.stringify(videoInfos)
};
}
// 批量处理视频
async batchProcessVideos(draftUrl, videoConfigs) {
const results = [];
for (const config of videoConfigs) {
try {
const result = await this.addVideos(draftUrl, config);
results.push(result);
// 添加延迟避免请求过快
await new Promise(resolve => setTimeout(resolve, 100));
} catch (error) {
console.error('视频处理失败:', error);
results.push({ error: error.message });
}
}
return results;
}
}
// 使用示例
const videoManager = new VideoManager();
// 创建视频序列
const sequence = videoManager.createVideoSequence([
{
url: "https://example.com/intro.mp4",
width: 1920,
height: 1080,
playDuration: 3000000,
totalDuration: 5000000
},
{
url: "https://example.com/main.mp4",
width: 1920,
height: 1080,
playDuration: 10000000,
totalDuration: 15000000,
volume: 0.8
}
]);
// 创建画中画
const pip = videoManager.createPictureInPicture(
{
url: "https://example.com/background.mp4",
width: 1920,
height: 1080,
duration: 10000000
},
{
url: "https://example.com/overlay.mp4",
width: 640,
height: 360,
duration: 6000000,
start: 2000000,
end: 8000000,
mask: "圆形"
}
);
// 批量处理
await videoManager.batchProcessVideos(draftUrl, [sequence, pip]);
Python 示例 (可选)
import requests
import json
from typing import List, Dict, Optional
class VideoProcessor:
def __init__(self, base_url="https://jy-api.fyshark.com"):
self.base_url = base_url
def add_videos(self, draft_url: str, video_config: Dict) -> Dict:
response = requests.post(
f'{self.base_url}/api/drafts/add_videos',
headers={'Content-Type': 'application/json'},
json={
"draft_url": draft_url,
**video_config
}
)
return response.json()
def create_video_sequence(self, videos: List[Dict], transition_type: str = "淡入淡出") -> Dict:
current_time = 0
video_infos = []
for i, video in enumerate(videos):
video_info = {
"video_url": video["url"],
"width": video["width"],
"height": video["height"],
"start": current_time,
"end": current_time + video["play_duration"],
"duration": video["total_duration"],
"volume": video.get("volume", 1.0)
}
# 添加转场效果(除了最后一个)
if i < len(videos) - 1:
video_info["transition"] = transition_type
video_info["transition_duration"] = 500000
# 添加遮罩
if "mask" in video:
video_info["mask"] = video["mask"]
video_infos.append(video_info)
current_time += video["play_duration"]
return {
"video_infos": json.dumps(video_infos)
}
def create_picture_in_picture(self, main_video: Dict, overlay_video: Dict,
position: Dict = None, scale: float = 0.3) -> Dict:
if position is None:
position = {"x": 300, "y": -200}
video_infos = [
{
"video_url": main_video["url"],
"width": main_video["width"],
"height": main_video["height"],
"start": 0,
"end": main_video["duration"],
"duration": main_video["duration"]
},
{
"video_url": overlay_video["url"],
"width": overlay_video["width"],
"height": overlay_video["height"],
"start": overlay_video.get("start", 0),
"end": overlay_video.get("end", overlay_video["duration"]),
"duration": overlay_video["duration"],
"mask": overlay_video.get("mask", "圆形")
}
]
return {
"video_infos": json.dumps(video_infos),
"transform_x": position["x"],
"transform_y": position["y"],
"scale_x": scale,
"scale_y": scale
}
# 使用示例
processor = VideoProcessor()
# 视频序列
videos = [
{
"url": "https://example.com/intro.mp4",
"width": 1920,
"height": 1080,
"play_duration": 3000000,
"total_duration": 5000000
},
{
"url": "https://example.com/main.mp4",
"width": 1920,
"height": 1080,
"play_duration": 10000000,
"total_duration": 15000000,
"volume": 0.8
}
]
sequence_config = processor.create_video_sequence(videos)
result = processor.add_videos("YOUR_DRAFT_URL", sequence_config)
print(f"结果: {result}")
错误码说明
错误码 | 错误信息 | 说明 | 解决方案 |
---|---|---|---|
400 | draft_url是必填项 | 缺少草稿URL参数 | 提供有效的草稿URL |
400 | video_infos是必填项 | 缺少视频信息参数 | 提供有效的视频信息JSON |
400 | video_infos格式错误 | JSON格式不正确 | 检查JSON字符串格式 |
400 | 视频配置验证失败 | 视频参数不符合要求 | 检查每个视频的参数 |
400 | video_url是必填项 | 视频URL缺失 | 为每个视频提供URL |
400 | 视频尺寸无效 | width或height无效 | 提供正数的宽度和高度 |
400 | 时间范围无效 | end必须大于start | 确保结束时间大于开始时间 |
400 | 透明度值无效 | alpha不在0-1范围内 | 使用0-1之间的透明度值 |
400 | 转场时长无效 | transition_duration超出范围 | 使用100000-2500000微秒范围 |
404 | 草稿不存在 | 指定的草稿URL无效 | 检查草稿URL是否正确 |
404 | 视频资源不存在 | 视频URL无法访问 | 检查视频URL是否可访问 |
500 | 视频处理失败 | 内部处理错误 | 联系技术支持 |
注意事项
- JSON格式: video_infos必须是合法的JSON字符串
- 时间单位: 所有时间参数使用微秒(1秒 = 1,000,000微秒)
- 视频格式: 确保视频文件格式被支持(如MP4、AVI等)
- 文件大小: 大视频文件可能影响处理速度
- 网络访问: 视频URL必须可以正常访问
- 遮罩限制: 只支持预定义的遮罩类型
- 转场限制: 转场时长有固定范围限制
- 性能考虑: 批量添加大量视频可能影响性能
工作流程
- 验证必填参数(draft_url, video_infos)
- 解析video_infos JSON字符串
- 验证每个视频的参数配置
- 获取并解密草稿内容
- 创建视频轨道
- 循环处理每个视频:
- 创建视频素材
- 添加视频片段
- 应用遮罩效果(如果指定)
- 应用缩放和变换
- 设置透明度和音量
- 添加转场效果(如果指定)
- 加密并保存更新后的草稿
- 返回处理结果
技术特性
批量处理
- 多视频支持: 一次请求可处理多个视频
- 独立配置: 每个视频可以有独立的参数设置
- 统一变换: 全局变换参数应用于所有视频
高级效果
- 遮罩系统: 支持6种预定义遮罩类型
- 转场动画: 支持视频间的平滑过渡
- 音量控制: 独立的音量调节
- 透明度混合: 支持视频叠加效果
性能优化
- 参数验证: 前置验证避免无效处理
- 错误隔离: 单个视频失败不影响其他视频
- 内存管理: 优化大文件处理内存占用
最佳实践
视频组织建议
const videoOrganization = {
// 时间规划
timing: {
intro: { start: 0, duration: 3000000 }, // 3秒
main: { start: 3000000, duration: 10000000 }, // 10秒
outro: { start: 13000000, duration: 2000000 } // 2秒
},
// 分辨率统一
resolution: {
hd: { width: 1920, height: 1080 },
mobile: { width: 1080, height: 1920 },
square: { width: 1080, height: 1080 }
},
// 音量平衡
audioLevels: {
background: 0.3, // 背景视频
main: 0.8, // 主要内容
accent: 1.0 // 强调部分
}
};
性能优化
- 视频预处理: 在上传前优化视频文件大小和格式
- 批量大小控制: 单次请求建议不超过10个视频
- 时间安排: 合理安排视频时间,避免过度重叠
- 网络优化: 使用CDN加速视频文件访问
创意应用
- 视频拼接: 创建连续的视频序列
- 画中画效果: 实现多视角显示
- 分屏展示: 同时展示多个视频内容
- 过渡动画: 使用转场创建平滑切换
- 遮罩创意: 使用不同遮罩创建独特视觉效果
高级用法
复杂视频组合
// 创建电影级转场效果
const createCinematicTransition = (videos) => {
return videos.map((video, index) => ({
...video,
transition: index < videos.length - 1 ? "电影转场" : undefined,
transition_duration: 800000, // 0.8秒转场
volume: video.type === 'background' ? 0.4 : 0.9
}));
};
// 多层视频叠加
const createVideoLayers = (baseVideo, overlays) => {
const videoInfos = [baseVideo];
overlays.forEach((overlay, index) => {
videoInfos.push({
...overlay,
mask: overlay.mask || "圆形",
// 每层稍微错开位置
transform_x: (index + 1) * 50,
transform_y: (index + 1) * -30
});
});
return { video_infos: JSON.stringify(videoInfos) };
};
响应式视频布局
const createResponsiveLayout = (videos, canvasSize) => {
const isVertical = canvasSize.height > canvasSize.width;
return videos.map((video, index) => {
if (isVertical) {
// 竖屏布局:垂直堆叠
return {
...video,
transform_y: (index - videos.length / 2) * 200,
scale_x: 0.8,
scale_y: 0.8
};
} else {
// 横屏布局:水平排列
return {
...video,
transform_x: (index - videos.length / 2) * 300,
scale_x: 0.6,
scale_y: 0.6
};
}
});
};
相关接口
- CREATE_DRAFT - 创建新草稿
- ADD_AUDIOS - 批量添加音频
- ADD_IMAGES - 批量添加图片
- ADD_KEYFRAMES - 批量添加关键帧(可用于视频动画)
- ADD_MASKS - 批量添加遮罩(可与视频组合使用)
- ADD_STICKER - 添加贴纸装饰
更新日志
- v1.0.0 (2025-08-01): 初始版本发布
- 支持批量视频添加功能
- 实现遮罩、转场、音量控制
- 支持缩放和位置变换
- 支持透明度调整
- 完善的参数验证机制
- 多种视频组合效果支持
📖 文档版本: v1.0.0
🔄 最后更新: 2025-08-01
👤 维护者: Developer