🎨 Обновление веб-интерфейса и исправления
✨ Новые возможности: - Красивый современный веб-интерфейс с градиентным дизайном - Адаптивный дизайн для мобильных устройств - Анимированные элементы и эффекты наведения - Улучшенная типографика и цветовая схема 🔧 Технические улучшения: - Исправлена проблема с внешним доступом (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
This commit is contained in:
parent
90ad38bca7
commit
a21654106b
6 changed files with 553 additions and 65 deletions
126
MANAGEMENT.md
Normal file
126
MANAGEMENT.md
Normal file
|
|
@ -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 <container_name>
|
||||
|
||||
# Перезапустите
|
||||
docker compose restart <service_name>
|
||||
```
|
||||
|
||||
### Проблема: Конфликт портов
|
||||
```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 <container_name>
|
||||
|
||||
# Следить за логами в реальном времени
|
||||
docker logs -f <container_name>
|
||||
```
|
||||
|
||||
|
||||
329
app.py
329
app.py
|
|
@ -340,22 +340,39 @@ 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
|
||||
# Получаем список доступных провайдеров
|
||||
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/rutracker",
|
||||
f"{TORRENT_SEARCH_URL}/api/search/id/{provider_name}",
|
||||
params={"query": torrent_id}
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
results = response.json()
|
||||
if results and len(results) > 0:
|
||||
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]
|
||||
print(f"Found torrent by ID: {result.get('Name', 'Unknown')[:100]}...")
|
||||
# Используем 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 = result.get('Name', '')
|
||||
torrent_title = torrent_name
|
||||
|
||||
if hash_value:
|
||||
# Генерируем чистую magnet-ссылку с публичными трекерами
|
||||
|
|
@ -369,7 +386,7 @@ async def search_torrent_by_id(torrent_id: str) -> dict:
|
|||
|
||||
# Парсим результат в стандартный формат
|
||||
torrent = {
|
||||
"title": result.get('Name', ''),
|
||||
"title": torrent_name,
|
||||
"url": result.get('Url', ''),
|
||||
"hash": result.get('Hash', ''),
|
||||
"magnet": magnet,
|
||||
|
|
@ -388,15 +405,20 @@ async def search_torrent_by_id(torrent_id: str) -> dict:
|
|||
"quality": result.get('Quality', ''),
|
||||
"video": result.get('Video', ''),
|
||||
"files": result.get('Files', []),
|
||||
"provider": "rutracker",
|
||||
"provider": provider_name,
|
||||
"id": torrent_id
|
||||
}
|
||||
return torrent
|
||||
else:
|
||||
print(f"No results found for ID: {torrent_id}")
|
||||
return None
|
||||
print(f"No results found for ID {torrent_id} on {provider_name}")
|
||||
else:
|
||||
print(f"Error searching by ID: {response.status_code} - {response.text}")
|
||||
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:
|
||||
|
|
@ -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("""
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>🎬 Поиск фильмов и сериалов</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
padding: 40px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 4rem;
|
||||
margin-bottom: 20px;
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
color: #666;
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 40px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
padding: 15px 20px;
|
||||
border: 2px solid #e1e5e9;
|
||||
border-radius: 50px;
|
||||
font-size: 1.1rem;
|
||||
outline: none;
|
||||
transition: all 0.3s ease;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.search-button {
|
||||
padding: 15px 30px;
|
||||
background: linear-gradient(45deg, #667eea, #764ba2);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 50px;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.search-button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.search-button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.features {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
gap: 20px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.feature {
|
||||
padding: 20px;
|
||||
background: rgba(102, 126, 234, 0.1);
|
||||
border-radius: 15px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.feature:hover {
|
||||
transform: translateY(-5px);
|
||||
background: rgba(102, 126, 234, 0.2);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.feature-desc {
|
||||
font-size: 0.9rem;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 3px solid #f3f3f3;
|
||||
border-top: 3px solid #667eea;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #e74c3c;
|
||||
background: #fdf2f2;
|
||||
padding: 10px 15px;
|
||||
border-radius: 10px;
|
||||
margin-top: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #27ae60;
|
||||
background: #f0f9f0;
|
||||
padding: 10px 15px;
|
||||
border-radius: 10px;
|
||||
margin-top: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="logo">🎬</div>
|
||||
<h1>Поиск фильмов и сериалов</h1>
|
||||
<p class="subtitle">
|
||||
Найдите любой фильм или сериал и скачайте его через торрент.<br>
|
||||
Быстро, удобно и бесплатно!
|
||||
</p>
|
||||
|
||||
<form class="search-form" action="/search" method="post" id="searchForm">
|
||||
<input
|
||||
type="text"
|
||||
name="movie_title"
|
||||
class="search-input"
|
||||
placeholder="Введите название фильма или сериала..."
|
||||
required
|
||||
autocomplete="off"
|
||||
>
|
||||
<button type="submit" class="search-button">
|
||||
🔍 Найти
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="loading" id="loading">
|
||||
<div class="spinner"></div>
|
||||
<p>Ищем фильмы...</p>
|
||||
</div>
|
||||
|
||||
<div class="error" id="error"></div>
|
||||
<div class="success" id="success"></div>
|
||||
|
||||
<div class="features">
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🎯</div>
|
||||
<div class="feature-title">Точный поиск</div>
|
||||
<div class="feature-desc">Находим именно то, что вы ищете</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">⚡</div>
|
||||
<div class="feature-title">Быстро</div>
|
||||
<div class="feature-desc">Результаты за секунды</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">🆓</div>
|
||||
<div class="feature-title">Бесплатно</div>
|
||||
<div class="feature-desc">Полностью бесплатный сервис</div>
|
||||
</div>
|
||||
<div class="feature">
|
||||
<div class="feature-icon">📱</div>
|
||||
<div class="feature-title">Удобно</div>
|
||||
<div class="feature-desc">Работает на всех устройствах</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('searchForm').addEventListener('submit', function(e) {
|
||||
const loading = document.getElementById('loading');
|
||||
const error = document.getElementById('error');
|
||||
const success = document.getElementById('success');
|
||||
|
||||
// Скрываем предыдущие сообщения
|
||||
error.style.display = 'none';
|
||||
success.style.display = 'none';
|
||||
|
||||
// Показываем загрузку
|
||||
loading.style.display = 'block';
|
||||
|
||||
// Проверяем, что поле не пустое
|
||||
const input = document.querySelector('input[name="movie_title"]');
|
||||
if (!input.value.trim()) {
|
||||
e.preventDefault();
|
||||
loading.style.display = 'none';
|
||||
error.textContent = 'Пожалуйста, введите название фильма';
|
||||
error.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Автофокус на поле ввода
|
||||
document.querySelector('input[name="movie_title"]').focus();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
37
start_all_services.sh
Executable file
37
start_all_services.sh
Executable file
|
|
@ -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"
|
||||
|
||||
|
||||
22
stop_all_services.sh
Executable file
22
stop_all_services.sh
Executable file
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue