Automated Action 92e4d992b2 Implement complete AI video dubbing backend with FastAPI
Features:
- JWT authentication with user registration and login
- Video upload to Amazon S3 with file validation (200MB limit)
- Audio transcription using OpenAI Whisper API
- Text translation using GPT-4 API
- Voice cloning and audio synthesis using ElevenLabs API
- Video processing with ffmpeg for audio replacement
- Complete SQLite database with proper models and migrations
- Background task processing for long-running operations
- Health endpoint and comprehensive API documentation

Tech stack:
- FastAPI with SQLAlchemy ORM
- SQLite database with Alembic migrations
- Amazon S3 for file storage
- OpenAI APIs for transcription and translation
- ElevenLabs API for voice cloning
- ffmpeg for video processing
- JWT authentication with bcrypt password hashing
2025-06-24 17:56:12 +00:00

137 lines
3.8 KiB
Python

import uuid
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form, BackgroundTasks
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import List
from app.db.session import get_db
from app.models.user import User
from app.models.video import Video
from app.utils.auth import get_current_user
from app.services.s3_service import upload_file_to_s3
router = APIRouter()
ALLOWED_VIDEO_TYPES = ["video/mp4", "video/quicktime"]
MAX_FILE_SIZE = 200 * 1024 * 1024 # 200MB
class VideoResponse(BaseModel):
id: int
user_id: int
original_s3_url: str
language_from: str
language_to: str
status: str
created_at: str
class Config:
orm_mode = True
class VideoUploadResponse(BaseModel):
video_id: int
message: str
s3_url: str
@router.post("/upload", response_model=VideoUploadResponse)
async def upload_video(
background_tasks: BackgroundTasks,
video: UploadFile = File(...),
language_from: str = Form(...),
language_to: str = Form(...),
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
# Validate file type
if video.content_type not in ALLOWED_VIDEO_TYPES:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid file type. Only MP4 and MOV files are allowed."
)
# Read file content
file_content = await video.read()
# Validate file size
if len(file_content) > MAX_FILE_SIZE:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="File too large. Maximum size is 200MB."
)
# Generate unique filename
file_extension = video.filename.split('.')[-1] if video.filename else 'mp4'
unique_filename = f"videos/{uuid.uuid4()}.{file_extension}"
# Upload to S3
s3_url = await upload_file_to_s3(file_content, unique_filename, video.content_type)
if not s3_url:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to upload video to S3"
)
# Save video metadata to database
db_video = Video(
user_id=current_user.id,
original_s3_url=s3_url,
language_from=language_from,
language_to=language_to,
status="uploaded"
)
db.add(db_video)
db.commit()
db.refresh(db_video)
return VideoUploadResponse(
video_id=db_video.id,
message="Video uploaded successfully",
s3_url=s3_url
)
@router.get("/", response_model=List[VideoResponse])
async def get_user_videos(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
videos = db.query(Video).filter(Video.user_id == current_user.id).all()
return [VideoResponse(
id=video.id,
user_id=video.user_id,
original_s3_url=video.original_s3_url,
language_from=video.language_from,
language_to=video.language_to,
status=video.status,
created_at=str(video.created_at)
) for video in videos]
@router.get("/{video_id}", response_model=VideoResponse)
async def get_video(
video_id: int,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
video = db.query(Video).filter(
Video.id == video_id,
Video.user_id == current_user.id
).first()
if not video:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Video not found"
)
return VideoResponse(
id=video.id,
user_id=video.user_id,
original_s3_url=video.original_s3_url,
language_from=video.language_from,
language_to=video.language_to,
status=video.status,
created_at=str(video.created_at)
)