diff --git a/app.py b/app.py index 94c5d89..989c682 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,6 @@ import os import re +import asyncio import httpx import requests from bs4 import BeautifulSoup @@ -352,22 +353,18 @@ async def search_torrent_by_id(torrent_id: str) -> dict: result = results[0] print(f"Found torrent by ID: {result.get('Name', 'Unknown')[:100]}...") - # Получаем magnet-ссылку и добавляем публичные трекеры - magnet = result.get('Magnet', '') - if magnet and not magnet.startswith('magnet:'): - # Если нет magnet-ссылки, генерируем из хэша - hash_value = result.get('Hash', '') - if hash_value: - magnet = f"magnet:?xt=urn:btih:{hash_value}" + # Получаем хэш и создаем чистую magnet-ссылку с публичными трекерами + hash_value = result.get('Hash', '') + torrent_title = result.get('Name', '') - # Создаем чистую magnet-ссылку только с хэшем для использования DHT - if magnet and magnet.startswith('magnet:'): - # Извлекаем только хэш из magnet-ссылки - import re - hash_match = re.search(r'xt=urn:btih:([A-F0-9]+)', magnet) - if hash_match: - hash_value = hash_match.group(1) - # Создаем чистую magnet-ссылку только с хэшем (DHT будет искать пиров) + if hash_value: + # Генерируем чистую magnet-ссылку с публичными трекерами + magnet = generate_clean_magnet(hash_value, torrent_title) + print(f"Generated clean magnet with public trackers: {magnet[:100]}...") + else: + # Fallback на оригинальную magnet-ссылку если нет хэша + magnet = result.get('Magnet', '') + if magnet and not magnet.startswith('magnet:'): magnet = f"magnet:?xt=urn:btih:{hash_value}" # Парсим результат в стандартный формат @@ -441,14 +438,16 @@ def parse_torrent_result(result: dict, movie_title: str, year: str = None) -> di # Парсим размер size_bytes = parse_size_to_bytes(result['Size']) - # Создаем magnet-ссылку (используем поле Magnet если есть, иначе генерируем из хэша) + # Создаем чистую magnet-ссылку с публичными трекерами magnet = None - if 'Magnet' in result and result['Magnet']: - magnet = result['Magnet'] - elif 'Hash' in result and result['Hash']: - # Генерируем magnet-ссылку из хэша + if 'Hash' in result and result['Hash']: + # Генерируем чистую magnet-ссылку из хэша с публичными трекерами hash_value = result['Hash'] - magnet = f"magnet:?xt=urn:btih:{hash_value}" + torrent_title = result['Name'] + magnet = generate_clean_magnet(hash_value, torrent_title) + elif 'Magnet' in result and result['Magnet']: + # Если нет хэша, используем оригинальную magnet-ссылку + magnet = result['Magnet'] elif 'Torrent' in result and result['Torrent']: # Используем URL на .torrent файл как fallback magnet = result['Torrent'] @@ -521,6 +520,32 @@ def parse_size_to_bytes(size_str: str) -> int: return 0 +def generate_clean_magnet(hash_value: str, title: str = None) -> str: + """Генерирует чистую magnet-ссылку с публичными трекерами""" + if not hash_value: + return "" + + # Проверенные рабочие трекеры (минимальный набор) + public_trackers = [ + "udp://tracker.opentrackr.org:1337/announce", + "udp://open.stealth.si:80/announce", + "udp://tracker.openbittorrent.com:6969/announce", + "udp://tracker.coppersurfer.tk:6969/announce", + "udp://tracker.leechers-paradise.org:6969/announce" + ] + + # Создаем базовую magnet-ссылку с хэшем + magnet = f"magnet:?xt=urn:btih:{hash_value}" + + # НЕ добавляем название файла - это может вызывать проблемы с кириллицей + # DHT сам найдет название по хэшу + + # Добавляем публичные трекеры + for tracker in public_trackers: + magnet += f"&tr={tracker}" + + return magnet + @app.get("/", response_class=HTMLResponse) async def home(request: Request): """Главная страница с формой поиска""" @@ -613,21 +638,15 @@ async def add_torrent_to_client(torrent_id: str = Form(...)): try: print(f"Attempting to add torrent with ID: {torrent_id}") - # Получаем magnet-ссылку по ID + # Получаем информацию о торренте по ID torrent_info = await search_torrent_by_id(torrent_id) if not torrent_info: return {"status": "error", "message": f"Торрент с ID {torrent_id} не найден"} - magnet = torrent_info.get('magnet') - if not magnet: - return {"status": "error", "message": f"Magnet-ссылка не найдена для торрента {torrent_id}"} - - print(f"Found magnet link: {magnet[:100]}...") - # Получаем учетные данные из переменных окружения qb_username = os.getenv("QBITTORRENT_USERNAME", "admin") qb_password = os.getenv("QBITTORRENT_PASSWORD", "vrubel07") - qb_host = os.getenv("QBITTORRENT_HOST", "172.17.0.1") + qb_host = os.getenv("QBITTORRENT_HOST", "localhost") qb_port = os.getenv("QBITTORRENT_PORT", "8080") qb_url = f"http://{qb_host}:{qb_port}" @@ -643,19 +662,52 @@ async def add_torrent_to_client(torrent_id: str = Form(...)): print("Successfully authenticated with qBittorrent") - # Добавляем торрент по magnet-ссылке - add_response = await client.post( - f"{qb_url}/api/v2/torrents/add", - data={"urls": magnet} - ) + # Пробуем сначала добавить через magnet-ссылку (более надежно) + magnet = torrent_info.get('magnet') + if magnet: + print(f"Trying to add via magnet link: {magnet[:100]}...") + add_response = await client.post( + f"{qb_url}/api/v2/torrents/add", + data={"urls": magnet} + ) + + print(f"Add via magnet response status: {add_response.status_code}") + print(f"Add via magnet response text: {add_response.text}") + + if add_response.status_code == 200 and add_response.text.strip() == "Ok.": + # Проверяем, что торрент действительно добавился + await asyncio.sleep(2) # Ждем немного + torrents_response = await client.get(f"{qb_url}/api/v2/torrents/info") + if torrents_response.status_code == 200: + torrents = torrents_response.json() + # Ищем торрент по хэшу + torrent_hash = torrent_info.get('hash', '').upper() + for torrent in torrents: + if torrent.get('hash', '').upper() == torrent_hash: + return {"status": "success", "message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через magnet-ссылку!"} + + return {"status": "success", "message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через magnet-ссылку!"} + else: + print(f"Magnet link failed, trying .torrent file...") - print(f"Add response status: {add_response.status_code}") - print(f"Add response text: {add_response.text}") - - if add_response.status_code == 200: - return {"status": "success", "message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent!"} + # Если magnet не сработал, пробуем .torrent файл + torrent_url = torrent_info.get('torrent_url') + if torrent_url: + print(f"Trying to add via .torrent file: {torrent_url}") + add_response = await client.post( + f"{qb_url}/api/v2/torrents/add", + data={"urls": torrent_url} + ) + + print(f"Add via .torrent response status: {add_response.status_code}") + print(f"Add via .torrent response text: {add_response.text}") + + if add_response.status_code == 200 and add_response.text.strip() == "Ok.": + return {"status": "success", "message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через .torrent файл!"} + else: + return {"status": "error", "message": f"Ошибка добавления торрента (HTTP {add_response.status_code}): {add_response.text}"} else: - return {"status": "error", "message": f"Ошибка добавления торрента (HTTP {add_response.status_code}): {add_response.text}"} + return {"status": "error", "message": f"Не найдена ни magnet-ссылка, ни .torrent файл для торрента {torrent_id}"} except httpx.ConnectError as e: print(f"Connection error: {e}")