140 lines
4.4 KiB
Python
140 lines
4.4 KiB
Python
from fastapi import APIRouter, Depends, status
|
|
from typing import Dict, Any
|
|
import logging
|
|
|
|
from backend.models.validation import URLValidationRequest, URLValidationResponse
|
|
from backend.services.video_service import VideoService
|
|
from backend.core.exceptions import UserInputError, ValidationError, UnsupportedFormatError
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter(prefix="/api", tags=["validation"])
|
|
|
|
|
|
def get_video_service() -> VideoService:
|
|
"""Dependency injection for VideoService."""
|
|
return VideoService()
|
|
|
|
|
|
@router.options("/validate-url")
|
|
async def validate_url_options():
|
|
"""Handle CORS preflight for validate-url endpoint."""
|
|
return {"message": "OK"}
|
|
|
|
@router.post(
|
|
"/validate-url",
|
|
response_model=URLValidationResponse,
|
|
status_code=status.HTTP_200_OK,
|
|
summary="Validate YouTube URL",
|
|
description="Validate a YouTube URL and extract the video ID if valid"
|
|
)
|
|
async def validate_url(
|
|
request: URLValidationRequest,
|
|
video_service: VideoService = Depends(get_video_service)
|
|
) -> URLValidationResponse:
|
|
"""
|
|
Validate a YouTube URL and extract video ID.
|
|
|
|
Args:
|
|
request: URLValidationRequest containing the URL to validate
|
|
video_service: VideoService instance for validation logic
|
|
|
|
Returns:
|
|
URLValidationResponse with validation result and details
|
|
"""
|
|
try:
|
|
# Extract video ID from URL
|
|
video_id = video_service.extract_video_id(request.url)
|
|
|
|
# Create normalized URL
|
|
normalized_url = video_service.normalize_url(video_id)
|
|
|
|
logger.info(f"Successfully validated URL: {request.url} -> Video ID: {video_id}")
|
|
|
|
return URLValidationResponse(
|
|
is_valid=True,
|
|
video_id=video_id,
|
|
video_url=normalized_url
|
|
)
|
|
|
|
except ValidationError as e:
|
|
logger.warning(f"Validation error for URL {request.url}: {e.message}")
|
|
return URLValidationResponse(
|
|
is_valid=False,
|
|
error={
|
|
"code": e.error_code.value,
|
|
"message": e.message,
|
|
"details": e.details,
|
|
"recoverable": e.recoverable
|
|
}
|
|
)
|
|
|
|
except UnsupportedFormatError as e:
|
|
logger.warning(f"Unsupported format for URL {request.url}: {e.message}")
|
|
return URLValidationResponse(
|
|
is_valid=False,
|
|
error={
|
|
"code": e.error_code.value,
|
|
"message": e.message,
|
|
"details": e.details,
|
|
"recoverable": e.recoverable
|
|
}
|
|
)
|
|
|
|
except UserInputError as e:
|
|
logger.warning(f"User input error for URL {request.url}: {e.message}")
|
|
return URLValidationResponse(
|
|
is_valid=False,
|
|
error={
|
|
"code": e.error_code.value,
|
|
"message": e.message,
|
|
"details": e.details,
|
|
"recoverable": e.recoverable
|
|
}
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error validating URL {request.url}: {str(e)}")
|
|
return URLValidationResponse(
|
|
is_valid=False,
|
|
error={
|
|
"code": "INTERNAL_ERROR",
|
|
"message": "An unexpected error occurred while validating the URL",
|
|
"details": {"url": request.url},
|
|
"recoverable": False
|
|
}
|
|
)
|
|
|
|
|
|
@router.get(
|
|
"/supported-formats",
|
|
response_model=Dict[str, Any],
|
|
summary="Get supported URL formats",
|
|
description="Retrieve list of supported YouTube URL formats"
|
|
)
|
|
async def get_supported_formats(
|
|
video_service: VideoService = Depends(get_video_service)
|
|
) -> Dict[str, Any]:
|
|
"""
|
|
Get list of supported YouTube URL formats.
|
|
|
|
Args:
|
|
video_service: VideoService instance
|
|
|
|
Returns:
|
|
Dictionary containing supported formats and examples
|
|
"""
|
|
return {
|
|
"supported_formats": video_service.get_supported_formats(),
|
|
"examples": {
|
|
"standard": "https://youtube.com/watch?v=dQw4w9WgXcQ",
|
|
"short": "https://youtu.be/dQw4w9WgXcQ",
|
|
"embed": "https://youtube.com/embed/dQw4w9WgXcQ",
|
|
"mobile": "https://m.youtube.com/watch?v=dQw4w9WgXcQ"
|
|
},
|
|
"notes": [
|
|
"Video IDs must be exactly 11 characters",
|
|
"Playlist URLs are not currently supported",
|
|
"URLs with timestamps and other parameters are accepted"
|
|
]
|
|
} |