134 lines
4.5 KiB
Python
134 lines
4.5 KiB
Python
from typing import Callable, Any, Optional
|
||
from yt_dlp import YoutubeDL
|
||
from database import get_all_active_proxies, format_proxy_url, mark_proxy_success
|
||
|
||
class ProxyError(Exception):
|
||
pass
|
||
|
||
def is_proxy_error(error_msg: str) -> bool:
|
||
proxy_error_keywords = [
|
||
'proxy',
|
||
'connection',
|
||
'timeout',
|
||
'timed out',
|
||
'refused',
|
||
'unreachable',
|
||
'unable to connect',
|
||
'network',
|
||
'failed to connect',
|
||
'connection reset',
|
||
'connection aborted',
|
||
'read timed out',
|
||
'http error 407', # Proxy authentication required
|
||
'tunnel connection failed',
|
||
'connect to host',
|
||
'bot', # YouTube bloqueando proxy como bot
|
||
'sign in to confirm', # YouTube pedindo verificação
|
||
'http error 429', # Too many requests
|
||
'failed to extract', # Falha ao extrair dados (pode ser proxy ruim)
|
||
'player response', # Problemas com resposta do player
|
||
'http error 403', # Forbidden (pode ser proxy bloqueado)
|
||
'http error 503', # Service unavailable (pode ser proxy)
|
||
'ssl', # Erros SSL causados por proxy
|
||
'certificate', # Erros de certificado causados por proxy
|
||
'certificate_verify_failed', # Verificação de certificado SSL falhou
|
||
]
|
||
|
||
non_proxy_error_keywords = [
|
||
'requested format is not available',
|
||
'format not available',
|
||
'no video formats',
|
||
'video unavailable',
|
||
'private video',
|
||
'age restricted',
|
||
]
|
||
|
||
error_lower = error_msg.lower()
|
||
|
||
if any(keyword in error_lower for keyword in non_proxy_error_keywords):
|
||
return False
|
||
|
||
return any(keyword in error_lower for keyword in proxy_error_keywords)
|
||
|
||
def execute_with_proxy_retry(
|
||
ydl_opts: dict,
|
||
operation: Callable[[YoutubeDL], Any],
|
||
retry_per_proxy: int = 2
|
||
) -> Any:
|
||
"""
|
||
Tenta executar operação com todos os proxies disponíveis.
|
||
Cada proxy é tentado N vezes antes de passar para o próximo.
|
||
Proxies NÃO são removidos do banco, apenas pulados.
|
||
|
||
Args:
|
||
ydl_opts: Opções do YoutubeDL
|
||
operation: Função a ser executada
|
||
retry_per_proxy: Número de tentativas por proxy antes de pular para o próximo
|
||
"""
|
||
# Busca TODOS os proxies ativos
|
||
all_proxies = get_all_active_proxies()
|
||
total_proxies = len(all_proxies)
|
||
|
||
print(f"\n{'='*60}")
|
||
print(f"Proxies disponíveis no banco: {total_proxies}")
|
||
print(f"Tentativas por proxy: {retry_per_proxy}")
|
||
print(f"{'='*60}\n")
|
||
|
||
last_error = None
|
||
|
||
# Tenta cada proxy da lista
|
||
for proxy_index, proxy_data in enumerate(all_proxies, 1):
|
||
proxy_url = format_proxy_url(proxy_data)
|
||
ydl_opts_with_proxy = {**ydl_opts, 'proxy': proxy_url}
|
||
|
||
print(f"\n[Proxy {proxy_index}/{total_proxies}] {proxy_url} (ID: {proxy_data['id']})")
|
||
|
||
# Tenta N vezes com o MESMO proxy
|
||
for attempt in range(1, retry_per_proxy + 1):
|
||
try:
|
||
print(f" → Tentativa {attempt}/{retry_per_proxy}...", end=" ")
|
||
|
||
with YoutubeDL(ydl_opts_with_proxy) as ydl:
|
||
result = operation(ydl)
|
||
|
||
print(f"✓ SUCESSO!")
|
||
mark_proxy_success(proxy_data['id'])
|
||
return result
|
||
|
||
except Exception as e:
|
||
error_msg = str(e)
|
||
last_error = e
|
||
|
||
print(f"✗ Falhou")
|
||
print(f" Erro: {error_msg[:80]}...")
|
||
|
||
# Se não for erro de proxy, lança imediatamente
|
||
if not is_proxy_error(error_msg):
|
||
print(f" ⚠ Erro não relacionado a proxy, abortando")
|
||
raise e
|
||
|
||
# Se chegou aqui, falhou todas as tentativas com este proxy
|
||
print(f" ⨯ Proxy falhou {retry_per_proxy} vezes, pulando para o próximo...")
|
||
|
||
# Se chegou aqui, todos os proxies falharam, tenta SEM proxy
|
||
print(f"\n{'='*60}")
|
||
print(f"Todos os {total_proxies} proxies falharam")
|
||
print(f"Tentando SEM proxy como último recurso...")
|
||
print(f"{'='*60}\n")
|
||
|
||
try:
|
||
print(f" → Tentativa sem proxy...", end=" ")
|
||
with YoutubeDL(ydl_opts) as ydl:
|
||
result = operation(ydl)
|
||
print(f"✓ SUCESSO!")
|
||
return result
|
||
except Exception as e:
|
||
print(f"✗ Falhou")
|
||
print(f" Erro: {str(e)[:80]}...")
|
||
last_error = e
|
||
|
||
raise ProxyError(
|
||
f"Falha após tentar {total_proxies} proxies + tentativa sem proxy. Último erro: {last_error}"
|
||
)
|
||
|