Update scripts/health_checker.py
All checks were successful
Generate M3U Playlist / build (push) Successful in 2m7s

This commit is contained in:
stoney420 2025-06-28 02:22:49 +02:00
parent e8a792a46b
commit 4ed1b80ac4

View file

@ -1,16 +1,18 @@
#!/usr/bin/env python3
"""
Health Checker - Simple URL health checking for IPTV channels
Health Checker - Simple health checking without external dependencies
"""
import logging
import requests
import urllib.request
import urllib.error
import socket
import time
import concurrent.futures
from typing import Dict, List, Optional
import time
class HealthChecker:
"""Simple health checker for IPTV channel URLs."""
"""Simple health checker using only standard library."""
def __init__(self, config):
self.config = config
@ -18,29 +20,52 @@ class HealthChecker:
self.timeout = config.settings.get('health_check_timeout', 5)
self.max_workers = config.settings.get('max_workers', 4)
# Set default socket timeout
socket.setdefaulttimeout(self.timeout)
def check_single_url(self, url: str) -> Dict:
"""Check a single URL for accessibility."""
"""Check a single URL for accessibility using urllib."""
start_time = time.time()
try:
response = requests.head(
url,
timeout=self.timeout,
allow_redirects=True,
# Create request with proper headers
req = urllib.request.Request(
url,
headers={'User-Agent': 'IPTV-Health-Checker/1.0'}
)
response_time = time.time() - start_time
# Try to open the URL
with urllib.request.urlopen(req, timeout=self.timeout) as response:
response_time = time.time() - start_time
status_code = response.getcode()
return {
'url': url,
'status': 'healthy' if status_code < 400 else 'unhealthy',
'status_code': status_code,
'response_time': round(response_time, 2),
'error': None
}
except urllib.error.HTTPError as e:
return {
'url': url,
'status': 'healthy' if response.status_code < 400 else 'unhealthy',
'status_code': response.status_code,
'response_time': round(response_time, 2),
'error': None
'status': 'unhealthy',
'status_code': e.code,
'response_time': time.time() - start_time,
'error': f'HTTP {e.code}: {e.reason}'
}
except requests.exceptions.Timeout:
except urllib.error.URLError as e:
return {
'url': url,
'status': 'unreachable',
'status_code': None,
'response_time': time.time() - start_time,
'error': f'URL Error: {e.reason}'
}
except socket.timeout:
return {
'url': url,
'status': 'timeout',
@ -49,15 +74,6 @@ class HealthChecker:
'error': 'Request timeout'
}
except requests.exceptions.ConnectionError:
return {
'url': url,
'status': 'unreachable',
'status_code': None,
'response_time': time.time() - start_time,
'error': 'Connection error'
}
except Exception as e:
return {
'url': url,
@ -90,29 +106,68 @@ class HealthChecker:
"""Perform batch health check on multiple channels."""
if not self.config.settings.get('enable_health_check', False):
self.logger.info("Health checking is disabled")
return {'enabled': False, 'results': []}
return {
'enabled': False,
'results': [],
'summary': {
'total': len(channels),
'healthy': 0,
'health_percentage': 0,
'total_check_time': 0
}
}
self.logger.info(f"Starting health check for {len(channels)} channels...")
start_time = time.time()
results = []
# Use ThreadPoolExecutor for concurrent checks
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
# Submit all health check tasks
future_to_channel = {
executor.submit(self.check_channel_health, channel): channel
for channel in channels
}
# Collect results as they complete
for future in concurrent.futures.as_completed(future_to_channel):
# Limit concurrent checks to avoid overwhelming servers
max_workers = min(self.max_workers, len(channels))
if max_workers > 1:
# Use ThreadPoolExecutor for concurrent checks
try:
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
# Submit all health check tasks
future_to_channel = {
executor.submit(self.check_channel_health, channel): channel
for channel in channels
}
# Collect results as they complete
for future in concurrent.futures.as_completed(future_to_channel, timeout=60):
try:
result = future.result(timeout=10)
results.append(result)
except Exception as e:
channel = future_to_channel[future]
self.logger.warning(f"Health check failed for {channel.get('Stream name', 'Unknown')}: {e}")
results.append({
'channel_name': channel.get('Stream name', 'Unknown'),
'url': channel.get('Stream URL', ''),
'status': 'error',
'status_code': None,
'response_time': 0,
'error': str(e)
})
except Exception as e:
self.logger.error(f"Concurrent health check failed: {e}")
# Fall back to sequential processing
for channel in channels:
try:
result = self.check_channel_health(channel)
results.append(result)
except Exception as channel_error:
self.logger.warning(f"Channel check failed: {channel_error}")
else:
# Sequential processing for single worker
for channel in channels:
try:
result = future.result()
result = self.check_channel_health(channel)
results.append(result)
except Exception as e:
channel = future_to_channel[future]
self.logger.error(f"Health check failed for {channel.get('Stream name', 'Unknown')}: {e}")
self.logger.warning(f"Health check failed for {channel.get('Stream name', 'Unknown')}: {e}")
results.append({
'channel_name': channel.get('Stream name', 'Unknown'),
'url': channel.get('Stream URL', ''),
@ -216,33 +271,4 @@ class HealthChecker:
except Exception as e:
self.logger.error(f"Could not save health report: {e}")
return None
# Simple fallback for when requests is not available
class SimpleHealthChecker:
"""Fallback health checker that doesn't require external dependencies."""
def __init__(self, config):
self.config = config
self.logger = logging.getLogger(__name__)
def batch_health_check(self, channels: List[Dict]) -> Dict:
"""Fallback that skips health checking."""
self.logger.info("Health checking disabled (requests library not available)")
return {
'enabled': False,
'results': [],
'summary': {'total': len(channels), 'healthy': 0, 'health_percentage': 0},
'total_time': 0
}
# Try to use the full health checker, fall back to simple one if requests isn't available
try:
import requests
# If requests is available, use the full HealthChecker
except ImportError:
# If requests is not available, use the fallback
class HealthChecker(SimpleHealthChecker):
pass
return None