Inicia concerto do Proxy
This commit is contained in:
33
database.py
33
database.py
@@ -49,6 +49,39 @@ def get_latest_proxy() -> Optional[Dict]:
|
|||||||
print(f"Erro ao buscar proxy: {e}")
|
print(f"Erro ao buscar proxy: {e}")
|
||||||
return None
|
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:
|
def delete_proxy(proxy_id: int) -> bool:
|
||||||
try:
|
try:
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
|
|||||||
21
main.py
21
main.py
@@ -105,7 +105,7 @@ def get_video_metadata(
|
|||||||
|
|
||||||
return info
|
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:
|
except ProxyError as e:
|
||||||
error_msg = str(e).replace('\n', ' ').strip()
|
error_msg = str(e).replace('\n', ' ').strip()
|
||||||
@@ -181,10 +181,17 @@ def download_video(
|
|||||||
"quiet": True,
|
"quiet": True,
|
||||||
"noplaylist": True,
|
"noplaylist": True,
|
||||||
"merge_output_format": "mp4",
|
"merge_output_format": "mp4",
|
||||||
"cookiefile": "/app/cookies.txt",
|
"nocheckcertificate": True,
|
||||||
"socket_timeout": 8,
|
"socket_timeout": 60,
|
||||||
"retries": 0,
|
"retries": 0,
|
||||||
"extractor_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:
|
try:
|
||||||
@@ -223,7 +230,7 @@ def download_video(
|
|||||||
"cached": False
|
"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:
|
except ProxyError as e:
|
||||||
raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}")
|
raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}")
|
||||||
@@ -239,6 +246,7 @@ def search_youtube_yt_dlp(
|
|||||||
"quiet": True,
|
"quiet": True,
|
||||||
"extract_flat": "in_playlist",
|
"extract_flat": "in_playlist",
|
||||||
"skip_download": True,
|
"skip_download": True,
|
||||||
|
"nocheckcertificate": True,
|
||||||
"socket_timeout": 8,
|
"socket_timeout": 8,
|
||||||
"retries": 0,
|
"retries": 0,
|
||||||
}
|
}
|
||||||
@@ -262,7 +270,7 @@ def search_youtube_yt_dlp(
|
|||||||
})
|
})
|
||||||
return {"results": results}
|
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:
|
except ProxyError as e:
|
||||||
raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}")
|
raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}")
|
||||||
@@ -275,6 +283,7 @@ def list_formats(url: str):
|
|||||||
"quiet": False,
|
"quiet": False,
|
||||||
"no_warnings": False,
|
"no_warnings": False,
|
||||||
"noplaylist": True,
|
"noplaylist": True,
|
||||||
|
"nocheckcertificate": True,
|
||||||
"force_ipv4": True,
|
"force_ipv4": True,
|
||||||
"geo_bypass": True,
|
"geo_bypass": True,
|
||||||
"extractor_args": {"youtube": {"player_client": ["android"], "player_skip": ["webpage"]}},
|
"extractor_args": {"youtube": {"player_client": ["android"], "player_skip": ["webpage"]}},
|
||||||
@@ -301,7 +310,7 @@ def list_formats(url: str):
|
|||||||
} for f in fmts]
|
} for f in fmts]
|
||||||
return {"total": len(brief), "formats": brief[:60]}
|
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:
|
except ProxyError as e:
|
||||||
raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}")
|
raise HTTPException(status_code=503, detail=f"Erro com proxies: {e}")
|
||||||
|
|||||||
113
proxy_manager.py
113
proxy_manager.py
@@ -1,6 +1,6 @@
|
|||||||
from typing import Callable, Any, Optional
|
from typing import Callable, Any, Optional
|
||||||
from yt_dlp import YoutubeDL
|
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):
|
class ProxyError(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -22,6 +22,16 @@ def is_proxy_error(error_msg: str) -> bool:
|
|||||||
'http error 407', # Proxy authentication required
|
'http error 407', # Proxy authentication required
|
||||||
'tunnel connection failed',
|
'tunnel connection failed',
|
||||||
'connect to host',
|
'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 = [
|
non_proxy_error_keywords = [
|
||||||
@@ -43,56 +53,81 @@ def is_proxy_error(error_msg: str) -> bool:
|
|||||||
def execute_with_proxy_retry(
|
def execute_with_proxy_retry(
|
||||||
ydl_opts: dict,
|
ydl_opts: dict,
|
||||||
operation: Callable[[YoutubeDL], Any],
|
operation: Callable[[YoutubeDL], Any],
|
||||||
max_retries: int = 10
|
retry_per_proxy: int = 2
|
||||||
) -> Any:
|
) -> 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
|
last_error = None
|
||||||
|
|
||||||
while attempts < max_retries:
|
# Tenta cada proxy da lista
|
||||||
attempts += 1
|
for proxy_index, proxy_data in enumerate(all_proxies, 1):
|
||||||
proxy_data = None
|
proxy_url = format_proxy_url(proxy_data)
|
||||||
|
ydl_opts_with_proxy = {**ydl_opts, 'proxy': proxy_url}
|
||||||
|
|
||||||
try:
|
print(f"\n[Proxy {proxy_index}/{total_proxies}] {proxy_url} (ID: {proxy_data['id']})")
|
||||||
proxy_data = get_latest_proxy()
|
|
||||||
|
|
||||||
if proxy_data:
|
# Tenta N vezes com o MESMO proxy
|
||||||
proxy_url = format_proxy_url(proxy_data)
|
for attempt in range(1, retry_per_proxy + 1):
|
||||||
ydl_opts_with_proxy = {**ydl_opts, 'proxy': proxy_url}
|
try:
|
||||||
print(f"Tentativa {attempts}: Usando proxy {proxy_url} (ID: {proxy_data['id']})")
|
print(f" → Tentativa {attempt}/{retry_per_proxy}...", end=" ")
|
||||||
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")
|
|
||||||
|
|
||||||
with YoutubeDL(ydl_opts_with_proxy) as ydl:
|
with YoutubeDL(ydl_opts_with_proxy) as ydl:
|
||||||
result = operation(ydl)
|
result = operation(ydl)
|
||||||
print(f"Operação concluída com sucesso na tentativa {attempts}")
|
|
||||||
|
|
||||||
if proxy_data:
|
|
||||||
mark_proxy_success(proxy_data['id'])
|
|
||||||
|
|
||||||
|
print(f"✓ SUCESSO!")
|
||||||
|
mark_proxy_success(proxy_data['id'])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
last_error = 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):
|
# Se não for erro de proxy, lança imediatamente
|
||||||
if proxy_data:
|
if not is_proxy_error(error_msg):
|
||||||
print(f"Erro identificado como erro de proxy. Removendo proxy ID {proxy_data['id']}")
|
print(f" ⚠ Erro não relacionado a proxy, abortando")
|
||||||
delete_proxy(proxy_data['id'])
|
raise e
|
||||||
else:
|
|
||||||
print("Erro de proxy mas nenhum proxy estava sendo usado")
|
|
||||||
|
|
||||||
continue
|
# Se chegou aqui, falhou todas as tentativas com este proxy
|
||||||
else:
|
print(f" ⨯ Proxy falhou {retry_per_proxy} vezes, pulando para o próximo...")
|
||||||
print(f"Erro não é relacionado a proxy, lançando exceção")
|
|
||||||
raise e
|
# 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(
|
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}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user