This commit is contained in:
jueming
2025-06-04 19:49:25 +08:00
parent 2e9b844f96
commit 2348948409
3 changed files with 137 additions and 28 deletions
+41 -2
View File
@@ -156,9 +156,48 @@ When using T2V-01-Director or I2V-01-Director models, you can use movement instr
3. Select an I2V model (I2V-01-Director recommended)
4. Enter API key and optionally write a prompt with movement instructions
5. Run to get a task_id
6. Use **Check Video Status** node to monitor progress
7. Once status is "success", use **Download Video** node to save the video
6. Use **Check Video Status** node to monitor progress (it will automatically wait until completion)
7. Once status is "success", use **Download Video** node with the file_id to save the video
## License
MIT License
### Check Video Status Node
This node checks the status of video generation tasks and waits until completion.
#### Input Parameters:
- **api_key**: MiniMax API key
- **task_id**: Task ID from Video Generation node
- **check_interval**: Check interval in seconds (default: 30, range: 10-300)
- **max_wait_time**: Maximum wait time in seconds (default: 1800/30 minutes, range: 5 minutes-2 hours)
#### Output:
- **status**: Task status (processing, success, failed)
- **file_id**: File ID of the generated video (required for downloading)
- **video_url**: Download URL for the generated video (may be empty)
- **cover_image_url**: URL for the video cover image
#### Features:
- **Automatic polling**: Continuously checks status until completion or failure
- **Progress tracking**: Shows elapsed time, remaining time, and attempt count
- **Timeout protection**: Prevents infinite waiting with configurable maximum wait time
- **Smart intervals**: Customizable check intervals to balance responsiveness and API usage
### Download Video Node
This node downloads generated videos using the file_id from the status check.
#### Input Parameters:
- **api_key**: MiniMax API key
- **file_id**: File ID from Check Video Status node
- **filename_prefix**: Prefix for the downloaded video file
#### Output:
- **video_path**: Absolute path to the downloaded video file
#### Process:
1. Uses file_id to retrieve download URL from MiniMax file API
2. Downloads the video file with progress tracking
3. Saves to ComfyUI output directory with timestamp
+21 -7
View File
@@ -56,29 +56,43 @@
### 检查视频状态节点
此节点检查视频生成任务的状态。
此节点检查视频生成任务的状态并等待直到完成。
#### 输入参数:
- **api_key**: MiniMax API 密钥
- **task_id**: 来自视频生成节点的任务 ID
- **check_interval**: 检查间隔(秒)(默认:30,范围:10-300)
- **max_wait_time**: 最大等待时间(秒)(默认:1800/30分钟,范围:5分钟-2小时)
#### 输出:
- **status**: 任务状态(processing, success, failed)
- **file_id**: 生成视频的文件 ID
- **video_url**: 生成视频的下载 URL
- **file_id**: 生成视频的文件 ID(下载时必需)
- **video_url**: 生成视频的下载 URL(可能为空)
- **cover_image_url**: 视频封面图像的 URL
#### 功能特性:
- **自动轮询**: 持续检查状态直到完成或失败
- **进度跟踪**: 显示已用时间、剩余时间和尝试次数
- **超时保护**: 通过可配置的最大等待时间防止无限等待
- **智能间隔**: 可自定义检查间隔,平衡响应性和 API 使用
### 下载视频节点
此节点将生成的视频从 URL 下载到本地存储。
此节点使用状态检查的 file_id 下载生成的视频。
#### 输入参数:
- **video_url**: 来自检查视频状态节点的视频 URL
- **api_key**: MiniMax API 密钥
- **file_id**: 来自检查视频状态节点的文件 ID
- **filename_prefix**: 下载视频文件的前缀
#### 输出:
- **video_path**: 下载视频文件的绝对路径
#### 处理流程:
1. 使用 file_id 从 MiniMax 文件 API 获取下载 URL
2. 带进度跟踪的视频文件下载
3. 保存到 ComfyUI 输出目录并添加时间戳
## 视频工作流示例
### 文生视频工作流:
@@ -94,8 +108,8 @@
3. 选择 I2V 模型(推荐 I2V-01-Director)
4. 输入 API 密钥,可选择性地编写带有运镜指令的提示词
5. 运行以获取 task_id
6. 使用**检查视频状态**节点监控进度
7. 状态为 "success" 后,使用**下载视频**节点保存视频
6. 使用**检查视频状态**节点监控进度(将自动等待直到完成)
7. 状态为 "success" 后,使用**下载视频**节点和 file_id 保存视频
## 安装
+75 -19
View File
@@ -1,21 +1,23 @@
import os
import requests
import time
import json
import folder_paths
from urllib.parse import urlparse
class DownloadVideo:
"""
Download Video from URL node for ComfyUI
Download Video using file_id from MiniMax API
"""
def __init__(self):
pass
self.retrieve_api = "https://api.minimaxi.chat/v1/files/retrieve"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"video_url": ("STRING", {"multiline": False, "placeholder": "Video URL from status check"}),
"api_key": ("STRING", {"multiline": False}),
"file_id": ("STRING", {"multiline": False, "placeholder": "File ID from video status check"}),
"filename_prefix": ("STRING", {"default": "minimax_video", "multiline": False}),
}
}
@@ -25,19 +27,69 @@ class DownloadVideo:
FUNCTION = "download_video"
CATEGORY = "JM-MiniMax-API/Video"
def download_video(self, video_url, filename_prefix):
if not video_url or not video_url.strip():
raise ValueError("Video URL must be provided")
def download_video(self, api_key, file_id, filename_prefix):
if not api_key or not file_id:
raise ValueError("API Key and File ID must be provided")
video_url = video_url.strip()
file_id = file_id.strip()
try:
# Validate URL
parsed_url = urlparse(video_url)
if not parsed_url.scheme or not parsed_url.netloc:
raise ValueError("Invalid video URL provided")
print(f"Step 1: Retrieving download URL for file_id: {file_id}")
print(f"Downloading video from: {video_url}")
# Step 1: Get download URL using file_id
retrieve_url = f"{self.retrieve_api}?file_id={file_id}"
headers = {
'authorization': f'Bearer {api_key}',
}
print(f"Retrieve URL: {retrieve_url}")
response = requests.get(retrieve_url, headers=headers, timeout=30)
print(f"Retrieve response status code: {response.status_code}")
if response.status_code != 200:
raise RuntimeError(f"Failed to retrieve file info. Status code: {response.status_code}")
try:
retrieve_data = response.json()
print(f"Retrieve response data: {json.dumps(retrieve_data, indent=2)}")
except json.JSONDecodeError:
print(f"Raw retrieve response content: {response.content}")
raise RuntimeError("Failed to decode JSON response from file retrieve API")
# Check for API errors
base_resp = retrieve_data.get("base_resp", {})
status_code = base_resp.get("status_code")
status_msg = base_resp.get("status_msg", "Unknown error")
if status_code is not None and status_code != 0:
error_messages = {
1002: "Rate limit exceeded, please try again later",
1004: "Authentication failed, please check your API key",
1008: "Insufficient account balance",
2013: "Invalid parameters, please check your file_id",
2049: "Invalid API key, please check your API key"
}
error_msg = error_messages.get(status_code, f"API Error {status_code}: {status_msg}")
raise RuntimeError(error_msg)
# Extract download URL
file_info = retrieve_data.get("file", {})
download_url = file_info.get("download_url", "")
if not download_url:
raise RuntimeError("No download URL found in API response")
print(f"Download URL retrieved: {download_url}")
# Step 2: Download the video file
print(f"Step 2: Downloading video from URL")
# Validate download URL
parsed_url = urlparse(download_url)
if not parsed_url.scheme or not parsed_url.netloc:
raise ValueError("Invalid download URL received from API")
# Create output directory
output_dir = folder_paths.get_output_directory()
@@ -66,11 +118,13 @@ class DownloadVideo:
video_filepath = os.path.join(output_dir, video_filename)
# Download video with progress tracking
with requests.get(video_url, stream=True, timeout=60) as response:
response.raise_for_status()
print(f"Downloading to: {video_filepath}")
with requests.get(download_url, stream=True, timeout=120) as download_response:
download_response.raise_for_status()
# Get content length for progress tracking
content_length = response.headers.get('content-length')
content_length = download_response.headers.get('content-length')
if content_length:
total_size = int(content_length)
print(f"Video file size: {total_size / (1024*1024):.1f} MB")
@@ -80,7 +134,7 @@ class DownloadVideo:
downloaded_size = 0
with open(video_filepath, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
for chunk in download_response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
downloaded_size += len(chunk)
@@ -90,13 +144,15 @@ class DownloadVideo:
progress = (downloaded_size / total_size) * 100
print(f"Download progress: {progress:.1f}%")
print(f"Video downloaded successfully to: {video_filepath}")
print(f"Final file size: {os.path.getsize(video_filepath) / (1024*1024):.1f} MB")
final_size = os.path.getsize(video_filepath)
print(f"Video downloaded successfully!")
print(f"File path: {video_filepath}")
print(f"Final file size: {final_size / (1024*1024):.1f} MB")
return (os.path.abspath(video_filepath),)
except requests.exceptions.RequestException as e:
print(f"Download error: {str(e)}")
print(f"Request error: {str(e)}")
raise RuntimeError(f"Failed to download video: {str(e)}")
except Exception as e:
print(f"Unexpected error: {str(e)}")