mirror of
https://github.com/TheSmallHanCat/sora2api.git
synced 2026-02-04 02:04:42 +08:00
feat: 普号增加25s普通视频、增加pro系列高清模型、统一模型名字、过载不再计入错误禁用计数
This commit is contained in:
92
README.md
92
README.md
@@ -110,14 +110,15 @@ python main.py
|
||||
|
||||
| 功能 | 模型 | 说明 |
|
||||
|------|------|------|
|
||||
| 文生图 | `sora-image*` | 使用 `content` 为字符串 |
|
||||
| 图生图 | `sora-image*` | 使用 `content` 数组 + `image_url` |
|
||||
| 文生视频 | `sora-video*` | 使用 `content` 为字符串 |
|
||||
| 图生视频 | `sora-video*` | 使用 `content` 数组 + `image_url` |
|
||||
| 创建角色 | `sora-video*` | 使用 `content` 数组 + `video_url` |
|
||||
| 角色生成视频 | `sora-video*` | 使用 `content` 数组 + `video_url` + 文本 |
|
||||
| Remix | `sora-video*` | 在 `content` 中包含 Remix ID |
|
||||
| 视频分镜 | `sora-video*` | 在 `content` 中使用```[时长s]提示词```格式触发 |
|
||||
| 文生图 | `gpt-image*` | 使用 `content` 为字符串 |
|
||||
| 图生图 | `gpt-image*` | 使用 `content` 数组 + `image_url` |
|
||||
| 文生视频 | `sora2-*` | 使用 `content` 为字符串 |
|
||||
| 图生视频 | `sora2-*` | 使用 `content` 数组 + `image_url` |
|
||||
| 视频风格 | `sora2-*` | 在提示词中使用 `{风格ID}` 格式,如 `{anime}提示词` |
|
||||
| 创建角色 | `sora2-*` | 使用 `content` 数组 + `video_url` |
|
||||
| 角色生成视频 | `sora2-*` | 使用 `content` 数组 + `video_url` + 文本 |
|
||||
| Remix | `sora2-*` | 在 `content` 中包含 Remix ID |
|
||||
| 视频分镜 | `sora2-*` | 在 `content` 中使用```[时长s]提示词```格式触发 |
|
||||
|
||||
---
|
||||
|
||||
@@ -135,20 +136,44 @@ python main.py
|
||||
|
||||
| 模型 | 说明 | 尺寸 |
|
||||
|------|------|------|
|
||||
| `sora-image` | 文生图(默认) | 360×360 |
|
||||
| `sora-image-landscape` | 文生图(横屏) | 540×360 |
|
||||
| `sora-image-portrait` | 文生图(竖屏) | 360×540 |
|
||||
| `gpt-image` | 文生图(正方形) | 360×360 |
|
||||
| `gpt-image-landscape` | 文生图(横屏) | 540×360 |
|
||||
| `gpt-image-portrait` | 文生图(竖屏) | 360×540 |
|
||||
|
||||
**视频模型**
|
||||
|
||||
**标准版(Sora2)**
|
||||
|
||||
| 模型 | 时长 | 方向 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `sora-video-10s` | 10秒 | 横屏 | 文生视频/图生视频 |
|
||||
| `sora-video-15s` | 15秒 | 横屏 | 文生视频/图生视频 |
|
||||
| `sora-video-landscape-10s` | 10秒 | 横屏 | 文生视频/图生视频 |
|
||||
| `sora-video-landscape-15s` | 15秒 | 横屏 | 文生视频/图生视频 |
|
||||
| `sora-video-portrait-10s` | 10秒 | 竖屏 | 文生视频/图生视频 |
|
||||
| `sora-video-portrait-15s` | 15秒 | 竖屏 | 文生视频/图生视频 |
|
||||
| `sora2-landscape-10s` | 10秒 | 横屏 | 文生视频/图生视频 |
|
||||
| `sora2-landscape-15s` | 15秒 | 横屏 | 文生视频/图生视频 |
|
||||
| `sora2-landscape-25s` | 25秒 | 横屏 | 文生视频/图生视频 |
|
||||
| `sora2-portrait-10s` | 10秒 | 竖屏 | 文生视频/图生视频 |
|
||||
| `sora2-portrait-15s` | 15秒 | 竖屏 | 文生视频/图生视频 |
|
||||
| `sora2-portrait-25s` | 25秒 | 竖屏 | 文生视频/图生视频 |
|
||||
|
||||
**Pro 版(需要 ChatGPT Pro 订阅)**
|
||||
|
||||
| 模型 | 时长 | 方向 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `sora2pro-landscape-10s` | 10秒 | 横屏 | Pro 质量文生视频/图生视频 |
|
||||
| `sora2pro-landscape-15s` | 15秒 | 横屏 | Pro 质量文生视频/图生视频 |
|
||||
| `sora2pro-landscape-25s` | 25秒 | 横屏 | Pro 质量文生视频/图生视频 |
|
||||
| `sora2pro-portrait-10s` | 10秒 | 竖屏 | Pro 质量文生视频/图生视频 |
|
||||
| `sora2pro-portrait-15s` | 15秒 | 竖屏 | Pro 质量文生视频/图生视频 |
|
||||
| `sora2pro-portrait-25s` | 25秒 | 竖屏 | Pro 质量文生视频/图生视频 |
|
||||
|
||||
**Pro HD 版(需要 ChatGPT Pro 订阅,高清质量)**
|
||||
|
||||
| 模型 | 时长 | 方向 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `sora2pro-hd-landscape-10s` | 10秒 | 横屏 | Pro 高清文生视频/图生视频 |
|
||||
| `sora2pro-hd-landscape-15s` | 15秒 | 横屏 | Pro 高清文生视频/图生视频 |
|
||||
| `sora2pro-hd-portrait-10s` | 10秒 | 竖屏 | Pro 高清文生视频/图生视频 |
|
||||
| `sora2pro-hd-portrait-15s` | 15秒 | 竖屏 | Pro 高清文生视频/图生视频 |
|
||||
|
||||
> **注意:** Pro 系列模型需要 ChatGPT Pro 订阅(`plan_type: "chatgpt_pro"`)。如果没有 Pro 账号,请求这些模型会返回错误。
|
||||
|
||||
#### 请求示例
|
||||
|
||||
@@ -159,13 +184,14 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-image",
|
||||
"model": "gpt-image",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "一只可爱的小猫咪"
|
||||
}
|
||||
]
|
||||
],
|
||||
"stream": true
|
||||
}'
|
||||
```
|
||||
|
||||
@@ -176,7 +202,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-image",
|
||||
"model": "gpt-image",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -205,7 +231,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -223,7 +249,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -254,13 +280,14 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "https://sora.chatgpt.com/p/s_68e3a06dcd888191b150971da152c1f5改成水墨画风格"
|
||||
}
|
||||
]
|
||||
],
|
||||
"stream": true
|
||||
}'
|
||||
```
|
||||
|
||||
@@ -280,13 +307,14 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": "[5.0s]猫猫从飞机上跳伞 [5.0s]猫猫降落 [10.0s]猫猫在田野奔跑"
|
||||
}
|
||||
]
|
||||
],
|
||||
"stream": true
|
||||
}'
|
||||
```
|
||||
|
||||
@@ -322,7 +350,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -340,7 +368,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -358,7 +386,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -394,7 +422,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -421,7 +449,7 @@ curl -X POST "http://localhost:8000/v1/chat/completions" \
|
||||
-H "Authorization: Bearer han1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@@ -461,7 +489,7 @@ response = requests.post(
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
json={
|
||||
"model": "sora-video-landscape-10s",
|
||||
"model": "sora2-landscape-10s",
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
|
||||
@@ -27,7 +27,7 @@ base_url = "http://127.0.0.1:8000"
|
||||
|
||||
[generation]
|
||||
image_timeout = 300
|
||||
video_timeout = 1500
|
||||
video_timeout = 3000
|
||||
|
||||
[admin]
|
||||
error_ban_threshold = 3
|
||||
|
||||
@@ -27,7 +27,7 @@ base_url = "http://127.0.0.1:8000"
|
||||
|
||||
[generation]
|
||||
image_timeout = 300
|
||||
video_timeout = 1500
|
||||
video_timeout = 3000
|
||||
|
||||
[admin]
|
||||
error_ban_threshold = 3
|
||||
|
||||
@@ -163,7 +163,7 @@ class Config:
|
||||
@property
|
||||
def video_timeout(self) -> int:
|
||||
"""Get video generation timeout in seconds"""
|
||||
return self._config.get("generation", {}).get("video_timeout", 1500)
|
||||
return self._config.get("generation", {}).get("video_timeout", 3000)
|
||||
|
||||
def set_video_timeout(self, timeout: int):
|
||||
"""Set video generation timeout in seconds"""
|
||||
|
||||
@@ -144,12 +144,12 @@ class Database:
|
||||
if count[0] == 0:
|
||||
# Get generation config from config_dict if provided, otherwise use defaults
|
||||
image_timeout = 300
|
||||
video_timeout = 1500
|
||||
video_timeout = 3000
|
||||
|
||||
if config_dict:
|
||||
generation_config = config_dict.get("generation", {})
|
||||
image_timeout = generation_config.get("image_timeout", 300)
|
||||
video_timeout = generation_config.get("video_timeout", 1500)
|
||||
video_timeout = generation_config.get("video_timeout", 3000)
|
||||
|
||||
await db.execute("""
|
||||
INSERT INTO generation_config (id, image_timeout, video_timeout)
|
||||
@@ -401,7 +401,7 @@ class Database:
|
||||
CREATE TABLE IF NOT EXISTS generation_config (
|
||||
id INTEGER PRIMARY KEY DEFAULT 1,
|
||||
image_timeout INTEGER DEFAULT 300,
|
||||
video_timeout INTEGER DEFAULT 1500,
|
||||
video_timeout INTEGER DEFAULT 3000,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
@@ -1013,7 +1013,7 @@ class Database:
|
||||
return GenerationConfig(**dict(row))
|
||||
# If no row exists, return a default config
|
||||
# This should not happen in normal operation as _ensure_config_rows should create it
|
||||
return GenerationConfig(image_timeout=300, video_timeout=1500)
|
||||
return GenerationConfig(image_timeout=300, video_timeout=3000)
|
||||
|
||||
async def update_generation_config(self, image_timeout: int = None, video_timeout: int = None):
|
||||
"""Update generation configuration"""
|
||||
@@ -1027,10 +1027,10 @@ class Database:
|
||||
current = dict(row)
|
||||
# Update only provided fields
|
||||
new_image_timeout = image_timeout if image_timeout is not None else current.get("image_timeout", 300)
|
||||
new_video_timeout = video_timeout if video_timeout is not None else current.get("video_timeout", 1500)
|
||||
new_video_timeout = video_timeout if video_timeout is not None else current.get("video_timeout", 3000)
|
||||
else:
|
||||
new_image_timeout = image_timeout if image_timeout is not None else 300
|
||||
new_video_timeout = video_timeout if video_timeout is not None else 1500
|
||||
new_video_timeout = video_timeout if video_timeout is not None else 3000
|
||||
|
||||
await db.execute("""
|
||||
UPDATE generation_config
|
||||
|
||||
@@ -19,52 +19,139 @@ from ..core.logger import debug_logger
|
||||
|
||||
# Model configuration
|
||||
MODEL_CONFIG = {
|
||||
"sora-image": {
|
||||
"gpt-image": {
|
||||
"type": "image",
|
||||
"width": 360,
|
||||
"height": 360
|
||||
},
|
||||
"sora-image-landscape": {
|
||||
"gpt-image-landscape": {
|
||||
"type": "image",
|
||||
"width": 540,
|
||||
"height": 360
|
||||
},
|
||||
"sora-image-portrait": {
|
||||
"gpt-image-portrait": {
|
||||
"type": "image",
|
||||
"width": 360,
|
||||
"height": 540
|
||||
},
|
||||
# Video models with 10s duration (300 frames)
|
||||
"sora-video-10s": {
|
||||
"sora2-landscape-10s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 300
|
||||
},
|
||||
"sora-video-landscape-10s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 300
|
||||
},
|
||||
"sora-video-portrait-10s": {
|
||||
"sora2-portrait-10s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 300
|
||||
},
|
||||
# Video models with 15s duration (450 frames)
|
||||
"sora-video-15s": {
|
||||
"sora2-landscape-15s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 450
|
||||
},
|
||||
"sora-video-landscape-15s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 450
|
||||
},
|
||||
"sora-video-portrait-15s": {
|
||||
"sora2-portrait-15s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 450
|
||||
},
|
||||
# Video models with 25s duration (750 frames)
|
||||
"sora2-landscape-25s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 750,
|
||||
"model": "sy_8",
|
||||
"size": "small"
|
||||
},
|
||||
"sora2-portrait-25s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 750,
|
||||
"model": "sy_8",
|
||||
"size": "small"
|
||||
},
|
||||
# Pro video models (require Pro subscription)
|
||||
"sora2pro-landscape-10s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 300,
|
||||
"model": "sy_ore",
|
||||
"size": "small",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-portrait-10s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 300,
|
||||
"model": "sy_ore",
|
||||
"size": "small",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-landscape-15s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 450,
|
||||
"model": "sy_ore",
|
||||
"size": "small",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-portrait-15s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 450,
|
||||
"model": "sy_ore",
|
||||
"size": "small",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-landscape-25s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 750,
|
||||
"model": "sy_ore",
|
||||
"size": "small",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-portrait-25s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 750,
|
||||
"model": "sy_ore",
|
||||
"size": "small",
|
||||
"require_pro": True
|
||||
},
|
||||
# Pro HD video models (require Pro subscription, high quality)
|
||||
"sora2pro-hd-landscape-10s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 300,
|
||||
"model": "sy_ore",
|
||||
"size": "large",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-hd-portrait-10s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 300,
|
||||
"model": "sy_ore",
|
||||
"size": "large",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-hd-landscape-15s": {
|
||||
"type": "video",
|
||||
"orientation": "landscape",
|
||||
"n_frames": 450,
|
||||
"model": "sy_ore",
|
||||
"size": "large",
|
||||
"require_pro": True
|
||||
},
|
||||
"sora2pro-hd-portrait-15s": {
|
||||
"type": "video",
|
||||
"orientation": "portrait",
|
||||
"n_frames": 450,
|
||||
"model": "sy_ore",
|
||||
"size": "large",
|
||||
"require_pro": True
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,10 +381,20 @@ class GenerationHandler:
|
||||
return
|
||||
|
||||
# Streaming mode: proceed with actual generation
|
||||
# Check if model requires Pro subscription
|
||||
require_pro = model_config.get("require_pro", False)
|
||||
|
||||
# Select token (with lock for image generation, Sora2 quota check for video generation)
|
||||
token_obj = await self.load_balancer.select_token(for_image_generation=is_image, for_video_generation=is_video)
|
||||
# If Pro is required, filter for Pro tokens only
|
||||
token_obj = await self.load_balancer.select_token(
|
||||
for_image_generation=is_image,
|
||||
for_video_generation=is_video,
|
||||
require_pro=require_pro
|
||||
)
|
||||
if not token_obj:
|
||||
if is_image:
|
||||
if require_pro:
|
||||
raise Exception("No available Pro tokens. Pro models require a ChatGPT Pro subscription.")
|
||||
elif is_image:
|
||||
raise Exception("No available tokens for image generation. All tokens are either disabled, cooling down, locked, or expired.")
|
||||
else:
|
||||
raise Exception("No available tokens for video generation. All tokens are either disabled, cooling down, Sora2 quota exhausted, don't support Sora2, or expired.")
|
||||
@@ -383,12 +480,18 @@ class GenerationHandler:
|
||||
)
|
||||
else:
|
||||
# Normal video generation
|
||||
# Get model and size from config (default to sy_8 and small for backward compatibility)
|
||||
sora_model = model_config.get("model", "sy_8")
|
||||
video_size = model_config.get("size", "small")
|
||||
|
||||
task_id = await self.sora_client.generate_video(
|
||||
clean_prompt, token_obj.token,
|
||||
orientation=model_config["orientation"],
|
||||
media_id=media_id,
|
||||
n_frames=n_frames,
|
||||
style_id=style_id
|
||||
style_id=style_id,
|
||||
model=sora_model,
|
||||
size=video_size
|
||||
)
|
||||
else:
|
||||
task_id = await self.sora_client.generate_image(
|
||||
@@ -1271,10 +1374,16 @@ class GenerationHandler:
|
||||
# Get n_frames from model configuration
|
||||
n_frames = model_config.get("n_frames", 300) # Default to 300 frames (10s)
|
||||
|
||||
# Get model and size from config (default to sy_8 and small for backward compatibility)
|
||||
sora_model = model_config.get("model", "sy_8")
|
||||
video_size = model_config.get("size", "small")
|
||||
|
||||
task_id = await self.sora_client.generate_video(
|
||||
full_prompt, token_obj.token,
|
||||
orientation=model_config["orientation"],
|
||||
n_frames=n_frames
|
||||
n_frames=n_frames,
|
||||
model=sora_model,
|
||||
size=video_size
|
||||
)
|
||||
debug_logger.log_info(f"Video generation started, task_id: {task_id}")
|
||||
|
||||
@@ -1282,7 +1391,7 @@ class GenerationHandler:
|
||||
task = Task(
|
||||
task_id=task_id,
|
||||
token_id=token_obj.id,
|
||||
model=f"sora-video-{model_config['orientation']}",
|
||||
model=f"sora2-video-{model_config['orientation']}",
|
||||
prompt=full_prompt,
|
||||
status="processing",
|
||||
progress=0.0
|
||||
@@ -1375,7 +1484,7 @@ class GenerationHandler:
|
||||
task = Task(
|
||||
task_id=task_id,
|
||||
token_id=token_obj.id,
|
||||
model=f"sora-video-{model_config['orientation']}",
|
||||
model=f"sora2-video-{model_config['orientation']}",
|
||||
prompt=f"remix:{remix_target_id} {clean_prompt}",
|
||||
status="processing",
|
||||
progress=0.0
|
||||
|
||||
@@ -17,13 +17,14 @@ class LoadBalancer:
|
||||
# Use image timeout from config as lock timeout
|
||||
self.token_lock = TokenLock(lock_timeout=config.image_timeout)
|
||||
|
||||
async def select_token(self, for_image_generation: bool = False, for_video_generation: bool = False) -> Optional[Token]:
|
||||
async def select_token(self, for_image_generation: bool = False, for_video_generation: bool = False, require_pro: bool = False) -> Optional[Token]:
|
||||
"""
|
||||
Select a token using random load balancing
|
||||
|
||||
Args:
|
||||
for_image_generation: If True, only select tokens that are not locked for image generation and have image_enabled=True
|
||||
for_video_generation: If True, filter out tokens with Sora2 quota exhausted (sora2_cooldown_until not expired), tokens that don't support Sora2, and tokens with video_enabled=False
|
||||
require_pro: If True, only select tokens with ChatGPT Pro subscription (plan_type="chatgpt_pro")
|
||||
|
||||
Returns:
|
||||
Selected token or None if no available tokens
|
||||
@@ -56,6 +57,13 @@ class LoadBalancer:
|
||||
if not active_tokens:
|
||||
return None
|
||||
|
||||
# Filter for Pro tokens if required
|
||||
if require_pro:
|
||||
pro_tokens = [token for token in active_tokens if token.plan_type == "chatgpt_pro"]
|
||||
if not pro_tokens:
|
||||
return None
|
||||
active_tokens = pro_tokens
|
||||
|
||||
# If for video generation, filter out tokens with Sora2 quota exhausted and tokens without Sora2 support
|
||||
if for_video_generation:
|
||||
from datetime import datetime
|
||||
|
||||
@@ -254,8 +254,20 @@ class SoraClient:
|
||||
return result["id"]
|
||||
|
||||
async def generate_video(self, prompt: str, token: str, orientation: str = "landscape",
|
||||
media_id: Optional[str] = None, n_frames: int = 450, style_id: Optional[str] = None) -> str:
|
||||
"""Generate video (text-to-video or image-to-video)"""
|
||||
media_id: Optional[str] = None, n_frames: int = 450, style_id: Optional[str] = None,
|
||||
model: str = "sy_8", size: str = "small") -> str:
|
||||
"""Generate video (text-to-video or image-to-video)
|
||||
|
||||
Args:
|
||||
prompt: Video generation prompt
|
||||
token: Access token
|
||||
orientation: Video orientation (landscape/portrait)
|
||||
media_id: Optional image media_id for image-to-video
|
||||
n_frames: Number of frames (300/450/750)
|
||||
style_id: Optional style ID
|
||||
model: Model to use (sy_8 for standard, sy_ore for pro)
|
||||
size: Video size (small for standard, large for HD)
|
||||
"""
|
||||
inpaint_items = []
|
||||
if media_id:
|
||||
inpaint_items = [{
|
||||
@@ -267,9 +279,9 @@ class SoraClient:
|
||||
"kind": "video",
|
||||
"prompt": prompt,
|
||||
"orientation": orientation,
|
||||
"size": "small",
|
||||
"size": size,
|
||||
"n_frames": n_frames,
|
||||
"model": "sy_8",
|
||||
"model": model,
|
||||
"inpaint_items": inpaint_items,
|
||||
"style_id": style_id
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user