跳转到正文MaoMaoToken
控制台

视频保存与下载

任务完成后,从查询响应的 video_url 获取视频链接并下载。

获取视频链接

# 查询任务状态
curl https://api.maomaotoken.com/v1/videos/{task_id} \
  -H "Authorization: Bearer YOUR_API_KEY"

响应示例:

{
  "id": "task_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "status": "completed",
  "progress": 100,
  "video_url": "https://cdn.example.com/videos/output.mp4",
  "completed_at": 1791000042
}

下载视频

使用 cURL

curl -L 'https://cdn.example.com/videos/output.mp4' \
  -o my_video.mp4

-L 选项用于跟随重定向。

使用 wget

wget 'https://cdn.example.com/videos/output.mp4' \
  -O my_video.mp4

使用 Python

import requests

video_url = "https://cdn.example.com/videos/output.mp4"

response = requests.get(video_url, stream=True)
response.raise_for_status()

with open('my_video.mp4', 'wb') as f:
    for chunk in response.iter_content(chunk_size=8192):
        if chunk:
            f.write(chunk)

print("下载完成!")

使用 Node.js

const https = require('https');
const fs = require('fs');

const videoUrl = "https://cdn.example.com/videos/output.mp4";
const outputPath = "./my_video.mp4";

https.get(videoUrl, (response) => {
  if (response.statusCode !== 200) {
    throw new Error(`Failed to download: ${response.statusCode}`);
  }
  
  const file = fs.createWriteStream(outputPath);
  response.pipe(file);
  
  file.on('finish', () => {
    file.close();
    console.log('下载完成!');
  });
  
  file.on('error', (err) => {
    fs.unlink(outputPath, () => {}); // 清理失败的文件
    throw err;
  });
});

下载前的准备

1. 确认任务已完成

// 确保 status 为 completed
const task = await queryTask(taskId);
if (task.status !== 'completed') {
  throw new Error(`任务未完成,当前状态: ${task.status}`);
}

2. 重新查询获取最新链接

// 不要使用缓存的旧链接,重新查询获取最新的 video_url
const latestTask = await fetch(
  `https://api.maomaotoken.com/v1/videos/${taskId}`,
  { headers: { 'Authorization': `Bearer ${apiKey}` } }
).then(r => r.json());

const videoUrl = latestTask.video_url;

下载验证

检查 HTTP 状态码

# 检查响应是否为 200 OK
curl -I 'https://cdn.example.com/videos/output.mp4'

# 输出示例
# HTTP/1.1 200 OK
# Content-Type: video/mp4
# Content-Length: 25000000

验证 Content-Type

# 确保返回的是视频文件而非 HTML
curl -I 'https://cdn.example.com/videos/output.mp4' | grep Content-Type

# ✓ 正确
# Content-Type: video/mp4

# ✗ 错误(说明链接已失效或返回了错误页面)
# Content-Type: text/html

验证文件大小

# 检查下载的文件大小是否合理
ls -lh my_video.mp4

# 查看服务器返回的大小
curl -I 'https://cdn.example.com/videos/output.mp4' | grep Content-Length

计算文件校验和

# macOS / Linux
md5sum my_video.mp4

# 与服务器返回的校验和对比(如支持)

下载失败处理

1. 重新查询任务

// 如果下载失败,重新查询任务确认状态仍为 completed
const task = await queryTask(taskId);

if (task.status === 'completed') {
  const freshUrl = task.video_url; // 获取新的 URL
  // 使用新 URL 重试下载
} else if (task.status === 'failed') {
  throw new Error(`任务失败: ${task.error.message}`);
} else {
  throw new Error(`任务未完成: ${task.status}`);
}

2. 检查链接有效性

# 检查链接是否过期或拒绝访问
curl -v 'https://cdn.example.com/videos/output.mp4' 2>&1 | head -20

# ✗ 常见错误响应
# < HTTP/1.1 404 Not Found      (链接已过期)
# < HTTP/1.1 403 Forbidden      (无权限访问)
# < HTTP/1.1 500 Server Error   (服务器异常)

3. 验证认证信息

某些配置可能需要在下载时携带认证:

# 尝试添加认证头
curl -L 'https://cdn.example.com/videos/output.mp4' \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -o my_video.mp4

4. 重试策略

const downloadWithRetry = async (url, maxRetries = 3) => {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url);
      
      if (response.status === 200) {
        return response.arrayBuffer();
      }
      
      if (response.status === 404 || response.status === 403) {
        throw new Error(`无法访问: ${response.status}`);
      }
      
      // 5xx 错误可重试
      console.warn(`服务器错误 ${response.status},${i + 1} 秒后重试...`);
      await new Promise(r => setTimeout(r, (i + 1) * 1000));
      
    } catch (error) {
      if (i === maxRetries - 1) throw error;
      console.warn(`下载失败,${i + 1} 秒后重试...`);
      await new Promise(r => setTimeout(r, (i + 1) * 1000));
    }
  }
};

保存建议

1. 使用对象存储

不要依赖 API 返回的链接作为永久存储,应保存到自己的对象存储:

// 下载后保存到阿里云 OSS
const OSS = require('ali-oss');

const client = new OSS({
  region: 'oss-cn-hangzhou',
  accessKeyId: YOUR_KEY_ID,
  accessKeySecret: YOUR_KEY_SECRET,
  bucket: 'my-bucket'
});

const videoBuffer = await downloadWithRetry(videoUrl);
const result = await client.putObject(
  `videos/${taskId}.mp4`,
  Buffer.from(videoBuffer)
);

console.log('已保存到 OSS:', result.url);

2. 保存元数据

// 记录任务信息以便追查
const metadata = {
  taskId: task.id,
  model: task.model,
  createdAt: new Date(task.created_at * 1000),
  completedAt: new Date(task.completed_at * 1000),
  videoUrl: task.video_url,
  localPath: '/path/to/my_video.mp4',
  fileSize: fs.statSync('/path/to/my_video.mp4').size,
  savedAt: new Date().toISOString()
};

// 保存到数据库或文件
saveToDatabase(metadata);

3. 定期清理

# 清理超过 30 天未使用的本地副本
find ./videos -type f -mtime +30 -delete

链接有效期

  • ⏰ 生成的视频链接有有效期
  • 📅 具体时长以线上服务策略为准
  • 💾 不要将链接当作永久地址
  • ✅ 建议任务完成后立即下载并保存到自己的存储

常见问题

本页内容

On this page