diff --git a/database.py b/database.py index 787ca94..8754d6b 100644 --- a/database.py +++ b/database.py @@ -49,6 +49,39 @@ def get_latest_proxy() -> Optional[Dict]: print(f"Erro ao buscar proxy: {e}") return None +def get_all_active_proxies() -> list: + """Retorna todos os proxies ativos do banco""" + try: + conn = get_db_connection() + cursor = conn.cursor() + + cursor.execute(""" + SELECT + id, ip_address, port, protocol, username, password, + country_code, country_name, city, is_active, is_anonymous, + response_time_ms, last_checked_at, last_successful_at, + failure_count, success_count, usage, source, notes, + created_at, updated_at + FROM proxies + WHERE is_active = TRUE + ORDER BY + last_successful_at DESC NULLS LAST, + response_time_ms ASC NULLS LAST, + (CASE WHEN success_count + failure_count > 0 + THEN CAST(success_count AS FLOAT) / (success_count + failure_count) + ELSE 0 END) DESC, + created_at DESC + """) + + proxies = cursor.fetchall() + cursor.close() + conn.close() + + return [dict(proxy) for proxy in proxies] if proxies else [] + except Exception as e: + print(f"Erro ao buscar proxies: {e}") + return [] + def delete_proxy(proxy_id: int) -> bool: try: conn = get_db_connection() diff --git a/main.py b/main.py index 8167eb4..ba42782 100644 --- a/main.py +++ b/main.py @@ -105,7 +105,7 @@ def get_video_metadata( return info - info = execute_with_proxy_retry(ydl_opts, extract_metadata) + info = execute_with_proxy_retry(ydl_opts, extract_metadata, retry_per_proxy=2) except ProxyError as e: error_msg = str(e).replace('\n', ' ').strip() @@ -181,10 +181,17 @@ def download_video( "quiet": True, "noplaylist": True, "merge_output_format": "mp4", - "cookiefile": "/app/cookies.txt", - "socket_timeout": 8, + "nocheckcertificate": True, + "socket_timeout": 60, "retries": 0, "extractor_retries": 0, + "force_ipv4": True, + "geo_bypass": True, + "extractor_args": {"youtube": {"player_client": ["android"], "player_skip": ["webpage"]}}, + "http_headers": { + "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7", + "User-Agent": "com.google.android.youtube/19.17.36 (Linux; U; Android 13) gzip", + }, } try: @@ -223,7 +230,7 @@ def download_video( "cached": False } - return execute_with_proxy_retry(ydl_opts, download_operation) + return execute_with_proxy_retry(ydl_opts, download_operation, retry_per_proxy=3) except ProxyError as e: raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}") @@ -239,6 +246,7 @@ def search_youtube_yt_dlp( "quiet": True, "extract_flat": "in_playlist", "skip_download": True, + "nocheckcertificate": True, "socket_timeout": 8, "retries": 0, } @@ -262,7 +270,7 @@ def search_youtube_yt_dlp( }) return {"results": results} - return execute_with_proxy_retry(ydl_opts, search_operation) + return execute_with_proxy_retry(ydl_opts, search_operation, retry_per_proxy=2) except ProxyError as e: raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}") @@ -275,6 +283,7 @@ def list_formats(url: str): "quiet": False, "no_warnings": False, "noplaylist": True, + "nocheckcertificate": True, "force_ipv4": True, "geo_bypass": True, "extractor_args": {"youtube": {"player_client": ["android"], "player_skip": ["webpage"]}}, @@ -301,7 +310,7 @@ def list_formats(url: str): } for f in fmts] return {"total": len(brief), "formats": brief[:60]} - return execute_with_proxy_retry(opts, list_formats_operation) + return execute_with_proxy_retry(opts, list_formats_operation, retry_per_proxy=2) except ProxyError as e: raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}") diff --git a/proxy_manager.py b/proxy_manager.py index 2448465..5dc478b 100644 --- a/proxy_manager.py +++ b/proxy_manager.py @@ -1,6 +1,6 @@ from typing import Callable, Any, Optional from yt_dlp import YoutubeDL -from database import get_latest_proxy, delete_proxy, format_proxy_url, mark_proxy_success +from database import get_all_active_proxies, format_proxy_url, mark_proxy_success class ProxyError(Exception): pass @@ -22,6 +22,16 @@ def is_proxy_error(error_msg: str) -> bool: '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 = [ @@ -43,56 +53,81 @@ def is_proxy_error(error_msg: str) -> bool: def execute_with_proxy_retry( ydl_opts: dict, operation: Callable[[YoutubeDL], Any], - max_retries: int = 10 + retry_per_proxy: int = 2 ) -> Any: - attempts = 0 + """ + 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 - while attempts < max_retries: - attempts += 1 - proxy_data = 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} - try: - proxy_data = get_latest_proxy() + print(f"\n[Proxy {proxy_index}/{total_proxies}] {proxy_url} (ID: {proxy_data['id']})") - if proxy_data: - proxy_url = format_proxy_url(proxy_data) - ydl_opts_with_proxy = {**ydl_opts, 'proxy': proxy_url} - print(f"Tentativa {attempts}: Usando proxy {proxy_url} (ID: {proxy_data['id']})") - else: - if attempts == 1: - print(f"Tentativa {attempts}: Nenhum proxy disponível, tentando sem proxy") - ydl_opts_with_proxy = ydl_opts - else: - raise ProxyError("Não há mais proxies disponíveis no banco de dados") + # 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"Operação concluída com sucesso na tentativa {attempts}") - - if proxy_data: - mark_proxy_success(proxy_data['id']) + 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 + except Exception as e: + error_msg = str(e) + last_error = e - print(f"Erro na tentativa {attempts}: {error_msg}") + print(f"✗ Falhou") + print(f" Erro: {error_msg[:80]}...") - if is_proxy_error(error_msg): - if proxy_data: - print(f"Erro identificado como erro de proxy. Removendo proxy ID {proxy_data['id']}") - delete_proxy(proxy_data['id']) - else: - print("Erro de proxy mas nenhum proxy estava sendo usado") + # 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 - continue - else: - print(f"Erro não é relacionado a proxy, lançando exceção") - 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 {max_retries} tentativas. Último erro: {last_error}" + f"Falha após tentar {total_proxies} proxies + tentativa sem proxy. Último erro: {last_error}" ) +