Automated Action 55f5e5f5a8 Add comprehensive AI video dubbing features with Docker support
- Auto language detection using OpenAI Whisper during video upload
- Editable transcript interface for reviewing/correcting transcriptions
- Updated translation pipeline to use edited transcripts when available
- Migrated from JWT to Google OAuth-only authentication for better security
- Added complete Docker containerization with docker-compose.yml
- Updated database schema with language detection and transcript editing fields
- Enhanced API documentation and workflow in README
- Added comprehensive environment variable configuration

🤖 Generated with BackendIM

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-28 08:12:00 +00:00

175 lines
4.9 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, Optional
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
from app.services.language_detection_service import detect_language_from_video
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
source_language: Optional[str] = None
status: str
created_at: str
class Config:
orm_mode = True
class VideoUploadResponse(BaseModel):
video_id: int
message: str
s3_url: str
detected_language: Optional[str] = None
class LanguageDetectionResponse(BaseModel):
video_id: int
detected_language: Optional[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"
)
# Detect language from video
detected_language = await detect_language_from_video(file_content)
# 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,
source_language=detected_language,
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,
detected_language=detected_language
)
@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,
source_language=video.source_language,
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,
source_language=video.source_language,
status=video.status,
created_at=str(video.created_at)
)
@router.get("/{video_id}/language", response_model=LanguageDetectionResponse)
async def get_video_language(
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 LanguageDetectionResponse(
video_id=video.id,
detected_language=video.source_language
)