Files
youtube-api/proxy_manager.py
2025-12-05 00:15:16 -03:00

134 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}"
)