findFilms/searchFilms/tmdb-proxy/tmdb_proxy.py
vrubelroman 127060d023 fix: пропал декоратор @app.post(/api/add-torrent), follow_redirects в tmdb-proxy
- Возвращён @app.post('/api/add-torrent') — был съеден при вставке
  proxy-torrent-download, из-за чего кнопка 'Добавить в клиент' всегда
  возвращала 404
- tmdb-proxy /proxy-torrent: добавлен follow_redirects=True — rutracker
  и kinozal отдают 302 перед .torrent файлом
2026-06-03 19:32:41 +00:00

173 lines
7.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
TMDB API Proxy Service
Прокси-сервис для TMDB API, который работает на хосте без VPN
"""
import os
import logging
import httpx
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import Response
from fastapi.middleware.cors import CORSMiddleware
# Настройка логирования
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = FastAPI(title="TMDB API Proxy", version="1.0.0")
# Настройка CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# API ключ TMDB (прописываем прямо здесь)
TMDB_API_KEY = os.getenv("TMDB_API_KEY", "6d58225585fb77af5945a964de41849f")
TMDB_BASE_URL = "https://api.themoviedb.org/3"
@app.get("/search/movie")
async def search_movies(
query: str = Query(..., description="Поисковый запрос"),
language: str = Query("ru-RU", description="Язык"),
include_adult: bool = Query(False, description="Включать взрослый контент"),
page: int = Query(1, description="Номер страницы")
):
"""Прокси для поиска фильмов через TMDB API"""
async with httpx.AsyncClient(timeout=30.0) as client:
try:
logger.info(f"Searching TMDB for query: {query}")
response = await client.get(
f"{TMDB_BASE_URL}/search/movie",
params={
"api_key": TMDB_API_KEY,
"query": query,
"language": language,
"include_adult": include_adult,
"page": page
}
)
response.raise_for_status()
data = response.json()
logger.info(f"TMDB search successful, found {data.get('total_results', 0)} results")
return data
except httpx.ConnectError as e:
logger.error(f"Connection error to TMDB API: {e}")
raise HTTPException(
status_code=503,
detail=f"Cannot connect to TMDB API. Check network connectivity and DNS. Error: {str(e)}"
)
except httpx.TimeoutException as e:
logger.error(f"Timeout connecting to TMDB API: {e}")
raise HTTPException(
status_code=504,
detail=f"Timeout connecting to TMDB API: {str(e)}"
)
except httpx.HTTPStatusError as e:
logger.error(f"TMDB API returned error status {e.response.status_code}: {e.response.text}")
raise HTTPException(
status_code=e.response.status_code,
detail=f"TMDB API error (status {e.response.status_code}): {e.response.text[:200]}"
)
except httpx.HTTPError as e:
logger.error(f"HTTP error: {e}")
raise HTTPException(status_code=500, detail=f"TMDB API error: {str(e)}")
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")
@app.get("/movie/{movie_id}")
async def get_movie_details(
movie_id: int,
language: str = Query("ru-RU", description="Язык"),
append_to_response: str = Query(None, description="Дополнительные данные")
):
"""Прокси для получения детальной информации о фильме из TMDB"""
async with httpx.AsyncClient(timeout=30.0) as client:
try:
logger.info(f"Fetching TMDB movie details for ID: {movie_id}")
params = {
"api_key": TMDB_API_KEY,
"language": language
}
if append_to_response:
params["append_to_response"] = append_to_response
response = await client.get(
f"{TMDB_BASE_URL}/movie/{movie_id}",
params=params
)
response.raise_for_status()
return response.json()
except httpx.ConnectError as e:
logger.error(f"Connection error to TMDB API: {e}")
raise HTTPException(
status_code=503,
detail=f"Cannot connect to TMDB API. Check network connectivity and DNS. Error: {str(e)}"
)
except httpx.TimeoutException as e:
logger.error(f"Timeout connecting to TMDB API: {e}")
raise HTTPException(
status_code=504,
detail=f"Timeout connecting to TMDB API: {str(e)}"
)
except httpx.HTTPStatusError as e:
logger.error(f"TMDB API returned error status {e.response.status_code}: {e.response.text}")
raise HTTPException(
status_code=e.response.status_code,
detail=f"TMDB API error (status {e.response.status_code}): {e.response.text[:200]}"
)
except httpx.HTTPError as e:
logger.error(f"HTTP error: {e}")
raise HTTPException(status_code=500, detail=f"TMDB API error: {str(e)}")
except Exception as e:
logger.error(f"Unexpected error: {e}", exc_info=True)
raise HTTPException(status_code=500, detail=f"Unexpected error: {str(e)}")
@app.get("/proxy-torrent")
async def proxy_torrent(url: str = Query(..., description="URL .torrent файла")):
"""Скачивает .torrent файл через NL-сервер (обходит DPI-блокировки)"""
async with httpx.AsyncClient(timeout=30.0) as client:
try:
logger.info(f"Proxying .torrent download: {url}")
response = await client.get(url, timeout=30.0, follow_redirects=True)
response.raise_for_status()
return Response(
content=response.content,
media_type=response.headers.get("content-type", "application/x-bittorrent"),
headers={
"Content-Disposition": f'attachment; filename="torrent.torrent"',
"Content-Length": str(len(response.content))
}
)
except httpx.HTTPStatusError as e:
logger.error(f".torrent proxy returned {e.response.status_code} for {url}")
raise HTTPException(
status_code=e.response.status_code,
detail=f"Failed to download .torrent (status {e.response.status_code})"
)
except httpx.RequestError as e:
logger.error(f".torrent proxy request failed for {url}: {e}")
raise HTTPException(
status_code=502,
detail=f"Cannot download .torrent: {str(e)}"
)
@app.get("/health")
async def health_check():
"""Проверка работоспособности сервиса"""
return {"status": "ok", "service": "tmdb-proxy"}
if __name__ == "__main__":
import uvicorn
port = int(os.getenv("PORT", "8001"))
print(f"Starting TMDB Proxy on 0.0.0.0:{port}")
uvicorn.run(app, host="0.0.0.0", port=port, log_level="info")