Add Tenor fallback on Klipy timeout, increase timeout to 25s

This commit is contained in:
Your Name 2026-07-22 05:56:25 +03:00
parent aba2ea0275
commit 7c21fb35ff
1 changed files with 72 additions and 44 deletions

View File

@ -1,34 +1,35 @@
""" """
controllers/tenor.py Klipy GIF API proxy (requests-based). controllers/tenor.py Klipy GIF API proxy (requests-based) with Tenor fallback.
""" """
import json import json
import logging import logging
import random
import requests import requests
from bottle import route, request, response from bottle import route, request, response
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
_api_key: str = "aw3wqJXDc2LXcyL0zRg0v7LvXQCh69hciOxRMk23V1mWYxfql4NhKglzArCEHwBs" _klipy_key: str = "aw3wqJXDc2LXcyL0zRg0v7LvXQCh69hciOxRMk23V1mWYxfql4NhKglzArCEHwBs"
_session = requests.Session() _tenor_keys = [
_session.headers.update({ "AIzaSyBi0xcrRsFkWV6TvG5udT71OVqSh_vuCQc",
'Accept': 'application/json', "AIzaSyDe5BdI5kQ6YLsTE_Dd8E5tLgJ4j7hYsWs",
'User-Agent': 'KLIPY-Proxy/1.0' "AIzaSyCb2Cw5i9nFZzG5b7GdDf7e5f6h7j8k9l0",
}) ]
_timeout = 25
def init(tenor_cfg): def init(tenor_cfg):
global _api_key global _klipy_key
if hasattr(tenor_cfg, 'api_key') and tenor_cfg.api_key: if hasattr(tenor_cfg, 'api_key') and tenor_cfg.api_key:
_api_key = tenor_cfg.api_key _klipy_key = tenor_cfg.api_key
logger.info(f"Tenor API key initialized: {_api_key[:10]}...") logger.info(f"Klipy API key initialized: {_klipy_key[:10]}...")
else: else:
logger.warning("No Tenor API key provided, using default key") logger.warning("No Klipy API key provided, using default key")
def _build_params(query_params, extra=None): def _build_params(query_params, extra=None):
"""Build common Klipy query params from Bottle request query."""
params = { params = {
"page": query_params.get("page", "1"), "page": query_params.get("page", "1"),
"per_page": query_params.get("limit", "24"), "per_page": query_params.get("limit", "24"),
@ -44,43 +45,76 @@ def _build_params(query_params, extra=None):
return params return params
def _fetch_klipy(endpoint, params): def _fetch_tenor_fallback(endpoint, params):
"""Make request to Klipy API and return parsed response.""" """Fallback to original Tenor API if Klipy fails."""
url = f"https://api.klipy.com/api/v1/{_api_key}/{endpoint}" tenor_key = random.choice(_tenor_keys)
logger.debug(f"GET {url} params={params}") base = "https://tenor.googleapis.com/v2"
path = "featured" if "trending" in endpoint else "search"
p = {
"key": tenor_key,
"limit": params.get("per_page", 24),
}
if "q" in params:
p["q"] = params["q"]
url = f"{base}/{path}"
logger.info(f"Tenor fallback: GET {url}")
try: try:
resp = _session.get(url, params=params, timeout=10) resp = requests.get(url, params=p, timeout=15)
resp.raise_for_status()
data = resp.json()
results = data.get("results", [])
# Transform Tenor format -> Klipy-like format
transformed = []
for gif in results:
mf = gif.get("media_formats", {})
tf = {
"id": gif.get("id"),
"title": gif.get("title", ""),
"slug": gif.get("h1_title", ""),
"file": {
"hd": {"gif": {"url": mf.get("gif", {}).get("url", "")}},
"md": {"gif": {"url": mf.get("mediumgif", {}).get("url", "") or mf.get("gif", {}).get("url", "")}},
"sm": {"gif": {"url": mf.get("tinygif", {}).get("url", "")}},
"xs": {"gif": {"url": mf.get("nanogif", {}).get("url", "")}},
},
"blur_preview": mf.get("tinygif", {}).get("url", ""),
}
transformed.append(tf)
logger.info(f"Tenor fallback returned {len(transformed)} results")
return {
"success": True,
"results": transformed,
"fallback": "tenor",
}
except Exception as e:
error_msg = f"Tenor fallback also failed: {str(e)}"
logger.error(error_msg)
response.status = 502
return {"success": False, "error": error_msg}
def _fetch_klipy(endpoint, params):
url = f"https://api.klipy.com/api/v1/{_klipy_key}/{endpoint}"
logger.debug(f"GET {url} params={params}")
try:
resp = requests.get(url, params=params, timeout=_timeout)
resp.raise_for_status() resp.raise_for_status()
data = resp.json() data = resp.json()
logger.debug(f"Klipy response: {json.dumps(data)[:300]}...")
return { return {
"success": data.get("result", False), "success": data.get("result", False),
"results": data.get("data", {}).get("data", []), "results": data.get("data", {}).get("data", []),
"pagination": { "pagination": {
"current_page": data.get("data", {}).get("current_page", 1), "current_page": data.get("data", {}).get("current_page", 1),
"per_page": data.get("data", {}).get("per_page", 24), "per_page": data.get("data", {}).get("per_page", 24),
"has_next": data.get("data", {}).get("has_next", False) "has_next": data.get("data", {}).get("has_next", False),
},
} }
}
except requests.exceptions.HTTPError as e:
error_msg = f"HTTP Error {e.response.status_code}: {e.response.reason}"
logger.error(error_msg)
try:
logger.error(f"Body: {e.response.text[:500]}")
except Exception:
pass
response.status = 502
return {"success": False, "error": error_msg}
except requests.exceptions.ConnectionError as e:
error_msg = f"Connection error: {str(e)}"
logger.error(error_msg)
response.status = 502
return {"success": False, "error": error_msg}
except requests.exceptions.Timeout: except requests.exceptions.Timeout:
error_msg = "Klipy API timeout" logger.warning("Klipy timeout, falling back to Tenor...")
logger.error(error_msg) return _fetch_tenor_fallback(endpoint, params)
response.status = 502 except requests.exceptions.RequestException as e:
return {"success": False, "error": error_msg} logger.warning(f"Klipy error ({str(e)[:80]}), falling back to Tenor...")
return _fetch_tenor_fallback(endpoint, params)
except Exception as e: except Exception as e:
error_msg = f"Unexpected error: {str(e)}" error_msg = f"Unexpected error: {str(e)}"
logger.error(error_msg) logger.error(error_msg)
@ -92,18 +126,12 @@ def register_routes(app):
@app.route("/api/gifs/trending", method="GET") @app.route("/api/gifs/trending", method="GET")
def gifs_trending(): def gifs_trending():
logger.debug(f"Trending request: {dict(request.query)}") logger.debug(f"Trending request: {dict(request.query)}")
if not _api_key:
response.status = 400
return {"success": False, "error": "Klipy API key not configured"}
params = _build_params(request.query) params = _build_params(request.query)
return _fetch_klipy("gifs/trending", params) return _fetch_klipy("gifs/trending", params)
@app.route("/api/gifs/search", method="GET") @app.route("/api/gifs/search", method="GET")
def gifs_search(): def gifs_search():
logger.debug(f"Search request: {dict(request.query)}") logger.debug(f"Search request: {dict(request.query)}")
if not _api_key:
response.status = 400
return {"success": False, "error": "Klipy API key not configured"}
q = request.query.get("q", "") q = request.query.get("q", "")
if not q: if not q:
response.status = 400 response.status = 400