GEN_VIDEO_STATUS 接口 - 查询视频渲染状态

📄 API_GEN_VIDEO_STATUS.md 🕒 8/10/2025, 6:55:40 PM 📏 17KB

GEN_VIDEO_STATUS 接口 - 查询视频渲染状态

📋 接口信息

接口地址: POST /api/drafts/gen_video_status
功能描述: 查询视频草稿的渲染状态,获取渲染进度和结果信息
版本: v1.0.0
更新时间: 2025年8月1日

🎯 功能特性

核心功能

高级特性

📝 请求参数

请求体 (JSON)

{
  "draft_url": "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json"
}

参数详解

参数名 类型 必填 说明
draft_url string 草稿文件的完整URL地址

参数说明

draft_url 支持格式

# OSS直链格式(推荐)
https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json

# 剪映链接格式(自动转换)
https://ts.fyshark.com/#/cozeToJianyin?drafId=https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/xxx.json

📤 响应格式

成功响应 - 渲染中 (200)

{
  "status": "success",
  "message": "视频状态查询成功",
  "data": {
    "code": 0,
    "message": "查询成功",
    "status": "rendering",
    "progress": 65,
    "estimated_time": 180,
    "start_time": "2025-08-01T10:30:00Z",
    "task_id": "render_task_12345678",
    "draft_url": "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json"
  }
}

成功响应 - 渲染完成 (200)

{
  "status": "success",
  "message": "视频状态查询成功",
  "data": {
    "code": 0,
    "message": "渲染完成",
    "status": "completed",
    "progress": 100,
    "video_url": "https://video-output.example.com/videos/final_video_12345.mp4",
    "file_size": 52428800,
    "duration": 120,
    "resolution": "1920x1080",
    "start_time": "2025-08-01T10:30:00Z",
    "end_time": "2025-08-01T10:35:00Z",
    "render_time": 300,
    "task_id": "render_task_12345678",
    "draft_url": "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json"
  }
}

成功响应 - 渲染失败 (200)

{
  "status": "success",
  "message": "视频状态查询成功",
  "data": {
    "code": 0,
    "message": "渲染失败",
    "status": "failed",
    "error_code": "INVALID_MEDIA",
    "error_message": "素材文件无法访问或格式不支持",
    "start_time": "2025-08-01T10:30:00Z",
    "end_time": "2025-08-01T10:32:00Z",
    "task_id": "render_task_12345678",
    "draft_url": "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json"
  }
}

错误响应 (400)

{
  "status": "error",
  "message": "参数验证失败",
  "errors": [
    "draft_url是必填项"
  ]
}

错误响应 (404)

{
  "status": "error",
  "message": "未找到渲染任务",
  "data": {
    "code": 404,
    "message": "Draft not found or no render task exists"
  }
}

错误响应 (500)

{
  "status": "error",
  "message": "状态查询失败: 网络连接超时"
}

🔧 使用示例

cURL 示例

基本查询

curl -X POST https://jy-api.fyshark.com/api/drafts/gen_video_status \
  -H "Content-Type: application/json" \
  -d '{
    "draft_url": "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json"
  }'

使用剪映链接格式

curl -X POST https://jy-api.fyshark.com/api/drafts/gen_video_status \
  -H "Content-Type: application/json" \
  -d '{
    "draft_url": "https://ts.fyshark.com/#/cozeToJianyin?drafId=https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json"
  }'

JavaScript 示例

基础使用

const queryVideoStatus = async (draftUrl) => {
  const response = await fetch('/api/drafts/gen_video_status', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      draft_url: draftUrl
    })
  });
  
  const result = await response.json();
  
  if (result.status === 'success') {
    return result.data;
  } else {
    throw new Error(result.message);
  }
};

// 使用示例
const draftUrl = "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json";

queryVideoStatus(draftUrl)
  .then(status => {
    console.log('渲染状态:', status.status);
    console.log('进度:', status.progress + '%');
    if (status.video_url) {
      console.log('视频地址:', status.video_url);
    }
  })
  .catch(error => {
    console.error('查询失败:', error.message);
  });

高级用法 - VideoStatusTracker 类

class VideoStatusTracker {
  constructor(baseUrl = 'https://jy-api.fyshark.com') {
    this.baseUrl = baseUrl;
  }

  // 查询单个视频状态
  async queryStatus(draftUrl) {
    const response = await fetch(`${this.baseUrl}/api/drafts/gen_video_status`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ draft_url: draftUrl })
    });

    const result = await response.json();
    if (result.status !== 'success') {
      throw new Error(`状态查询失败: ${result.message}`);
    }

    return result.data;
  }

  // 轮询查询直到完成
  async waitForCompletion(draftUrl, options = {}) {
    const {
      interval = 5000, // 轮询间隔 5秒
      timeout = 600000, // 超时时间 10分钟
      onProgress = null // 进度回调
    } = options;

    const startTime = Date.now();
    
    while (Date.now() - startTime < timeout) {
      try {
        const status = await this.queryStatus(draftUrl);
        
        // 调用进度回调
        if (onProgress) {
          onProgress(status);
        }
        
        // 检查完成状态
        if (status.status === 'completed') {
          return {
            success: true,
            video_url: status.video_url,
            duration: status.duration,
            file_size: status.file_size,
            render_time: status.render_time
          };
        }
        
        // 检查失败状态
        if (status.status === 'failed') {
          return {
            success: false,
            error_code: status.error_code,
            error_message: status.error_message
          };
        }
        
        // 等待下次查询
        await new Promise(resolve => setTimeout(resolve, interval));
        
      } catch (error) {
        console.warn('状态查询失败,重试中...', error.message);
        await new Promise(resolve => setTimeout(resolve, interval));
      }
    }
    
    throw new Error('等待渲染完成超时');
  }

  // 批量查询多个视频状态
  async batchQuery(draftUrls) {
    const results = [];
    
    for (const draftUrl of draftUrls) {
      try {
        const status = await this.queryStatus(draftUrl);
        results.push({
          success: true,
          draft_url: draftUrl,
          status: status
        });
      } catch (error) {
        results.push({
          success: false,
          draft_url: draftUrl,
          error: error.message
        });
      }
    }
    
    return results;
  }

  // 获取渲染统计信息
  async getRenderStats(draftUrls) {
    const results = await this.batchQuery(draftUrls);
    
    const stats = {
      total: results.length,
      completed: 0,
      rendering: 0,
      failed: 0,
      queued: 0,
      unknown: 0
    };
    
    results.forEach(result => {
      if (result.success) {
        const status = result.status.status;
        if (stats.hasOwnProperty(status)) {
          stats[status]++;
        } else {
          stats.unknown++;
        }
      } else {
        stats.unknown++;
      }
    });
    
    return stats;
  }
}

// 使用示例
const tracker = new VideoStatusTracker();

// 单次查询
const status = await tracker.queryStatus(draftUrl);
console.log('当前状态:', status);

// 等待完成
const result = await tracker.waitForCompletion(draftUrl, {
  onProgress: (status) => {
    console.log(`渲染进度: ${status.progress}%`);
  }
});

if (result.success) {
  console.log('渲染完成!', result.video_url);
} else {
  console.log('渲染失败:', result.error_message);
}

// 批量查询
const draftUrls = [
  "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/draft1.json",
  "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/draft2.json"
];

const batchResults = await tracker.batchQuery(draftUrls);
console.log('批量查询结果:', batchResults);

// 获取统计信息
const stats = await tracker.getRenderStats(draftUrls);
console.log('渲染统计:', stats);

Python 示例

import requests
import time
import json

class VideoStatusTracker:
    def __init__(self, base_url="https://jy-api.fyshark.com"):
        self.base_url = base_url
    
    def query_status(self, draft_url):
        """查询视频渲染状态"""
        
        url = f"{self.base_url}/api/drafts/gen_video_status"
        headers = {"Content-Type": "application/json"}
        data = {"draft_url": draft_url}
        
        response = requests.post(url, headers=headers, json=data)
        result = response.json()
        
        if result["status"] == "success":
            return result["data"]
        else:
            raise Exception(f"状态查询失败: {result['message']}")
    
    def wait_for_completion(self, draft_url, interval=5, timeout=600, on_progress=None):
        """轮询查询直到渲染完成"""
        
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            try:
                status = self.query_status(draft_url)
                
                # 调用进度回调
                if on_progress:
                    on_progress(status)
                
                # 检查完成状态
                if status["status"] == "completed":
                    return {
                        "success": True,
                        "video_url": status["video_url"],
                        "duration": status.get("duration"),
                        "file_size": status.get("file_size"),
                        "render_time": status.get("render_time")
                    }
                
                # 检查失败状态
                if status["status"] == "failed":
                    return {
                        "success": False,
                        "error_code": status.get("error_code"),
                        "error_message": status.get("error_message")
                    }
                
                # 等待下次查询
                time.sleep(interval)
                
            except Exception as e:
                print(f"状态查询失败,重试中... {e}")
                time.sleep(interval)
        
        raise Exception("等待渲染完成超时")
    
    def batch_query(self, draft_urls):
        """批量查询多个视频状态"""
        results = []
        
        for draft_url in draft_urls:
            try:
                status = self.query_status(draft_url)
                results.append({
                    "success": True,
                    "draft_url": draft_url,
                    "status": status
                })
            except Exception as e:
                results.append({
                    "success": False,
                    "draft_url": draft_url,
                    "error": str(e)
                })
        
        return results
    
    def get_render_stats(self, draft_urls):
        """获取渲染统计信息"""
        results = self.batch_query(draft_urls)
        
        stats = {
            "total": len(results),
            "completed": 0,
            "rendering": 0,
            "failed": 0,
            "queued": 0,
            "unknown": 0
        }
        
        for result in results:
            if result["success"]:
                status = result["status"]["status"]
                if status in stats:
                    stats[status] += 1
                else:
                    stats["unknown"] += 1
            else:
                stats["unknown"] += 1
        
        return stats

# 使用示例
tracker = VideoStatusTracker()

# 单次查询
draft_url = "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/0b3de2f7-e263-4cfb-b811-79f337a3843c.json"

try:
    status = tracker.query_status(draft_url)
    print("当前状态:")
    print(f"  状态: {status['status']}")
    print(f"  进度: {status.get('progress', 0)}%")
    if status.get('video_url'):
        print(f"  视频地址: {status['video_url']}")
except Exception as e:
    print(f"查询失败: {e}")

# 等待完成
try:
    def progress_callback(status):
        print(f"渲染进度: {status.get('progress', 0)}%")
    
    result = tracker.wait_for_completion(
        draft_url,
        on_progress=progress_callback
    )
    
    if result["success"]:
        print(f"渲染完成! 视频地址: {result['video_url']}")
    else:
        print(f"渲染失败: {result['error_message']}")
        
except Exception as e:
    print(f"等待完成失败: {e}")

# 批量查询
draft_urls = [
    "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/draft1.json",
    "https://video-snot-12220.oss-cn-shanghai.aliyuncs.com/draft/draft2.json"
]

batch_results = tracker.batch_query(draft_urls)
for result in batch_results:
    if result["success"]:
        print(f"✅ {result['draft_url']} - {result['status']['status']}")
    else:
        print(f"❌ {result['draft_url']} - {result['error']}")

# 获取统计信息
stats = tracker.get_render_stats(draft_urls)
print("渲染统计:", json.dumps(stats, indent=2))

📊 状态说明

渲染状态类型

状态 说明 progress 操作建议
queued 排队中 0 等待处理,可继续查询
preparing 准备中 1-10 正在准备渲染资源
rendering 渲染中 10-99 正在渲染,可定期查询进度
completed 已完成 100 渲染成功,可获取视频链接
failed 渲染失败 - 检查错误信息,修复后重新提交
cancelled 已取消 - 任务被用户或系统取消

错误码说明

错误码 说明 解决方案
INVALID_DRAFT 草稿文件无效 检查草稿文件格式和内容
INVALID_MEDIA 素材文件无效 确认所有素材文件可访问
INSUFFICIENT_QUOTA 配额不足 充值或联系管理员
RENDER_TIMEOUT 渲染超时 简化草稿内容或重新提交
SYSTEM_ERROR 系统错误 稍后重试或联系技术支持

⚠️ 注意事项

查询限制

  1. 查询频率: 建议查询间隔不少于5秒,避免过频查询
  2. 超时处理: 长时间未完成的任务可能需要重新提交
  3. 缓存时间: 状态信息可能有1-2秒的缓存延迟
  4. 并发限制: 同时查询的任务数量建议不超过50个

使用建议

  1. 轮询策略: 采用指数退避算法避免频繁查询
  2. 错误重试: 网络错误时应进行适当重试
  3. 状态存储: 建议本地存储任务状态减少查询次数
  4. 用户体验: 为用户提供清晰的进度和状态反馈

数据时效性

  1. 完成状态: 视频链接通常保存30天
  2. 失败信息: 错误信息保存7天后清理
  3. 历史记录: 建议及时下载完成的视频文件
  4. 状态变更: 状态变更通知可能有延迟

🎨 应用场景

用户界面集成

自动化流程

系统集成

🔄 工作流程

基本查询流程

1. 准备草稿URL
   ↓
2. 调用状态查询接口
   ↓
3. 解析返回的状态信息
   ↓
4. 根据状态决定后续操作

轮询等待流程

查询状态 → 检查状态 → 是否完成?
    ↑                       ↓
    ←——— 等待间隔 ←——— 否
                           ↓
                          是
                           ↓
                    获取结果/处理错误

✨ 最佳实践

智能轮询

// ✅ 推荐:智能轮询策略
const smartPolling = async (draftUrl) => {
  let interval = 5000; // 起始间隔5秒
  let attempts = 0;
  const maxInterval = 30000; // 最大间隔30秒
  
  while (attempts < 120) { // 最多查询2小时
    try {
      const status = await queryVideoStatus(draftUrl);
      
      if (status.status === 'completed' || status.status === 'failed') {
        return status;
      }
      
      // 动态调整查询间隔
      if (status.status === 'rendering' && status.progress > 50) {
        interval = 10000; // 渲染后期减少查询频率
      } else if (status.status === 'queued') {
        interval = Math.min(interval * 1.2, maxInterval); // 排队时逐步增加间隔
      }
      
      await new Promise(resolve => setTimeout(resolve, interval));
      attempts++;
      
    } catch (error) {
      console.warn('查询失败,重试...', error.message);
      await new Promise(resolve => setTimeout(resolve, interval));
      attempts++;
    }
  }
  
  throw new Error('查询超时');
};

批量监控

// ✅ 推荐:批量任务监控
const monitorBatchRender = async (draftUrls) => {
  const results = new Map();
  
  const checkStatus = async () => {
    const pendingUrls = draftUrls.filter(url => {
      const result = results.get(url);
      return !result || (result.status !== 'completed' && result.status !== 'failed');
    });
    
    if (pendingUrls.length === 0) {
      return true; // 全部完成
    }
    
    for (const draftUrl of pendingUrls) {
      try {
        const status = await queryVideoStatus(draftUrl);
        results.set(draftUrl, status);
        
        console.log(`${draftUrl}: ${status.status} ${status.progress || 0}%`);
      } catch (error) {
        console.warn(`查询失败 ${draftUrl}:`, error.message);
      }
    }
    
    return false;
  };
  
  // 定期检查
  while (!(await checkStatus())) {
    await new Promise(resolve => setTimeout(resolve, 10000));
  }
  
  return Array.from(results.entries()).map(([url, status]) => ({
    draft_url: url,
    status: status
  }));
};

📖 版本历史


接口开发: JY API Team
文档更新: 2025年8月1日
技术支持: GitHub Issues
相关接口: gen_video - 视频渲染接口