# ============================================ # File: scraper/ui_log.py # Purpose: Central UI log buffer for WebGUI # Single global buffer. No book_id. # ============================================ import redis import os from datetime import datetime REDIS_URL = os.getenv("REDIS_BROKER", "redis://redis:6379/0") LOG_BUFFER_SIZE = int(os.getenv("LOG_BUFFER_SIZE", "1000")) r = redis.Redis.from_url(REDIS_URL, decode_responses=True) UI_LOG_KEY = "logs:ui" def push_ui(message: str): """Push a message into the global UI log (no book_id).""" if not message or not message.strip(): return ts = datetime.now().strftime("%H:%M:%S") entry = f"[{ts}] {message}" r.rpush(UI_LOG_KEY, entry) r.ltrim(UI_LOG_KEY, -LOG_BUFFER_SIZE, -1) def get_ui_logs(limit: int = None): """Return last N global UI log lines.""" if limit is None: limit = LOG_BUFFER_SIZE return r.lrange(UI_LOG_KEY, -limit, -1) def reset_ui_logs(): """ Clear the entire UI log buffer. Used by: - Clear button in GUI - Auto-clear when new book scraping starts """ r.delete(UI_LOG_KEY) # ============================================================ # Delta-based log retrieval using Redis indexes # ============================================================ def get_ui_logs_delta(last_index: int): """ Returns (new_lines, total_count). Only returns log lines AFTER last_index. Example: last_index = 10 → returns logs with Redis indexes 11..end """ total = r.llen(UI_LOG_KEY) if total == 0: return [], 0 # First load OR index invalid → send entire buffer if last_index < 0 or last_index >= total: logs = r.lrange(UI_LOG_KEY, 0, -1) return logs, total # Only new logs new_lines = r.lrange(UI_LOG_KEY, last_index + 1, -1) return new_lines, total