""" Video API endpoints """ import logging from typing import List, Optional from datetime import datetime from fastapi import APIRouter, Depends, HTTPException, Query, status from pydantic import BaseModel from sqlalchemy.orm import Session from ..core.database import get_db from ..models.video import VideoRecord, VideoStatus from ..services.video_service import VideoService logger = logging.getLogger(__name__) router = APIRouter() # Pydantic models for API requests/responses class VideoResponse(BaseModel): """Video response model""" id: str playlist_id: str video_url: str video_id: str title: Optional[str] playlist_index: Optional[int] upload_date: Optional[datetime] status: str download_requested_at: Optional[datetime] download_completed_at: Optional[datetime] metube_download_id: Optional[str] original_filename: Optional[str] file_moved: bool file_location_note: Optional[str] error_message: Optional[str] retry_count: int last_error_at: Optional[datetime] created_at: datetime updated_at: datetime class Config: from_attributes = True class VideoActionResponse(BaseModel): """Response for video actions""" status: str message: str video: Optional[VideoResponse] = None @router.get("/{video_id}", response_model=VideoResponse) async def get_video( video_id: str, db: Session = Depends(get_db) ): """Get a specific video record""" try: service = VideoService(db) video = service.get_video(video_id) if not video: raise HTTPException(status_code=404, detail="Video not found") return video except HTTPException: raise except Exception as e: logger.error(f"Error getting video {video_id}: {e}") raise HTTPException(status_code=500, detail=f"Error getting video: {str(e)}") @router.post("/{video_id}/download", response_model=VideoActionResponse) async def trigger_video_download( video_id: str, db: Session = Depends(get_db) ): """Manually trigger download for a video""" try: service = VideoService(db) # Get video video = service.get_video(video_id) if not video: raise HTTPException(status_code=404, detail="Video not found") # Trigger download result = await service.download_video(video_id) return VideoActionResponse( status="ok", message="Download triggered successfully", video=result ) except HTTPException: raise except ValueError as e: logger.warning(f"Cannot download video {video_id}: {e}") raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Error downloading video {video_id}: {e}") raise HTTPException(status_code=500, detail=f"Error downloading video: {str(e)}") @router.post("/{video_id}/file-moved", response_model=VideoActionResponse) async def mark_file_as_moved( video_id: str, location_note: Optional[str] = Query(None, description="Optional note about new file location"), db: Session = Depends(get_db) ): """Mark a video file as moved by the user""" try: service = VideoService(db) # Get video video = service.get_video(video_id) if not video: raise HTTPException(status_code=404, detail="Video not found") # Mark as moved updated_video = service.mark_file_as_moved(video_id, location_note) return VideoActionResponse( status="ok", message="File marked as moved successfully", video=updated_video ) except HTTPException: raise except ValueError as e: logger.warning(f"Cannot mark file as moved for video {video_id}: {e}") raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Error marking file as moved for video {video_id}: {e}") raise HTTPException(status_code=500, detail=f"Error marking file as moved: {str(e)}") @router.post("/{video_id}/skip", response_model=VideoActionResponse) async def skip_video( video_id: str, db: Session = Depends(get_db) ): """Mark a video as skipped (won't be downloaded)""" try: service = VideoService(db) # Get video video = service.get_video(video_id) if not video: raise HTTPException(status_code=404, detail="Video not found") # Skip video updated_video = service.skip_video(video_id) return VideoActionResponse( status="ok", message="Video marked as skipped", video=updated_video ) except HTTPException: raise except ValueError as e: logger.warning(f"Cannot skip video {video_id}: {e}") raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Error skipping video {video_id}: {e}") raise HTTPException(status_code=500, detail=f"Error skipping video: {str(e)}") @router.post("/{video_id}/reset", response_model=VideoActionResponse) async def reset_video( video_id: str, db: Session = Depends(get_db) ): """Reset a video to pending status (allow re-download)""" try: service = VideoService(db) # Get video video = service.get_video(video_id) if not video: raise HTTPException(status_code=404, detail="Video not found") # Reset video updated_video = service.reset_video(video_id) return VideoActionResponse( status="ok", message="Video reset to pending status", video=updated_video ) except HTTPException: raise except ValueError as e: logger.warning(f"Cannot reset video {video_id}: {e}") raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Error resetting video {video_id}: {e}") raise HTTPException(status_code=500, detail=f"Error resetting video: {str(e)}")