feat: 普号增加25s普通视频、增加pro系列高清模型、统一模型名字、过载不再计入错误禁用计数

This commit is contained in:
TheSmallHanCat
2025-12-24 19:39:28 +08:00
parent 2c2fd44b6a
commit fad83533bf
8 changed files with 226 additions and 69 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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
}