diff --git a/app.py b/app.py
index 989c682..fcb6878 100644
--- a/app.py
+++ b/app.py
@@ -684,9 +684,19 @@ async def add_torrent_to_client(torrent_id: str = Form(...)):
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-ссылку!",
+ "torrent_hash": torrent.get('hash'),
+ "torrent_name": torrent_info.get('title', 'Unknown')
+ }
- 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-ссылку!",
+ "torrent_hash": torrent_hash,
+ "torrent_name": torrent_info.get('title', 'Unknown')
+ }
else:
print(f"Magnet link failed, trying .torrent file...")
@@ -703,7 +713,12 @@ 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.":
- return {"status": "success", "message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через .torrent файл!"}
+ return {
+ "status": "success",
+ "message": f"Торрент '{torrent_info.get('title', 'Unknown')[:50]}...' добавлен в qBittorrent через .torrent файл!",
+ "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}"}
else:
diff --git a/telegram_bot.py b/telegram_bot.py
index e9190cc..f9331fb 100644
--- a/telegram_bot.py
+++ b/telegram_bot.py
@@ -8,6 +8,7 @@ import os
import asyncio
import httpx
import logging
+import json
from typing import Dict, List, Optional
from dataclasses import dataclass
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup, InputMediaPhoto
@@ -65,6 +66,7 @@ class MovieSearchBot:
def __init__(self):
self.application = Application.builder().token(TELEGRAM_BOT_TOKEN).build()
self.user_states = {} # Состояния пользователей
+ self.download_monitor = None # Мониторинг загрузок
self.setup_handlers()
def setup_handlers(self):
@@ -587,6 +589,20 @@ class MovieSearchBot:
data = response.json()
if data.get("status") == "success":
message = f"✅ {data.get('message', 'Торрент успешно добавлен!')}"
+
+ # Добавляем в мониторинг загрузок
+ if self.download_monitor and data.get("torrent_hash"):
+ user_id = update.effective_user.id
+ movie = context.user_data.get('selected_movie')
+ torrent_name = data.get("torrent_name", "Unknown")
+
+ if movie:
+ self.download_monitor.add_download(
+ torrent_hash=data.get("torrent_hash"),
+ user_id=user_id,
+ movie_title=movie.title,
+ torrent_name=torrent_name
+ )
else:
message = f"❌ {data.get('message', 'Ошибка при добавлении торрента')}"
else:
@@ -613,8 +629,157 @@ class MovieSearchBot:
def run(self):
"""Запуск бота"""
logger.info("Starting Movie Search Bot...")
+
+ # Инициализируем мониторинг загрузок
+ self.download_monitor = DownloadMonitor(self)
+
+ # Запускаем мониторинг в фоновом режиме
+ def start_monitoring():
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+ loop.run_until_complete(self.download_monitor.start_monitoring())
+
+ import threading
+ monitor_thread = threading.Thread(target=start_monitoring, daemon=True)
+ monitor_thread.start()
+
self.application.run_polling()
+class DownloadMonitor:
+ """Мониторинг загрузок в qBittorrent и отправка уведомлений"""
+
+ def __init__(self, bot_instance: MovieSearchBot):
+ self.bot = bot_instance
+ self.active_downloads = {} # {torrent_hash: {user_id, movie_title, torrent_name}}
+ self.qbittorrent_url = f"http://{QBITTORRENT_HOST}:{QBITTORRENT_PORT}"
+ self.qbittorrent_username = QBITTORRENT_USERNAME
+ self.qbittorrent_password = QBITTORRENT_PASSWORD
+ self.session_cookie = None
+
+ async def authenticate_qbittorrent(self):
+ """Аутентификация в qBittorrent"""
+ try:
+ async with httpx.AsyncClient(timeout=30.0) as client:
+ response = await client.post(
+ f"{self.qbittorrent_url}/api/v2/auth/login",
+ data={
+ "username": self.qbittorrent_username,
+ "password": self.qbittorrent_password
+ }
+ )
+ if response.status_code == 200 and response.text == "Ok.":
+ # Сохраняем cookie для последующих запросов
+ self.session_cookie = response.cookies.get('SID')
+ logger.info("qBittorrent authentication successful")
+ return True
+ else:
+ logger.error(f"qBittorrent authentication failed: {response.status_code} - {response.text}")
+ return False
+ except Exception as e:
+ logger.error(f"Error authenticating with qBittorrent: {e}")
+ return False
+
+ async def get_torrents_info(self):
+ """Получение информации о торрентах"""
+ try:
+ if not self.session_cookie:
+ await self.authenticate_qbittorrent()
+
+ async with httpx.AsyncClient(timeout=30.0) as client:
+ response = await client.get(
+ f"{self.qbittorrent_url}/api/v2/torrents/info",
+ cookies={"SID": self.session_cookie}
+ )
+ if response.status_code == 200:
+ return response.json()
+ else:
+ logger.error(f"Failed to get torrents info: {response.status_code}")
+ return []
+ except Exception as e:
+ logger.error(f"Error getting torrents info: {e}")
+ return []
+
+ async def check_downloads(self):
+ """Проверка статуса загрузок и отправка уведомлений"""
+ try:
+ torrents = await self.get_torrents_info()
+ if not torrents:
+ return
+
+ for torrent in torrents:
+ torrent_hash = torrent.get('hash')
+ torrent_name = torrent.get('name', 'Unknown')
+ state = torrent.get('state')
+ progress = torrent.get('progress', 0)
+
+ # Проверяем, отслеживаем ли мы этот торрент
+ if torrent_hash in self.active_downloads:
+ download_info = self.active_downloads[torrent_hash]
+ user_id = download_info['user_id']
+ movie_title = download_info['movie_title']
+
+ # Если загрузка завершена (state == 'uploading' или progress == 1.0)
+ if state in ['uploading', 'stalledUP'] or progress >= 1.0:
+ try:
+ # Отправляем уведомление
+ await self.bot.application.bot.send_message(
+ chat_id=user_id,
+ text=f"🎉 Загрузка завершена!\n\n"
+ f"🎬 Фильм: {movie_title}\n"
+ f"📁 Торрент: {torrent_name}\n"
+ f"✅ Статус: Готов к просмотру!",
+ parse_mode=ParseMode.HTML
+ )
+
+ # Удаляем из отслеживания
+ del self.active_downloads[torrent_hash]
+ logger.info(f"Download completed notification sent for {movie_title}")
+
+ except Exception as e:
+ logger.error(f"Error sending completion notification: {e}")
+
+ # Если загрузка остановлена с ошибкой
+ elif state in ['error', 'missingFiles']:
+ try:
+ await self.bot.application.bot.send_message(
+ chat_id=user_id,
+ text=f"❌ Ошибка загрузки\n\n"
+ f"🎬 Фильм: {movie_title}\n"
+ f"📁 Торрент: {torrent_name}\n"
+ f"⚠️ Статус: {state}",
+ parse_mode=ParseMode.HTML
+ )
+
+ # Удаляем из отслеживания
+ del self.active_downloads[torrent_hash]
+ logger.info(f"Download error notification sent for {movie_title}")
+
+ except Exception as e:
+ logger.error(f"Error sending error notification: {e}")
+
+ except Exception as e:
+ logger.error(f"Error checking downloads: {e}")
+
+ def add_download(self, torrent_hash: str, user_id: int, movie_title: str, torrent_name: str):
+ """Добавление торрента в отслеживание"""
+ self.active_downloads[torrent_hash] = {
+ 'user_id': user_id,
+ 'movie_title': movie_title,
+ 'torrent_name': torrent_name
+ }
+ logger.info(f"Added download to monitoring: {movie_title} for user {user_id}")
+
+ async def start_monitoring(self):
+ """Запуск мониторинга загрузок"""
+ logger.info("Starting download monitoring...")
+ while True:
+ try:
+ await self.check_downloads()
+ await asyncio.sleep(30) # Проверяем каждые 30 секунд
+ except Exception as e:
+ logger.error(f"Error in monitoring loop: {e}")
+ await asyncio.sleep(60) # При ошибке ждем минуту
+
def main():
"""Главная функция"""
bot = MovieSearchBot()