Add Tenor fallback on Klipy timeout, increase timeout to 25s
This commit is contained in:
parent
aba2ea0275
commit
7c21fb35ff
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue