rescored / backend /app_utils.py
calebhan's picture
deployment 15
ee9cbc1
"""Utility functions for Rescored backend."""
import re
from urllib.parse import urlparse, parse_qs
import yt_dlp
def validate_youtube_url(url: str) -> tuple[bool, str | None]:
"""
Validate YouTube URL and extract video ID.
Args:
url: YouTube URL to validate
Returns:
(is_valid, video_id or error_message)
"""
# Supported formats:
# - https://www.youtube.com/watch?v=VIDEO_ID
# - https://youtu.be/VIDEO_ID
# - https://m.youtube.com/watch?v=VIDEO_ID
patterns = [
r'(?:youtube\.com/watch\?v=|youtu\.be/)([a-zA-Z0-9_-]{11})',
r'youtube\.com/embed/([a-zA-Z0-9_-]{11})',
]
for pattern in patterns:
match = re.search(pattern, url)
if match:
return True, match.group(1)
return False, "Invalid YouTube URL format"
def check_video_availability(video_id: str, max_duration: int = 900) -> dict:
"""
Check if video is available for download.
Args:
video_id: YouTube video ID
max_duration: Maximum allowed duration in seconds
Returns:
Dictionary with 'available' (bool) and 'reason' or 'info'
"""
ydl_opts = {
'quiet': True,
'no_warnings': True,
'extract_flat': True,
'force_ipv4': True, # Force IPv4 to avoid DNS issues
'socket_timeout': 30,
'source_address': '0.0.0.0', # Bind to all interfaces
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(
f"https://youtube.com/watch?v={video_id}",
download=False
)
# Check duration
duration = info.get('duration', 0)
if duration > max_duration:
return {
'available': False,
'reason': f'Video too long (max {max_duration // 60} minutes)'
}
# Check if age-restricted
if info.get('age_limit', 0) > 0:
return {
'available': False,
'reason': 'Age-restricted content not supported'
}
return {'available': True, 'info': info}
except yt_dlp.utils.DownloadError as e:
return {'available': False, 'reason': str(e)}
except Exception as e:
return {'available': False, 'reason': f'Error checking video: {str(e)}'}