Added video studio router and endpoints. Added research router and endpoints. Added youtube router and endpoints. Added onboarding utils router and endpoints. Added onboarding utils service. Added onboarding utils models. Added onboarding utils routes. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils. Added onboarding utils utils.
This commit is contained in:
304
backend/routers/video_studio/endpoints/create.py
Normal file
304
backend/routers/video_studio/endpoints/create.py
Normal file
@@ -0,0 +1,304 @@
|
||||
"""
|
||||
Create video endpoints: text-to-video and image-to-video generation.
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, BackgroundTasks
|
||||
from sqlalchemy.orm import Session
|
||||
from typing import Optional, Dict, Any
|
||||
import uuid
|
||||
|
||||
from ...database import get_db
|
||||
from ...models.content_asset_models import AssetSource, AssetType
|
||||
from ...services.video_studio import VideoStudioService
|
||||
from ...services.asset_service import ContentAssetService
|
||||
from ...utils.auth import get_current_user, require_authenticated_user
|
||||
from ...utils.logger_utils import get_service_logger
|
||||
from api.story_writer.task_manager import task_manager
|
||||
from ..tasks.video_generation import execute_video_generation_task
|
||||
|
||||
logger = get_service_logger("video_studio.endpoints.create")
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/generate")
|
||||
async def generate_video(
|
||||
background_tasks: BackgroundTasks,
|
||||
prompt: str = Form(..., description="Text description for video generation"),
|
||||
negative_prompt: Optional[str] = Form(None, description="What to avoid in the video"),
|
||||
duration: int = Form(5, description="Video duration in seconds", ge=1, le=10),
|
||||
resolution: str = Form("720p", description="Video resolution"),
|
||||
aspect_ratio: str = Form("16:9", description="Video aspect ratio"),
|
||||
motion_preset: str = Form("medium", description="Motion intensity"),
|
||||
provider: str = Form("wavespeed", description="AI provider to use"),
|
||||
model: str = Form("hunyuan-video-1.5", description="Specific AI model to use"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate video from text description using AI models.
|
||||
|
||||
Supports multiple providers and models for optimal quality and cost.
|
||||
"""
|
||||
try:
|
||||
user_id = require_authenticated_user(current_user)
|
||||
|
||||
# Initialize services
|
||||
video_service = VideoStudioService()
|
||||
asset_service = ContentAssetService(db)
|
||||
|
||||
logger.info(f"[VideoStudio] Text-to-video request: user={user_id}, model={model}, duration={duration}s")
|
||||
|
||||
# Generate video
|
||||
result = await video_service.generate_text_to_video(
|
||||
prompt=prompt,
|
||||
negative_prompt=negative_prompt,
|
||||
duration=duration,
|
||||
resolution=resolution,
|
||||
aspect_ratio=aspect_ratio,
|
||||
motion_preset=motion_preset,
|
||||
provider=provider,
|
||||
model=model,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
if not result.get("success"):
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Video generation failed: {result.get('error', 'Unknown error')}"
|
||||
)
|
||||
|
||||
# Store in asset library if successful
|
||||
video_url = result.get("video_url")
|
||||
if video_url:
|
||||
asset_metadata = {
|
||||
"prompt": prompt,
|
||||
"negative_prompt": negative_prompt,
|
||||
"duration": duration,
|
||||
"resolution": resolution,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
"motion_preset": motion_preset,
|
||||
"provider": provider,
|
||||
"model": model,
|
||||
"generation_type": "text-to-video",
|
||||
}
|
||||
|
||||
asset_service.create_asset(
|
||||
user_id=user_id,
|
||||
filename=f"video_{uuid.uuid4().hex[:8]}.mp4",
|
||||
file_url=video_url,
|
||||
asset_type=AssetType.VIDEO,
|
||||
source_module=AssetSource.VIDEO_STUDIO,
|
||||
asset_metadata=asset_metadata,
|
||||
cost=result.get("cost", 0),
|
||||
tags=["video_studio", "text-to-video", "ai-generated"]
|
||||
)
|
||||
|
||||
logger.info(f"[VideoStudio] Video generated successfully: user={user_id}, url={video_url}")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"video_url": video_url,
|
||||
"cost": result.get("cost", 0),
|
||||
"estimated_duration": result.get("estimated_duration", duration),
|
||||
"model_used": model,
|
||||
"provider": provider,
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[VideoStudio] Text-to-video error: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"Video generation failed: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/transform")
|
||||
async def transform_to_video(
|
||||
background_tasks: BackgroundTasks,
|
||||
file: UploadFile = File(..., description="Image file to transform"),
|
||||
prompt: Optional[str] = Form(None, description="Optional text prompt to guide transformation"),
|
||||
duration: int = Form(5, description="Video duration in seconds", ge=1, le=10),
|
||||
resolution: str = Form("720p", description="Video resolution"),
|
||||
aspect_ratio: str = Form("16:9", description="Video aspect ratio"),
|
||||
motion_preset: str = Form("medium", description="Motion intensity"),
|
||||
provider: str = Form("wavespeed", description="AI provider to use"),
|
||||
model: str = Form("alibaba/wan-2.5", description="Specific AI model to use"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Transform image to video using AI models.
|
||||
|
||||
Supports various motion presets and durations for dynamic video creation.
|
||||
"""
|
||||
try:
|
||||
user_id = require_authenticated_user(current_user)
|
||||
|
||||
# Validate file type
|
||||
if not file.content_type.startswith('image/'):
|
||||
raise HTTPException(status_code=400, detail="File must be an image")
|
||||
|
||||
# Initialize services
|
||||
video_service = VideoStudioService()
|
||||
asset_service = ContentAssetService(db)
|
||||
|
||||
logger.info(f"[VideoStudio] Image-to-video request: user={user_id}, model={model}, duration={duration}s")
|
||||
|
||||
# Read image file
|
||||
image_data = await file.read()
|
||||
|
||||
# Generate video
|
||||
result = await video_service.generate_image_to_video(
|
||||
image_data=image_data,
|
||||
prompt=prompt,
|
||||
duration=duration,
|
||||
resolution=resolution,
|
||||
aspect_ratio=aspect_ratio,
|
||||
motion_preset=motion_preset,
|
||||
provider=provider,
|
||||
model=model,
|
||||
user_id=user_id,
|
||||
)
|
||||
|
||||
if not result.get("success"):
|
||||
raise HTTPException(
|
||||
status_code=500,
|
||||
detail=f"Video transformation failed: {result.get('error', 'Unknown error')}"
|
||||
)
|
||||
|
||||
# Store in asset library if successful
|
||||
video_url = result.get("video_url")
|
||||
if video_url:
|
||||
asset_metadata = {
|
||||
"original_image": file.filename,
|
||||
"prompt": prompt,
|
||||
"duration": duration,
|
||||
"resolution": resolution,
|
||||
"aspect_ratio": aspect_ratio,
|
||||
"motion_preset": motion_preset,
|
||||
"provider": provider,
|
||||
"model": model,
|
||||
"generation_type": "image-to-video",
|
||||
}
|
||||
|
||||
asset_service.create_asset(
|
||||
user_id=user_id,
|
||||
filename=f"video_{uuid.uuid4().hex[:8]}.mp4",
|
||||
file_url=video_url,
|
||||
asset_type=AssetType.VIDEO,
|
||||
source_module=AssetSource.VIDEO_STUDIO,
|
||||
asset_metadata=asset_metadata,
|
||||
cost=result.get("cost", 0),
|
||||
tags=["video_studio", "image-to-video", "ai-generated"]
|
||||
)
|
||||
|
||||
logger.info(f"[VideoStudio] Video transformation successful: user={user_id}, url={video_url}")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"video_url": video_url,
|
||||
"cost": result.get("cost", 0),
|
||||
"estimated_duration": result.get("estimated_duration", duration),
|
||||
"model_used": model,
|
||||
"provider": provider,
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[VideoStudio] Image-to-video error: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"Video transformation failed: {str(e)}")
|
||||
|
||||
|
||||
@router.post("/generate-async")
|
||||
async def generate_video_async(
|
||||
background_tasks: BackgroundTasks,
|
||||
prompt: Optional[str] = Form(None, description="Text description for video generation"),
|
||||
image: Optional[UploadFile] = File(None, description="Image file for image-to-video"),
|
||||
operation_type: str = Form("text-to-video", description="Operation type: text-to-video or image-to-video"),
|
||||
negative_prompt: Optional[str] = Form(None, description="What to avoid in the video"),
|
||||
duration: int = Form(5, description="Video duration in seconds", ge=1, le=10),
|
||||
resolution: str = Form("720p", description="Video resolution"),
|
||||
aspect_ratio: str = Form("16:9", description="Video aspect ratio"),
|
||||
motion_preset: str = Form("medium", description="Motion intensity"),
|
||||
provider: str = Form("wavespeed", description="AI provider to use"),
|
||||
model: str = Form("alibaba/wan-2.5", description="Specific AI model to use"),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Generate video asynchronously with polling support.
|
||||
|
||||
Returns task_id for polling. Frontend can poll /api/video-studio/task/{task_id}/status
|
||||
to get progress updates and final result.
|
||||
"""
|
||||
try:
|
||||
user_id = require_authenticated_user(current_user)
|
||||
|
||||
# Validate operation type
|
||||
if operation_type not in ["text-to-video", "image-to-video"]:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=f"Invalid operation_type: {operation_type}. Must be 'text-to-video' or 'image-to-video'"
|
||||
)
|
||||
|
||||
# Validate inputs based on operation type
|
||||
if operation_type == "text-to-video" and not prompt:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="prompt is required for text-to-video generation"
|
||||
)
|
||||
|
||||
if operation_type == "image-to-video" and not image:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="image file is required for image-to-video generation"
|
||||
)
|
||||
|
||||
# Read image data if provided
|
||||
image_data = None
|
||||
if image:
|
||||
image_data = await image.read()
|
||||
if len(image_data) == 0:
|
||||
raise HTTPException(status_code=400, detail="Image file is empty")
|
||||
|
||||
# Create task
|
||||
task_id = task_manager.create_task("video_generation")
|
||||
|
||||
# Prepare kwargs
|
||||
kwargs = {
|
||||
"duration": duration,
|
||||
"resolution": resolution,
|
||||
"model": model,
|
||||
}
|
||||
if negative_prompt:
|
||||
kwargs["negative_prompt"] = negative_prompt
|
||||
if aspect_ratio:
|
||||
kwargs["aspect_ratio"] = aspect_ratio
|
||||
if motion_preset:
|
||||
kwargs["motion_preset"] = motion_preset
|
||||
|
||||
# Start background task
|
||||
background_tasks.add_task(
|
||||
execute_video_generation_task,
|
||||
task_id=task_id,
|
||||
operation_type=operation_type,
|
||||
user_id=user_id,
|
||||
prompt=prompt,
|
||||
image_data=image_data,
|
||||
provider=provider,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
logger.info(f"[VideoStudio] Started async video generation: task_id={task_id}, operation={operation_type}, user={user_id}")
|
||||
|
||||
return {
|
||||
"task_id": task_id,
|
||||
"status": "pending",
|
||||
"message": f"Video generation started. This may take several minutes. Poll /api/video-studio/task/{task_id}/status for updates."
|
||||
}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"[VideoStudio] Failed to start async video generation: {e}", exc_info=True)
|
||||
raise HTTPException(status_code=500, detail=f"Failed to start video generation: {str(e)}")
|
||||
Reference in New Issue
Block a user