более надежный поиск магнет ссылки
This commit is contained in:
parent
05f6fe40f0
commit
368d57012d
2 changed files with 209 additions and 36 deletions
239
app.py
239
app.py
|
|
@ -301,18 +301,40 @@ async def search_torrents(movie_title: str, year: str = None, original_title: st
|
|||
# Обрабатываем все результаты и скорим их
|
||||
for result in results[:20]: # Берем больше результатов для скоринга
|
||||
print(f"Processing torrent: {result['Name'][:100]}...")
|
||||
# Логируем доступные поля для отладки
|
||||
torrent_id = result.get('Id') or result.get('id') or result.get('ID') or result.get('Hash', '')[:8] or ''
|
||||
if torrent_id:
|
||||
print(f" Found ID: {torrent_id[:20]}...")
|
||||
torrent = parse_torrent_result(result, movie_title, year)
|
||||
if torrent:
|
||||
# Если ID не был найден, пробуем использовать Hash как ID
|
||||
if not torrent.get('id') or torrent.get('id') == '':
|
||||
torrent_id_from_hash = result.get('Hash', '')
|
||||
if torrent_id_from_hash:
|
||||
torrent['id'] = torrent_id_from_hash
|
||||
print(f" Using Hash as ID: {torrent_id_from_hash[:20]}...")
|
||||
|
||||
# Скорим торрент
|
||||
score = score_torrent(torrent, movie_title, original_title, year)
|
||||
torrent['relevance_score'] = score
|
||||
size_mb = torrent.get('size_bytes', 0) / (1024 * 1024)
|
||||
print(f"Torrent score: {score:.2f}, size: {size_mb:.1f}MB, seeds: {torrent.get('seeds', 0)} - {result['Name'][:100]}...")
|
||||
print(f"Torrent score: {score:.2f}, size: {size_mb:.1f}MB, seeds: {torrent.get('seeds', 0)}, ID: {torrent.get('id', 'None')[:20]}...")
|
||||
|
||||
# Добавляем только если скор больше 0.1 и размер больше 100MB
|
||||
if score > 0.1 and torrent.get('size_bytes', 0) > 100 * 1024 * 1024:
|
||||
# Проверяем, что ID есть, иначе используем Hash
|
||||
if not torrent.get('id') or torrent.get('id') == '':
|
||||
if result.get('Hash'):
|
||||
torrent['id'] = result['Hash']
|
||||
elif result.get('Url'):
|
||||
# Пытаемся извлечь ID из URL
|
||||
url_id_match = re.search(r'/(\d+)/?$', result.get('Url', ''))
|
||||
if url_id_match:
|
||||
torrent['id'] = url_id_match.group(1)
|
||||
else:
|
||||
torrent['id'] = result.get('Url', '')[-20:] # Используем последние 20 символов URL
|
||||
torrents.append(torrent)
|
||||
print(f" ✅ Added to results")
|
||||
print(f" ✅ Added to results with ID: {torrent.get('id', 'None')[:20]}...")
|
||||
else:
|
||||
print(f" ❌ Filtered out (score: {score:.2f}, size: {size_mb:.1f}MB)")
|
||||
|
||||
|
|
@ -355,15 +377,28 @@ async def search_torrent_by_id(torrent_id: str) -> dict:
|
|||
|
||||
try:
|
||||
print(f"Searching ID {torrent_id} on {provider_name}")
|
||||
response = await client.get(
|
||||
# Пробуем разные варианты API для поиска по ID
|
||||
search_urls = [
|
||||
f"{TORRENT_SEARCH_URL}/api/search/id/{provider_name}",
|
||||
params={"query": torrent_id}
|
||||
)
|
||||
f"{TORRENT_SEARCH_URL}/api/search/{provider_name}",
|
||||
]
|
||||
|
||||
if response.status_code == 200:
|
||||
results = response.json()
|
||||
print(f"Response from {provider_name}: {type(results)} - {len(results) if isinstance(results, list) else 'not a list'}")
|
||||
if results and isinstance(results, list) and len(results) > 0:
|
||||
response = None
|
||||
results = None
|
||||
|
||||
for search_url in search_urls:
|
||||
try:
|
||||
response = await client.get(search_url, params={"query": torrent_id}, timeout=30.0)
|
||||
if response.status_code == 200:
|
||||
results = response.json()
|
||||
print(f"Response from {provider_name} ({search_url}): {type(results)} - {len(results) if isinstance(results, list) else 'not a list'}")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"Error trying {search_url}: {e}")
|
||||
continue
|
||||
|
||||
if response and response.status_code == 200 and results:
|
||||
if isinstance(results, list) and len(results) > 0:
|
||||
# Берем первый результат
|
||||
result = results[0]
|
||||
# Используем Original_Name если Name пустое
|
||||
|
|
@ -374,6 +409,16 @@ async def search_torrent_by_id(torrent_id: str) -> dict:
|
|||
hash_value = result.get('Hash', '')
|
||||
torrent_title = torrent_name
|
||||
|
||||
# Если хэша нет в Hash, пытаемся извлечь из Magnet ссылки
|
||||
if not hash_value:
|
||||
original_magnet = result.get('Magnet', '')
|
||||
if original_magnet:
|
||||
# Извлекаем хэш из magnet ссылки
|
||||
hash_match = re.search(r'urn:btih:([a-fA-F0-9]{40}|[a-zA-Z0-9]{32})', original_magnet)
|
||||
if hash_match:
|
||||
hash_value = hash_match.group(1)
|
||||
print(f"Extracted hash from magnet link: {hash_value[:10]}...")
|
||||
|
||||
if hash_value:
|
||||
# Генерируем чистую magnet-ссылку с публичными трекерами
|
||||
magnet = generate_clean_magnet(hash_value, torrent_title)
|
||||
|
|
@ -381,8 +426,9 @@ async def search_torrent_by_id(torrent_id: str) -> dict:
|
|||
else:
|
||||
# Fallback на оригинальную magnet-ссылку если нет хэша
|
||||
magnet = result.get('Magnet', '')
|
||||
if magnet and not magnet.startswith('magnet:'):
|
||||
magnet = f"magnet:?xt=urn:btih:{hash_value}"
|
||||
if not magnet or not magnet.startswith('magnet:'):
|
||||
print(f"Warning: No hash found and no valid magnet link. Hash: {hash_value}, Magnet: {result.get('Magnet', 'None')[:50]}")
|
||||
magnet = ""
|
||||
|
||||
# Парсим результат в стандартный формат
|
||||
torrent = {
|
||||
|
|
@ -486,6 +532,25 @@ def parse_torrent_result(result: dict, movie_title: str, year: str = None) -> di
|
|||
elif 'nnmclub.to' in torrent_url:
|
||||
provider = 'nonameclub'
|
||||
|
||||
# Извлекаем ID из разных возможных полей
|
||||
torrent_id = (
|
||||
result.get('Id') or
|
||||
result.get('id') or
|
||||
result.get('ID') or
|
||||
result.get('Hash', '')[:8] or # Используем первые 8 символов хэша как fallback
|
||||
''
|
||||
)
|
||||
|
||||
# Если ID не найден, пытаемся извлечь из URL
|
||||
if not torrent_id and result.get('Url'):
|
||||
url_id_match = re.search(r'/(\d+)/?$', result.get('Url', ''))
|
||||
if url_id_match:
|
||||
torrent_id = url_id_match.group(1)
|
||||
|
||||
# Если все еще нет ID, используем Hash полностью
|
||||
if not torrent_id:
|
||||
torrent_id = result.get('Hash', '')
|
||||
|
||||
return {
|
||||
"title": result['Name'],
|
||||
"size_bytes": size_bytes,
|
||||
|
|
@ -499,7 +564,7 @@ def parse_torrent_result(result: dict, movie_title: str, year: str = None) -> di
|
|||
"category": result.get('Category', ''),
|
||||
"date": result.get('Date', ''),
|
||||
"provider": provider,
|
||||
"id": result.get('Id', '')
|
||||
"id": torrent_id
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Error parsing torrent result: {e}")
|
||||
|
|
@ -911,6 +976,60 @@ async def api_search_torrent_by_id(torrent_id: str):
|
|||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Torrent ID search error: {str(e)}")
|
||||
|
||||
@app.get("/api/check-qbittorrent")
|
||||
async def check_qbittorrent_connection():
|
||||
"""Проверка доступности qBittorrent"""
|
||||
try:
|
||||
qb_username = os.getenv("QBITTORRENT_USERNAME", "admin")
|
||||
qb_password = os.getenv("QBITTORRENT_PASSWORD", "vrubel07")
|
||||
qb_host = os.getenv("QBITTORRENT_HOST", "localhost")
|
||||
qb_port = os.getenv("QBITTORRENT_PORT", "8082")
|
||||
qb_url = f"http://{qb_host}:{qb_port}"
|
||||
|
||||
async with httpx.AsyncClient(timeout=10.0) as client:
|
||||
try:
|
||||
auth_response = await client.post(
|
||||
f"{qb_url}/api/v2/auth/login",
|
||||
data={"username": qb_username, "password": qb_password}
|
||||
)
|
||||
|
||||
if auth_response.status_code == 200 and auth_response.text.strip() == "Ok.":
|
||||
# Получаем версию qBittorrent
|
||||
version_response = await client.get(f"{qb_url}/api/v2/app/version")
|
||||
version = version_response.text if version_response.status_code == 200 else "Unknown"
|
||||
|
||||
# Получаем список торрентов
|
||||
torrents_response = await client.get(f"{qb_url}/api/v2/torrents/info")
|
||||
torrents_count = 0
|
||||
if torrents_response.status_code == 200:
|
||||
torrents_count = len(torrents_response.json())
|
||||
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "qBittorrent доступен",
|
||||
"url": qb_url,
|
||||
"version": version,
|
||||
"torrents_count": torrents_count
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Ошибка аутентификации: {auth_response.text}",
|
||||
"url": qb_url
|
||||
}
|
||||
except httpx.ConnectError as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Не удалось подключиться к qBittorrent: {str(e)}",
|
||||
"url": qb_url,
|
||||
"hint": "Проверьте, что qBittorrent запущен и доступен по адресу выше. Если используется VPN, убедитесь, что он настроен правильно."
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"status": "error",
|
||||
"message": f"Ошибка при проверке: {str(e)}"
|
||||
}
|
||||
|
||||
@app.get("/torrents/{movie_title}", response_class=HTMLResponse)
|
||||
async def torrents_page(request: Request, movie_title: str, year: str = None):
|
||||
"""Страница с результатами поиска торрентов"""
|
||||
|
|
@ -940,10 +1059,16 @@ async def add_torrent_to_client(torrent_id: str = Form(...)):
|
|||
try:
|
||||
print(f"Attempting to add torrent with ID: {torrent_id}")
|
||||
|
||||
if not torrent_id or torrent_id.strip() == '':
|
||||
return {"status": "error", "message": "ID торрента не указан"}
|
||||
|
||||
# Получаем информацию о торренте по ID
|
||||
torrent_info = await search_torrent_by_id(torrent_id)
|
||||
if not torrent_info:
|
||||
return {"status": "error", "message": f"Торрент с ID {torrent_id} не найден"}
|
||||
print(f"Torrent info is None for ID: {torrent_id}")
|
||||
return {"status": "error", "message": f"Торрент с ID {torrent_id} не найден. Проверьте логи для деталей."}
|
||||
|
||||
print(f"Found torrent info: title={torrent_info.get('title', 'Unknown')[:50]}, magnet={'present' if torrent_info.get('magnet') else 'missing'}, torrent_url={'present' if torrent_info.get('torrent_url') else 'missing'}")
|
||||
|
||||
# Получаем учетные данные из переменных окружения
|
||||
qb_username = os.getenv("QBITTORRENT_USERNAME", "admin")
|
||||
|
|
@ -965,8 +1090,10 @@ async def add_torrent_to_client(torrent_id: str = Form(...)):
|
|||
print("Successfully authenticated with qBittorrent")
|
||||
|
||||
# Пробуем сначала добавить через magnet-ссылку (более надежно)
|
||||
magnet = torrent_info.get('magnet')
|
||||
if magnet:
|
||||
magnet = torrent_info.get('magnet', '').strip()
|
||||
|
||||
# Проверяем, что magnet ссылка валидна (содержит хэш)
|
||||
if magnet and 'urn:btih:' in magnet and len(magnet) > 20:
|
||||
print(f"Trying to add via magnet link: {magnet[:100]}...")
|
||||
add_response = await client.post(
|
||||
f"{qb_url}/api/v2/torrents/add",
|
||||
|
|
@ -979,20 +1106,29 @@ async def add_torrent_to_client(torrent_id: str = Form(...)):
|
|||
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-ссылку!",
|
||||
"torrent_hash": torrent.get('hash'),
|
||||
"torrent_name": torrent_info.get('title', 'Unknown')
|
||||
}
|
||||
torrent_hash = torrent_info.get('hash', '').upper()
|
||||
|
||||
# Если хэш не был в torrent_info, извлекаем из magnet ссылки
|
||||
if not torrent_hash and magnet:
|
||||
hash_match = re.search(r'urn:btih:([a-fA-F0-9]{40}|[a-zA-Z0-9]{32})', magnet)
|
||||
if hash_match:
|
||||
torrent_hash = hash_match.group(1).upper()
|
||||
|
||||
if torrent_hash:
|
||||
torrents_response = await client.get(f"{qb_url}/api/v2/torrents/info")
|
||||
if torrents_response.status_code == 200:
|
||||
torrents = torrents_response.json()
|
||||
# Ищем торрент по хэшу
|
||||
for torrent in torrents:
|
||||
if torrent.get('hash', '').upper() == torrent_hash:
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через magnet-ссылку!",
|
||||
"torrent_hash": torrent.get('hash'),
|
||||
"torrent_name": torrent_info.get('title', 'Unknown')
|
||||
}
|
||||
|
||||
# Если не нашли по хэшу, но ответ был Ok, считаем успешным
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через magnet-ссылку!",
|
||||
|
|
@ -1000,10 +1136,16 @@ async def add_torrent_to_client(torrent_id: str = Form(...)):
|
|||
"torrent_name": torrent_info.get('title', 'Unknown')
|
||||
}
|
||||
else:
|
||||
print(f"Magnet link failed, trying .torrent file...")
|
||||
print(f"Magnet link failed (status: {add_response.status_code}, response: {add_response.text}), trying .torrent file...")
|
||||
else:
|
||||
print(f"Magnet link invalid or empty: '{magnet[:50] if magnet else 'None'}...', trying .torrent file...")
|
||||
|
||||
# Если magnet не сработал, пробуем .torrent файл
|
||||
torrent_url = torrent_info.get('torrent_url')
|
||||
torrent_url = torrent_info.get('torrent_url', '')
|
||||
if not torrent_url:
|
||||
# Пробуем также поле url
|
||||
torrent_url = torrent_info.get('url', '')
|
||||
|
||||
if torrent_url:
|
||||
print(f"Trying to add via .torrent file: {torrent_url}")
|
||||
add_response = await client.post(
|
||||
|
|
@ -1015,16 +1157,47 @@ async def add_torrent_to_client(torrent_id: str = Form(...)):
|
|||
print(f"Add via .torrent response text: {add_response.text}")
|
||||
|
||||
if add_response.status_code == 200 and add_response.text.strip() == "Ok.":
|
||||
# Проверяем, что торрент действительно добавился
|
||||
await asyncio.sleep(2) # Ждем немного
|
||||
|
||||
# Пытаемся найти торрент в списке по названию (так как хэш может быть неизвестен)
|
||||
torrent_title = torrent_info.get('title', '').lower()
|
||||
torrents_response = await client.get(f"{qb_url}/api/v2/torrents/info")
|
||||
|
||||
if torrents_response.status_code == 200:
|
||||
torrents = torrents_response.json()
|
||||
# Ищем торрент по названию (первые 20 символов для точного совпадения)
|
||||
added_torrent = None
|
||||
for torrent in torrents:
|
||||
torrent_name = torrent.get('name', '').lower()
|
||||
# Проверяем совпадение по началу названия
|
||||
if torrent_title and torrent_name:
|
||||
if torrent_title[:20] in torrent_name or torrent_name[:20] in torrent_title:
|
||||
added_torrent = torrent
|
||||
break
|
||||
|
||||
if added_torrent:
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' успешно добавлен в qBittorrent через .torrent файл!",
|
||||
"torrent_hash": added_torrent.get('hash', ''),
|
||||
"torrent_name": added_torrent.get('name', torrent_info.get('title', 'Unknown'))
|
||||
}
|
||||
|
||||
# Если не нашли по названию, но ответ был Ok, считаем успешным
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через .torrent файл!",
|
||||
"message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через .torrent файл (проверьте список торрентов в qBittorrent)!",
|
||||
"torrent_hash": torrent_info.get('hash', ''),
|
||||
"torrent_name": torrent_info.get('title', 'Unknown')
|
||||
}
|
||||
else:
|
||||
return {"status": "error", "message": f"Ошибка добавления торрента (HTTP {add_response.status_code}): {add_response.text}"}
|
||||
error_msg = add_response.text.strip()
|
||||
if "Fails" in error_msg or "Bad Request" in error_msg:
|
||||
return {"status": "error", "message": f"qBittorrent отклонил торрент. Возможно, проблема с VPN или файл недоступен. Ответ: {error_msg}"}
|
||||
return {"status": "error", "message": f"Ошибка добавления торрента (HTTP {add_response.status_code}): {error_msg}"}
|
||||
else:
|
||||
return {"status": "error", "message": f"Не найдена ни magnet-ссылка, ни .torrent файл для торрента {torrent_id}"}
|
||||
return {"status": "error", "message": f"Не найдена ни валидная magnet-ссылка, ни .torrent файл для торрента {torrent_id}. Проверьте, что торрент доступен."}
|
||||
|
||||
except httpx.ConnectError as e:
|
||||
print(f"Connection error: {e}")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ services:
|
|||
- TORAPI_URL=http://torrent-api:8000
|
||||
- TORRENT_SEARCH_URL=http://host.docker.internal:8443
|
||||
- TORRENT_ADD_URL=http://host.docker.internal:8088
|
||||
- QBITTORRENT_USERNAME=admin
|
||||
- QBITTORRENT_USERNAME=vrubelroman
|
||||
- QBITTORRENT_PASSWORD=vrubel07
|
||||
- QBITTORRENT_HOST=host.docker.internal
|
||||
- QBITTORRENT_PORT=8082
|
||||
|
|
@ -39,7 +39,7 @@ services:
|
|||
image: lifailon/torapi:latest
|
||||
container_name: TorAPI-qBittorrent
|
||||
environment:
|
||||
- USERNAME=admin
|
||||
- USERNAME=vrubelroman
|
||||
- PASSWORD=vrubel07
|
||||
- PROXY_ADDRESS=host.docker.internal
|
||||
- PROXY_PORT=8082
|
||||
|
|
@ -62,7 +62,7 @@ services:
|
|||
- TELEGRAM_BOT_TOKEN=7662650066:AAFgsfYJNYgpcSHaSe6fspsjqmhMkOBT1s4
|
||||
- TORRENT_SEARCH_URL=http://host.docker.internal:8443
|
||||
- TORRENT_ADD_URL=http://host.docker.internal:8088
|
||||
- QBITTORRENT_USERNAME=admin
|
||||
- QBITTORRENT_USERNAME=vrubelroman
|
||||
- QBITTORRENT_PASSWORD=vrubel07
|
||||
- QBITTORRENT_HOST=host.docker.internal
|
||||
- QBITTORRENT_PORT=8082
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue