youtube-summarizer/backend/api/summaries_fs.py

192 lines
6.1 KiB
Python

"""API endpoints for file system-based summary management."""
from fastapi import APIRouter, HTTPException, Path, Query
from pydantic import BaseModel, Field
from typing import List, Dict, Any, Optional
import logging
from ..services.summary_storage import storage_service
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/summaries", tags=["file-summaries"])
class SummaryResponse(BaseModel):
"""Response model for a single summary."""
video_id: str
generated_at: str
model: str
summary: str
key_points: List[str]
main_themes: List[str]
actionable_insights: List[str]
confidence_score: float
processing_metadata: Dict[str, Any]
cost_data: Dict[str, Any]
transcript_length: int
file_path: str
file_size_bytes: int
file_created_at: str
file_modified_at: str
class SummaryListResponse(BaseModel):
"""Response model for multiple summaries."""
video_id: str
summaries: List[SummaryResponse]
total_summaries: int
class SummaryStatsResponse(BaseModel):
"""Response model for summary statistics."""
total_videos_with_summaries: int
total_summaries: int
total_size_bytes: int
total_size_mb: float
model_distribution: Dict[str, int]
video_ids: List[str]
@router.get("/video/{video_id}", response_model=SummaryListResponse)
async def get_video_summaries(
video_id: str = Path(..., description="YouTube video ID"),
):
"""Get all summaries for a specific video."""
try:
summaries_data = storage_service.list_summaries(video_id)
# Convert to Pydantic models
summaries = [SummaryResponse(**summary) for summary in summaries_data]
return SummaryListResponse(
video_id=video_id,
summaries=summaries,
total_summaries=len(summaries)
)
except Exception as e:
logger.error(f"Failed to get summaries for video {video_id}: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve summaries: {str(e)}"
)
@router.get("/video/{video_id}/{timestamp}", response_model=SummaryResponse)
async def get_specific_summary(
video_id: str = Path(..., description="YouTube video ID"),
timestamp: str = Path(..., description="Summary timestamp")
):
"""Get a specific summary by video ID and timestamp."""
try:
summary_data = storage_service.get_summary(video_id, timestamp)
if not summary_data:
raise HTTPException(
status_code=404,
detail=f"Summary not found for video {video_id} with timestamp {timestamp}"
)
return SummaryResponse(**summary_data)
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to get summary {video_id}/{timestamp}: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve summary: {str(e)}"
)
@router.get("/stats", response_model=SummaryStatsResponse)
async def get_summary_stats():
"""Get statistics about all stored summaries."""
try:
stats = storage_service.get_summary_stats()
return SummaryStatsResponse(**stats)
except Exception as e:
logger.error(f"Failed to get summary stats: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve statistics: {str(e)}"
)
@router.get("/videos", response_model=List[str])
async def list_videos_with_summaries():
"""Get list of video IDs that have summaries."""
try:
video_ids = storage_service.get_videos_with_summaries()
return video_ids
except Exception as e:
logger.error(f"Failed to list videos with summaries: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to retrieve video list: {str(e)}"
)
@router.delete("/video/{video_id}/{timestamp}")
async def delete_summary(
video_id: str = Path(..., description="YouTube video ID"),
timestamp: str = Path(..., description="Summary timestamp")
):
"""Delete a specific summary."""
try:
success = storage_service.delete_summary(video_id, timestamp)
if not success:
raise HTTPException(
status_code=404,
detail=f"Summary not found for video {video_id} with timestamp {timestamp}"
)
return {"message": f"Summary deleted successfully", "video_id": video_id, "timestamp": timestamp}
except HTTPException:
raise
except Exception as e:
logger.error(f"Failed to delete summary {video_id}/{timestamp}: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to delete summary: {str(e)}"
)
@router.post("/video/{video_id}/generate")
async def trigger_summary_generation(
video_id: str = Path(..., description="YouTube video ID"),
force: bool = Query(False, description="Force regeneration even if summaries exist")
):
"""Trigger summary generation for a video."""
try:
# Check if summaries already exist
existing_summaries = storage_service.list_summaries(video_id)
if existing_summaries and not force:
return {
"message": "Summaries already exist for this video",
"video_id": video_id,
"existing_summaries": len(existing_summaries),
"use_force_parameter": "Set force=true to regenerate"
}
# TODO: Integrate with actual summary generation pipeline
# For now, return a placeholder response
return {
"message": "Summary generation would be triggered here",
"video_id": video_id,
"force": force,
"note": "This endpoint will be connected to the DeepSeek summarization pipeline"
}
except Exception as e:
logger.error(f"Failed to trigger summary generation for {video_id}: {e}")
raise HTTPException(
status_code=500,
detail=f"Failed to trigger summary generation: {str(e)}"
)