ADD_VIDEOS API 接口文档

📄 API_ADD_VIDEOS.md 🕒 8/10/2025, 6:55:14 PM 📏 20KB

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)

参数详解

时间参数

透明度参数

缩放参数

位置参数

遮罩类型

支持的遮罩类型:

转场效果

音量控制

响应格式

成功响应 (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 视频处理失败 内部处理错误 联系技术支持

注意事项

  1. JSON格式: video_infos必须是合法的JSON字符串
  2. 时间单位: 所有时间参数使用微秒(1秒 = 1,000,000微秒)
  3. 视频格式: 确保视频文件格式被支持(如MP4、AVI等)
  4. 文件大小: 大视频文件可能影响处理速度
  5. 网络访问: 视频URL必须可以正常访问
  6. 遮罩限制: 只支持预定义的遮罩类型
  7. 转场限制: 转场时长有固定范围限制
  8. 性能考虑: 批量添加大量视频可能影响性能

工作流程

  1. 验证必填参数(draft_url, video_infos)
  2. 解析video_infos JSON字符串
  3. 验证每个视频的参数配置
  4. 获取并解密草稿内容
  5. 创建视频轨道
  6. 循环处理每个视频:
    • 创建视频素材
    • 添加视频片段
    • 应用遮罩效果(如果指定)
    • 应用缩放和变换
    • 设置透明度和音量
    • 添加转场效果(如果指定)
  7. 加密并保存更新后的草稿
  8. 返回处理结果

技术特性

批量处理

高级效果

性能优化

最佳实践

视频组织建议

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       // 强调部分
  }
};

性能优化

  1. 视频预处理: 在上传前优化视频文件大小和格式
  2. 批量大小控制: 单次请求建议不超过10个视频
  3. 时间安排: 合理安排视频时间,避免过度重叠
  4. 网络优化: 使用CDN加速视频文件访问

创意应用

  1. 视频拼接: 创建连续的视频序列
  2. 画中画效果: 实现多视角显示
  3. 分屏展示: 同时展示多个视频内容
  4. 过渡动画: 使用转场创建平滑切换
  5. 遮罩创意: 使用不同遮罩创建独特视觉效果

高级用法

复杂视频组合

// 创建电影级转场效果
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
      };
    }
  });
};

相关接口

更新日志


📖 文档版本: v1.0.0
🔄 最后更新: 2025-08-01
👤 维护者: Developer