
- 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>
233 lines
6.4 KiB
Python
233 lines
6.4 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
|
|
from sqlalchemy.orm import Session
|
|
from pydantic import BaseModel
|
|
from typing import Optional
|
|
from app.db.session import get_db
|
|
from app.models.user import User
|
|
from app.models.video import Video
|
|
from app.models.transcription import Transcription
|
|
from app.utils.auth import get_current_user
|
|
from app.services.transcription_service import transcribe_video_audio
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class TranscriptionResponse(BaseModel):
|
|
id: int
|
|
video_id: int
|
|
text: str
|
|
created_at: str
|
|
|
|
class Config:
|
|
orm_mode = True
|
|
|
|
|
|
class TranscriptionStartResponse(BaseModel):
|
|
message: str
|
|
video_id: int
|
|
|
|
|
|
class EditableTranscriptionResponse(BaseModel):
|
|
video_id: int
|
|
original_transcript: str
|
|
edited_transcript: Optional[str] = None
|
|
|
|
|
|
class EditTranscriptRequest(BaseModel):
|
|
edited_transcript: str
|
|
|
|
|
|
class EditTranscriptResponse(BaseModel):
|
|
message: str
|
|
video_id: int
|
|
|
|
|
|
async def background_transcribe(video_id: int, video_s3_url: str, db: Session):
|
|
try:
|
|
# Update video status
|
|
video = db.query(Video).filter(Video.id == video_id).first()
|
|
if video:
|
|
video.status = "transcribing"
|
|
db.commit()
|
|
|
|
# Transcribe the video
|
|
transcript_text = await transcribe_video_audio(video_s3_url)
|
|
|
|
if transcript_text:
|
|
# Save transcription to database
|
|
transcription = Transcription(
|
|
video_id=video_id,
|
|
text=transcript_text
|
|
)
|
|
db.add(transcription)
|
|
|
|
# Update video status
|
|
if video:
|
|
video.status = "transcribed"
|
|
|
|
db.commit()
|
|
else:
|
|
# Update video status to error
|
|
if video:
|
|
video.status = "transcription_failed"
|
|
db.commit()
|
|
|
|
except Exception:
|
|
# Update video status to error
|
|
video = db.query(Video).filter(Video.id == video_id).first()
|
|
if video:
|
|
video.status = "transcription_failed"
|
|
db.commit()
|
|
|
|
|
|
@router.post("/{video_id}", response_model=TranscriptionStartResponse)
|
|
async def start_transcription(
|
|
video_id: int,
|
|
background_tasks: BackgroundTasks,
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
# Check if video exists and belongs to user
|
|
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"
|
|
)
|
|
|
|
# Check if transcription already exists
|
|
existing_transcription = db.query(Transcription).filter(
|
|
Transcription.video_id == video_id
|
|
).first()
|
|
|
|
if existing_transcription:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Transcription already exists for this video"
|
|
)
|
|
|
|
# Start background transcription
|
|
background_tasks.add_task(background_transcribe, video_id, video.original_s3_url, db)
|
|
|
|
return TranscriptionStartResponse(
|
|
message="Transcription started in background",
|
|
video_id=video_id
|
|
)
|
|
|
|
|
|
@router.get("/{video_id}", response_model=TranscriptionResponse)
|
|
async def get_transcription(
|
|
video_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
# Check if video exists and belongs to user
|
|
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"
|
|
)
|
|
|
|
# Get transcription
|
|
transcription = db.query(Transcription).filter(
|
|
Transcription.video_id == video_id
|
|
).first()
|
|
|
|
if not transcription:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Transcription not found"
|
|
)
|
|
|
|
return TranscriptionResponse(
|
|
id=transcription.id,
|
|
video_id=transcription.video_id,
|
|
text=transcription.text,
|
|
created_at=str(transcription.created_at)
|
|
)
|
|
|
|
|
|
@router.get("/{video_id}/editable", response_model=EditableTranscriptionResponse)
|
|
async def get_editable_transcription(
|
|
video_id: int,
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
# Check if video exists and belongs to user
|
|
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"
|
|
)
|
|
|
|
# Get transcription
|
|
transcription = db.query(Transcription).filter(
|
|
Transcription.video_id == video_id
|
|
).first()
|
|
|
|
if not transcription:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Transcription not found"
|
|
)
|
|
|
|
return EditableTranscriptionResponse(
|
|
video_id=video_id,
|
|
original_transcript=transcription.text,
|
|
edited_transcript=transcription.edited_text
|
|
)
|
|
|
|
|
|
@router.put("/{video_id}/editable", response_model=EditTranscriptResponse)
|
|
async def update_editable_transcription(
|
|
video_id: int,
|
|
request: EditTranscriptRequest,
|
|
current_user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
# Check if video exists and belongs to user
|
|
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"
|
|
)
|
|
|
|
# Get transcription
|
|
transcription = db.query(Transcription).filter(
|
|
Transcription.video_id == video_id
|
|
).first()
|
|
|
|
if not transcription:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
detail="Transcription not found"
|
|
)
|
|
|
|
# Update edited transcript
|
|
transcription.edited_text = request.edited_transcript
|
|
transcription.is_edited = True
|
|
db.commit()
|
|
|
|
return EditTranscriptResponse(
|
|
message="Transcript updated successfully",
|
|
video_id=video_id
|
|
) |