| | 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") |
| |
|
| | |
| | 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. |
| | """ |
| | |
| | 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") |
| |
|
| | |
| | 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) |
| |
|
| | |
| | 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: |
| | |
| | if summary.s3_key: |
| | await s3_service.delete_file(summary.s3_key) |
| | |
| | |
| | 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)}") |
| |
|