130 lines
4.4 KiB
Python
130 lines
4.4 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, mark_proxy_failure
|
|
|
|
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
|
|
'failed to parse json', # Erros de parsing JSON (proxy retornando HTML)
|
|
'jsondecode', # Erros de decodificação JSON
|
|
'remote end closed', # Conexão fechada pelo proxy
|
|
'remotedisconnected', # Desconexão remota
|
|
'connection/parsing error', # Erros de conexão/parsing do download
|
|
]
|
|
|
|
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,
|
|
max_proxies_to_try: int = None
|
|
) -> Any:
|
|
"""
|
|
Tenta executar operação com 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
|
|
max_proxies_to_try: Número máximo de proxies a tentar (None = todos)
|
|
"""
|
|
# Busca TODOS os proxies ativos
|
|
all_proxies = get_all_active_proxies()
|
|
|
|
# Limita número de proxies se especificado
|
|
if max_proxies_to_try is not None and max_proxies_to_try > 0:
|
|
all_proxies = all_proxies[:max_proxies_to_try]
|
|
|
|
total_proxies = len(all_proxies)
|
|
|
|
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)
|
|
# Proxy desativado temporariamente - Para reativar, descomente a linha abaixo:
|
|
# ydl_opts_with_proxy = {**ydl_opts, 'proxy': proxy_url}
|
|
ydl_opts_with_proxy = {**ydl_opts} # Sem proxy por enquanto
|
|
|
|
proxy_failed = False
|
|
|
|
# Tenta N vezes com o MESMO proxy
|
|
for attempt in range(1, retry_per_proxy + 1):
|
|
try:
|
|
with YoutubeDL(ydl_opts_with_proxy) as ydl:
|
|
result = operation(ydl)
|
|
|
|
mark_proxy_success(proxy_data['id'])
|
|
return result
|
|
|
|
except Exception as e:
|
|
error_msg = str(e)
|
|
last_error = e
|
|
|
|
# Se não for erro de proxy, lança imediatamente
|
|
if not is_proxy_error(error_msg):
|
|
raise e
|
|
|
|
proxy_failed = True
|
|
|
|
# Se chegou aqui, falhou todas as tentativas com este proxy
|
|
if proxy_failed:
|
|
mark_proxy_failure(proxy_data['id'])
|
|
|
|
# Se chegou aqui, todos os proxies falharam, tenta SEM proxy
|
|
try:
|
|
with YoutubeDL(ydl_opts) as ydl:
|
|
result = operation(ydl)
|
|
return result
|
|
except Exception as e:
|
|
last_error = e
|
|
|
|
raise ProxyError(
|
|
f"Falha após tentar {total_proxies} proxies + tentativa sem proxy. Último erro: {last_error}"
|
|
)
|
|
|