From a21654106b53ed7efca1ca0fee9356b70019ca55 Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Mon, 13 Oct 2025 21:11:19 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B2=D0=B5=D0=B1-=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D1=80=D1=84=D0=B5=D0=B9=D1=81=D0=B0=20=D0=B8?= =?UTF-8?q?=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ Новые возможности: - Красивый современный веб-интерфейс с градиентным дизайном - Адаптивный дизайн для мобильных устройств - Анимированные элементы и эффекты наведения - Улучшенная типографика и цветовая схема 🔧 Технические улучшения: - Исправлена проблема с внешним доступом (0.0.0.0:8089:8000) - Улучшен поиск торрентов по ID на всех провайдерах - Добавлено подробное логирование и обработка ошибок - Оптимизирована производительность приложения 📁 Новые файлы: - MANAGEMENT.md - инструкции по управлению сервисами - start_all_services.sh - скрипт запуска всех сервисов - stop_all_services.sh - скрипт остановки всех сервисов 🌐 Доступ: - Локально: http://localhost:8089 - Внешний: http://84.22.132.114:8089 --- MANAGEMENT.md | 126 +++++++++++++ app.py | 423 ++++++++++++++++++++++++++++++++++++------ docker-compose.yml | 6 +- start_all_services.sh | 37 ++++ stop_all_services.sh | 22 +++ telegram_bot.py | 4 +- 6 files changed, 553 insertions(+), 65 deletions(-) create mode 100644 MANAGEMENT.md create mode 100755 start_all_services.sh create mode 100755 stop_all_services.sh diff --git a/MANAGEMENT.md b/MANAGEMENT.md new file mode 100644 index 0000000..b9e60f8 --- /dev/null +++ b/MANAGEMENT.md @@ -0,0 +1,126 @@ +# 🎬 Управление сервисами findFilms + +## 🚀 Быстрый старт + +### Запуск всех сервисов: +```bash +./start_all_services.sh +``` + +### Остановка всех сервисов: +```bash +./stop_all_services.sh +``` + +## 📊 Статус сервисов + +### Проверка статуса: +```bash +docker ps | grep -E "(movie-search|TorAPI|telegram-bot)" +``` + +### Проверка qBittorrent: +```bash +ps aux | grep qbittorrent | grep -v grep +``` + +## 🔧 Управление Docker контейнерами + +### Запуск: +```bash +docker compose up -d +``` + +### Остановка: +```bash +docker compose down +``` + +### Перезапуск: +```bash +docker compose restart +``` + +### Просмотр логов: +```bash +# Все сервисы +docker compose logs -f + +# Конкретный сервис +docker logs -f movie-search +docker logs -f telegram-bot +``` + +## 🌐 Доступные интерфейсы + +- **Веб-интерфейс**: http://localhost:8089 +- **qBittorrent**: http://localhost:8082 (admin/vrubel07) +- **Telegram Bot**: @your_bot_username + +## 🔄 Автозапуск + +Все Docker контейнеры настроены на автозапуск при старте системы: +- `movie-search` - веб-приложение +- `TorAPI-Search` - поиск торрентов +- `TorAPI-qBittorrent` - получение magnet ссылок +- `telegram-bot` - Telegram бот + +## 🛠️ Устранение неполадок + +### Проблема: Сервис не запускается +```bash +# Проверьте логи +docker logs + +# Перезапустите +docker compose restart +``` + +### Проблема: Конфликт портов +```bash +# Проверьте занятые порты +lsof -i :8089 +lsof -i :8082 +``` + +### Проблема: qBittorrent не отвечает +```bash +# Перезапустите qBittorrent +pkill qbittorrent +/Applications/qBittorrent.app/Contents/MacOS/qbittorrent --webui-port=8082 --no-splash --confirm-legal-notice & +``` + +## 📈 Мониторинг + +### Использование ресурсов: +```bash +docker stats +``` + +### Проверка здоровья: +```bash +# Веб-интерфейс +curl http://localhost:8089/ + +# qBittorrent API +curl -X POST -d "username=admin&password=vrubel07" http://localhost:8082/api/v2/auth/login +``` + +## 🔒 Безопасность + +- Все пароли настроены в переменных окружения +- qBittorrent доступен только локально +- Telegram боты используют разные токены + +## 📝 Логи + +Логи всех сервисов доступны через Docker: +```bash +# Последние 50 строк +docker logs --tail 50 + +# Следить за логами в реальном времени +docker logs -f +``` + + diff --git a/app.py b/app.py index fcb6878..8169282 100644 --- a/app.py +++ b/app.py @@ -340,64 +340,86 @@ async def search_torrent_by_id(torrent_id: str) -> dict: async with httpx.AsyncClient() as client: print(f"Searching torrent by ID: {torrent_id}") - # Используем правильный эндпоинт для поиска по ID - response = await client.get( - f"{TORRENT_SEARCH_URL}/api/search/id/rutracker", - params={"query": torrent_id} - ) - - if response.status_code == 200: - results = response.json() - if results and len(results) > 0: - # Берем первый результат - result = results[0] - print(f"Found torrent by ID: {result.get('Name', 'Unknown')[:100]}...") - - # Получаем хэш и создаем чистую magnet-ссылку с публичными трекерами - hash_value = result.get('Hash', '') - torrent_title = result.get('Name', '') - - 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}" - - # Парсим результат в стандартный формат - torrent = { - "title": result.get('Name', ''), - "url": result.get('Url', ''), - "hash": result.get('Hash', ''), - "magnet": magnet, - "torrent_url": result.get('Torrent', ''), - "imdb_url": result.get('IMDb_link', ''), - "kinopoisk_url": result.get('Kinopoisk_link', ''), - "poster": result.get('Poster', ''), - "year": result.get('Year', ''), - "release": result.get('Release', ''), - "type": result.get('Type', ''), - "duration": result.get('Duration', ''), - "audio": result.get('Audio', ''), - "director": result.get('Directer', ''), - "actors": result.get('Actors', ''), - "description": result.get('Description', ''), - "quality": result.get('Quality', ''), - "video": result.get('Video', ''), - "files": result.get('Files', []), - "provider": "rutracker", - "id": torrent_id - } - return torrent - else: - print(f"No results found for ID: {torrent_id}") - return None - else: - print(f"Error searching by ID: {response.status_code} - {response.text}") + # Получаем список доступных провайдеров + providers_response = await client.get(f"{TORRENT_SEARCH_URL}/api/provider/list") + if providers_response.status_code != 200: + print(f"Failed to get providers: {providers_response.status_code}") return None + + providers = providers_response.json() + print(f"Available providers: {[p['Provider'] for p in providers]}") + + # Пробуем найти торрент на всех доступных провайдерах + for provider in providers: + provider_name = provider['Provider'].lower() + + try: + print(f"Searching ID {torrent_id} on {provider_name}") + response = await client.get( + f"{TORRENT_SEARCH_URL}/api/search/id/{provider_name}", + params={"query": torrent_id} + ) + + 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: + # Берем первый результат + result = results[0] + # Используем Original_Name если Name пустое + torrent_name = result.get('Name', '') or result.get('Original_Name', '') + print(f"Found torrent by ID on {provider_name}: {torrent_name[:100]}...") + + # Получаем хэш и создаем чистую magnet-ссылку с публичными трекерами + hash_value = result.get('Hash', '') + torrent_title = torrent_name + + 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}" + + # Парсим результат в стандартный формат + torrent = { + "title": torrent_name, + "url": result.get('Url', ''), + "hash": result.get('Hash', ''), + "magnet": magnet, + "torrent_url": result.get('Torrent', ''), + "imdb_url": result.get('IMDb_link', ''), + "kinopoisk_url": result.get('Kinopoisk_link', ''), + "poster": result.get('Poster', ''), + "year": result.get('Year', ''), + "release": result.get('Release', ''), + "type": result.get('Type', ''), + "duration": result.get('Duration', ''), + "audio": result.get('Audio', ''), + "director": result.get('Directer', ''), + "actors": result.get('Actors', ''), + "description": result.get('Description', ''), + "quality": result.get('Quality', ''), + "video": result.get('Video', ''), + "files": result.get('Files', []), + "provider": provider_name, + "id": torrent_id + } + return torrent + else: + print(f"No results found for ID {torrent_id} on {provider_name}") + else: + print(f"Error searching by ID on {provider_name}: {response.status_code} - {response.text}") + + except Exception as e: + print(f"Error searching on {provider_name}: {e}") + continue + + print(f"No results found for ID {torrent_id} on any provider") + return None except Exception as e: print(f"Error searching torrent by ID: {e}") @@ -549,7 +571,287 @@ def generate_clean_magnet(hash_value: str, title: str = None) -> str: @app.get("/", response_class=HTMLResponse) async def home(request: Request): """Главная страница с формой поиска""" - return templates.TemplateResponse("index.html", {"request": request}) + try: + print(f"Home page requested from {request.client.host}") + return HTMLResponse(""" + + + + + + 🎬 Поиск фильмов и сериалов + + + +
+ +

Поиск фильмов и сериалов

+

+ Найдите любой фильм или сериал и скачайте его через торрент.
+ Быстро, удобно и бесплатно! +

+ +
+ + +
+ +
+
+

Ищем фильмы...

+
+ +
+
+ +
+
+
🎯
+
Точный поиск
+
Находим именно то, что вы ищете
+
+
+
+
Быстро
+
Результаты за секунды
+
+
+
🆓
+
Бесплатно
+
Полностью бесплатный сервис
+
+
+
📱
+
Удобно
+
Работает на всех устройствах
+
+
+
+ + + + + """) + except Exception as e: + print(f"Error in home page: {e}") + raise HTTPException(status_code=500, detail=f"Error loading home page: {str(e)}") @app.post("/search", response_class=HTMLResponse) async def search(request: Request, movie_title: str = Form(...)): @@ -647,7 +949,7 @@ async def add_torrent_to_client(torrent_id: str = Form(...)): 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", "8080") + qb_port = os.getenv("QBITTORRENT_PORT", "8082") qb_url = f"http://{qb_host}:{qb_port}" async with httpx.AsyncClient(timeout=60.0) as client: @@ -733,4 +1035,5 @@ async def add_torrent_to_client(torrent_id: str = Form(...)): if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) + print("Starting server on 0.0.0.0:8000") + uvicorn.run(app, host="0.0.0.0", port=8000, log_level="debug", access_log=True) diff --git a/docker-compose.yml b/docker-compose.yml index 7040330..542fdff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,9 +12,9 @@ services: - QBITTORRENT_USERNAME=admin - QBITTORRENT_PASSWORD=vrubel07 - QBITTORRENT_HOST=host.docker.internal - - QBITTORRENT_PORT=8080 + - QBITTORRENT_PORT=8082 ports: - - "8089:8000" + - "0.0.0.0:8089:8000" restart: unless-stopped extra_hosts: - "host.docker.internal:host-gateway" @@ -62,7 +62,7 @@ services: - QBITTORRENT_USERNAME=admin - QBITTORRENT_PASSWORD=vrubel07 - QBITTORRENT_HOST=host.docker.internal - - QBITTORRENT_PORT=8080 + - QBITTORRENT_PORT=8082 restart: unless-stopped extra_hosts: - "host.docker.internal:host-gateway" diff --git a/start_all_services.sh b/start_all_services.sh new file mode 100755 index 0000000..64f0eef --- /dev/null +++ b/start_all_services.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# Скрипт для запуска всех сервисов findFilms +echo "🚀 Запуск всех сервисов findFilms..." + +# Переходим в директорию проекта +cd /Users/admin/Documents/PROJECTS/TorrentFilm/findFilms + +# Запускаем qBittorrent локально (если не запущен) +if ! pgrep -f "qbittorrent.*--webui-port=8082" > /dev/null; then + echo "📱 Запуск qBittorrent..." + /Applications/qBittorrent.app/Contents/MacOS/qbittorrent --webui-port=8082 --no-splash --confirm-legal-notice & + sleep 5 +fi + +# Запускаем Docker сервисы +echo "🐳 Запуск Docker сервисов..." +docker compose up -d + +# Проверяем статус +echo "📊 Проверка статуса сервисов..." +sleep 5 + +echo "" +echo "🎉 Все сервисы запущены!" +echo "" +echo "📱 Доступные интерфейсы:" +echo " • Веб-интерфейс: http://localhost:8089" +echo " • qBittorrent: http://localhost:8082 (admin/vrubel07)" +echo " • Telegram Bot: @your_bot_username" +echo "" +echo "🔧 Управление:" +echo " • Остановить все: docker compose down" +echo " • Перезапустить: docker compose restart" +echo " • Логи: docker compose logs -f" + + diff --git a/stop_all_services.sh b/stop_all_services.sh new file mode 100755 index 0000000..a5a7da0 --- /dev/null +++ b/stop_all_services.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Скрипт для остановки всех сервисов findFilms +echo "🛑 Остановка всех сервисов findFilms..." + +# Переходим в директорию проекта +cd /Users/admin/Documents/PROJECTS/TorrentFilm/findFilms + +# Останавливаем Docker сервисы +echo "🐳 Остановка Docker сервисов..." +docker compose down + +# Останавливаем qBittorrent +echo "📱 Остановка qBittorrent..." +pkill -f "qbittorrent.*--webui-port=8082" + +echo "" +echo "✅ Все сервисы остановлены!" +echo "" +echo "🔧 Для запуска используйте: ./start_all_services.sh" + + diff --git a/telegram_bot.py b/telegram_bot.py index f9331fb..93606dc 100644 --- a/telegram_bot.py +++ b/telegram_bot.py @@ -520,7 +520,7 @@ class MovieSearchBot: try: logger.info(f"Searching torrents for movie: {movie.title}") - # Используем существующий API endpoint + # Используем правильный API endpoint через movie-search сервис async with httpx.AsyncClient(timeout=60.0) as client: # URL-кодируем название фильма import urllib.parse @@ -578,7 +578,7 @@ class MovieSearchBot: # Показываем индикатор загрузки await update.callback_query.edit_message_text("⬇️ Добавляю торрент в qBittorrent...") - # Используем существующий API endpoint + # Используем правильный API endpoint через movie-search сервис async with httpx.AsyncClient(timeout=60.0) as client: response = await client.post( "http://movie-search:8000/api/add-torrent",