matsuap's picture
Upload folder using huggingface_hub
839da96 verified
import logging
from fastapi import APIRouter, Depends, HTTPException, BackgroundTasks
from sqlalchemy.orm import Session
from typing import List
from datetime import datetime
from api.auth import get_current_user
from models import db_models
from models.schemas import VideoSummaryGenerateRequest, VideoSummaryResponse
from core.database import get_db, SessionLocal
from api.websocket_routes import manager
from services.video_generator_service import video_generator_service
from services.slides_video_service import slides_video_service
from services.s3_service import s3_service
router = APIRouter(prefix="/api/videos", tags=["video-generator"])
logger = logging.getLogger(__name__)
async def run_video_generation(summary_id: int, request: VideoSummaryGenerateRequest, user_id: int):
"""Background task for video summary generation"""
logger.info(f"Starting background video generation for ID: {summary_id}")
db = SessionLocal()
connection_id = f"user_{user_id}"
try:
db_summary = db.query(db_models.VideoSummary).filter(db_models.VideoSummary.id == summary_id).first()
if not db_summary:
logger.error(f"Video summary {summary_id} not found in database")
return
if request.use_slides_transformation:
logger.info(f"Task {summary_id}: Using slides transformation pipeline")
result = await slides_video_service.generate_transformed_video_summary(
file_key=request.file_key,
language=request.language,
voice_name=request.voice_name,
custom_prompt=request.custom_prompt
)
else:
logger.info(f"Task {summary_id}: Using standard video pipeline")
result = await video_generator_service.generate_video_summary(
file_key=request.file_key,
language=request.language,
voice_name=request.voice_name
)
if not db_summary.title or "Video Summary " not in db_summary.title:
db_summary.title = result["title"]
db_summary.s3_key = result["s3_key"]
db_summary.s3_url = result["s3_url"]
db_summary.status = "completed"
db.commit()
logger.info(f"Task {summary_id}: Successfully completed")
# Notify via WebSocket
await manager.send_result(connection_id, {
"type": "video",
"id": db_summary.id,
"status": "completed",
"title": db_summary.title
})
except Exception as e:
logger.error(f"Task {summary_id}: Background video generation failed: {e}")
db_summary = db.query(db_models.VideoSummary).filter(db_models.VideoSummary.id == summary_id).first()
if db_summary:
db_summary.status = "failed"
db_summary.error_message = "video generation failed"
db.commit()
await manager.send_error(connection_id, f"Video generation failed: {str(e)}")
finally:
db.close()
@router.post("/generate", response_model=VideoSummaryResponse)
async def generate_video_summary(
request: VideoSummaryGenerateRequest,
background_tasks: BackgroundTasks,
current_user: db_models.User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Initiates video summary generation in the background.
"""
# Check source ownership
source = db.query(db_models.Source).filter(
db_models.Source.s3_key == request.file_key,
db_models.Source.user_id == current_user.id
).first()
if not source:
raise HTTPException(status_code=403, detail="Not authorized to access this file")
# Create initial processing record
file_base = request.file_key.split('/')[-1].rsplit('.', 1)[0] if request.file_key else None
title = f"Video Summary {file_base}"
db_summary = db_models.VideoSummary(
title=title,
user_id=current_user.id,
source_id=source.id,
status="processing"
)
db.add(db_summary)
db.commit()
db.refresh(db_summary)
# Offload to background task
background_tasks.add_task(run_video_generation, db_summary.id, request, current_user.id)
return db_summary
@router.get("/list", response_model=List[VideoSummaryResponse])
async def list_video_summaries(
current_user: db_models.User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Lists all generated video summaries for the current user.
"""
try:
summaries = db.query(db_models.VideoSummary).filter(
db_models.VideoSummary.user_id == current_user.id
).order_by(db_models.VideoSummary.created_at.desc()).all()
return [VideoSummaryResponse.model_validate(s) for s in summaries]
except Exception as e:
logger.error(f"Failed to list video summaries: {e}")
raise HTTPException(status_code=500, detail=str(e))
@router.get("/{video_id}", response_model=VideoSummaryResponse)
async def get_video_summary(
video_id: int,
current_user: db_models.User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Retrieves a specific video summary.
"""
summary = db.query(db_models.VideoSummary).filter(
db_models.VideoSummary.id == video_id,
db_models.VideoSummary.user_id == current_user.id
).first()
if not summary:
raise HTTPException(status_code=404, detail="Video summary not found")
return VideoSummaryResponse.model_validate(summary)
@router.delete("/{video_id}")
async def delete_video_summary(
video_id: int,
current_user: db_models.User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Deletes a specific video summary from database and S3.
"""
summary = db.query(db_models.VideoSummary).filter(
db_models.VideoSummary.id == video_id,
db_models.VideoSummary.user_id == current_user.id
).first()
if not summary:
raise HTTPException(status_code=404, detail="Video summary not found")
try:
# 1. Delete from S3 if it exists
if summary.s3_key:
await s3_service.delete_file(summary.s3_key)
# 2. Delete from DB
db.delete(summary)
db.commit()
return {"message": "Video summary and associated S3 file deleted successfully"}
except Exception as e:
db.rollback()
logger.error(f"Failed to delete video summary: {e}")
raise HTTPException(status_code=500, detail=f"Deletion failed: {str(e)}")