Validate if a stream URL is accessible without downloading the full content. Note: UDP/RTP/RTSP streams are automatically considered valid as they cannot be validated via HTTP methods. Args: url (str): The URL to validate user_agent (str): User agent to use for the
(url, user_agent=None, timeout=(5, 5))
| 431 | close_old_connections() |
| 432 | |
| 433 | def validate_stream_url(url, user_agent=None, timeout=(5, 5)): |
| 434 | """ |
| 435 | Validate if a stream URL is accessible without downloading the full content. |
| 436 | |
| 437 | Note: UDP/RTP/RTSP streams are automatically considered valid as they cannot |
| 438 | be validated via HTTP methods. |
| 439 | |
| 440 | Args: |
| 441 | url (str): The URL to validate |
| 442 | user_agent (str): User agent to use for the request |
| 443 | timeout (tuple): Connection and read timeout in seconds |
| 444 | |
| 445 | Returns: |
| 446 | tuple: (is_valid, final_url, status_code, message) |
| 447 | """ |
| 448 | # Check if URL uses non-HTTP protocols (UDP/RTP/RTSP) |
| 449 | # These cannot be validated via HTTP methods, so we skip validation |
| 450 | if url.startswith(('udp://', 'rtp://', 'rtsp://')): |
| 451 | logger.info(f"Skipping HTTP validation for non-HTTP protocol: {url}") |
| 452 | return True, url, 200, "Non-HTTP protocol (UDP/RTP/RTSP) - validation skipped" |
| 453 | |
| 454 | try: |
| 455 | # Create session with proper headers |
| 456 | session = requests.Session() |
| 457 | headers = { |
| 458 | 'User-Agent': user_agent, |
| 459 | 'Connection': 'close' # Don't keep connection alive |
| 460 | } |
| 461 | session.headers.update(headers) |
| 462 | |
| 463 | # Make HEAD request first as it's faster and doesn't download content |
| 464 | head_request_success = True |
| 465 | try: |
| 466 | head_response = session.head( |
| 467 | url, |
| 468 | timeout=timeout, |
| 469 | allow_redirects=True |
| 470 | ) |
| 471 | except requests.exceptions.RequestException as e: |
| 472 | head_request_success = False |
| 473 | logger.warning(f"Request error (HEAD), assuming HEAD not supported: {str(e)}") |
| 474 | |
| 475 | # If HEAD not supported, server will return 405 or other error |
| 476 | if head_request_success and (200 <= head_response.status_code < 300): |
| 477 | # HEAD request successful |
| 478 | return True, url, head_response.status_code, "Valid (HEAD request)" |
| 479 | |
| 480 | # Try a GET request with stream=True to avoid downloading all content |
| 481 | get_response = session.get( |
| 482 | url, |
| 483 | stream=True, |
| 484 | timeout=timeout, |
| 485 | allow_redirects=True |
| 486 | ) |
| 487 | |
| 488 | # IMPORTANT: Check status code first before checking content |
| 489 | if not (200 <= get_response.status_code < 300): |
| 490 | logger.warning(f"Stream validation failed with HTTP status {get_response.status_code}") |