""" controllers/tenor.py — Klipy GIF API proxy (requests-based) with Tenor fallback. """ import json import logging import random import requests from bottle import route, request, response logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) _klipy_key: str = "aw3wqJXDc2LXcyL0zRg0v7LvXQCh69hciOxRMk23V1mWYxfql4NhKglzArCEHwBs" _tenor_keys = [ "AIzaSyBi0xcrRsFkWV6TvG5udT71OVqSh_vuCQc", "AIzaSyDe5BdI5kQ6YLsTE_Dd8E5tLgJ4j7hYsWs", "AIzaSyCb2Cw5i9nFZzG5b7GdDf7e5f6h7j8k9l0", ] _timeout = 25 def init(tenor_cfg): global _klipy_key if hasattr(tenor_cfg, 'api_key') and tenor_cfg.api_key: _klipy_key = tenor_cfg.api_key logger.info(f"Klipy API key initialized: {_klipy_key[:10]}...") else: logger.warning("No Klipy API key provided, using default key") def _build_params(query_params, extra=None): params = { "page": query_params.get("page", "1"), "per_page": query_params.get("limit", "24"), "content_filter": query_params.get("content_filter", "off"), "locale": query_params.get("locale", "us"), } if query_params.get("customer_id"): params["customer_id"] = query_params["customer_id"] if query_params.get("format_filter"): params["format_filter"] = query_params["format_filter"] if extra: params.update(extra) return params def _fetch_tenor_fallback(endpoint, params): """Fallback to original Tenor API if Klipy fails.""" tenor_key = random.choice(_tenor_keys) 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: 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() data = resp.json() return { "success": data.get("result", False), "results": data.get("data", {}).get("data", []), "pagination": { "current_page": data.get("data", {}).get("current_page", 1), "per_page": data.get("data", {}).get("per_page", 24), "has_next": data.get("data", {}).get("has_next", False), }, } except requests.exceptions.Timeout: logger.warning("Klipy timeout, falling back to Tenor...") return _fetch_tenor_fallback(endpoint, params) except requests.exceptions.RequestException as e: logger.warning(f"Klipy error ({str(e)[:80]}), falling back to Tenor...") return _fetch_tenor_fallback(endpoint, params) except Exception as e: error_msg = f"Unexpected error: {str(e)}" logger.error(error_msg) response.status = 502 return {"success": False, "error": error_msg} def register_routes(app): @app.route("/api/gifs/trending", method="GET") def gifs_trending(): logger.debug(f"Trending request: {dict(request.query)}") params = _build_params(request.query) return _fetch_klipy("gifs/trending", params) @app.route("/api/gifs/search", method="GET") def gifs_search(): logger.debug(f"Search request: {dict(request.query)}") q = request.query.get("q", "") if not q: response.status = 400 return {"success": False, "error": "Missing query"} params = _build_params(request.query, extra={"q": q}) return _fetch_klipy("gifs/search", params)