
- 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>
175 lines
4.9 KiB
Python
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
|
|
) |