File size: 4,328 Bytes
579e534 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 | """
cache.py — lightweight SQLite cache for scraped Tenor results.
HF Spaces free tier gives you a persistent disk mounted at /data if you
enable "persistent storage", otherwise the filesystem resets on restart.
Either way this works fine — it just won't survive a restart without
persistent storage enabled. Path is overridable via TENOR_CACHE_PATH env var.
"""
import os
import json
import sqlite3
import time
import threading
DB_PATH = os.environ.get("TENOR_CACHE_PATH", "/data/tenor_cache.db")
# fall back to local dir if /data doesn't exist (no persistent storage enabled)
if not os.path.isdir(os.path.dirname(DB_PATH)):
DB_PATH = os.environ.get("TENOR_CACHE_PATH_FALLBACK", "./tenor_cache.db")
_lock = threading.Lock()
def _connect():
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
conn.row_factory = sqlite3.Row
return conn
def init_db():
with _lock:
conn = _connect()
conn.execute("""
CREATE TABLE IF NOT EXISTS search_cache (
query TEXT NOT NULL,
page INTEGER NOT NULL DEFAULT 1,
results_json TEXT NOT NULL,
cached_at REAL NOT NULL,
PRIMARY KEY (query, page)
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS favorites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
gif_url TEXT NOT NULL UNIQUE,
mp4_url TEXT,
title TEXT,
tag TEXT,
added_at REAL NOT NULL
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS request_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
endpoint TEXT NOT NULL,
query TEXT,
api_key TEXT,
ts REAL NOT NULL
)
""")
conn.commit()
conn.close()
def get_cached_search(query, page=1, max_age_seconds=3600):
with _lock:
conn = _connect()
row = conn.execute(
"SELECT results_json, cached_at FROM search_cache WHERE query = ? AND page = ?",
(query.lower().strip(), page),
).fetchone()
conn.close()
if not row:
return None
if time.time() - row["cached_at"] > max_age_seconds:
return None
return json.loads(row["results_json"])
def set_cached_search(query, results, page=1):
with _lock:
conn = _connect()
conn.execute(
"INSERT OR REPLACE INTO search_cache (query, page, results_json, cached_at) VALUES (?, ?, ?, ?)",
(query.lower().strip(), page, json.dumps(results), time.time()),
)
conn.commit()
conn.close()
def add_favorite(gif_url, mp4_url=None, title="", tag=""):
with _lock:
conn = _connect()
try:
conn.execute(
"INSERT OR IGNORE INTO favorites (gif_url, mp4_url, title, tag, added_at) VALUES (?, ?, ?, ?, ?)",
(gif_url, mp4_url, title, tag, time.time()),
)
conn.commit()
finally:
conn.close()
def remove_favorite(favorite_id):
with _lock:
conn = _connect()
conn.execute("DELETE FROM favorites WHERE id = ?", (favorite_id,))
conn.commit()
conn.close()
def list_favorites(tag=None):
with _lock:
conn = _connect()
if tag:
rows = conn.execute(
"SELECT * FROM favorites WHERE tag = ? ORDER BY added_at DESC", (tag,)
).fetchall()
else:
rows = conn.execute(
"SELECT * FROM favorites ORDER BY added_at DESC"
).fetchall()
conn.close()
return [dict(r) for r in rows]
def log_request(endpoint, query=None, api_key=None):
with _lock:
conn = _connect()
conn.execute(
"INSERT INTO request_log (endpoint, query, api_key, ts) VALUES (?, ?, ?, ?)",
(endpoint, query, api_key, time.time()),
)
conn.commit()
conn.close()
def clear_expired(max_age_seconds=86400):
with _lock:
conn = _connect()
conn.execute(
"DELETE FROM search_cache WHERE cached_at < ?",
(time.time() - max_age_seconds,),
)
conn.commit()
conn.close() |