import os import redis # GUI log (non-breaking) from scraper.ui_log import push_ui # --------------------------------------------------------- # Default Redis connection (Docker workers) # --------------------------------------------------------- REDIS_URL = os.getenv("REDIS_URL", "redis://redis:6379/0") r = redis.Redis.from_url(REDIS_URL, decode_responses=True) # Debug mode (optional) ABORT_DEBUG = os.getenv("ABORT_DEBUG", "1") == "1" # Internal flag to avoid spamming the same message _seen_debug_keys = set() # ========================================================= # ABORT FLAG # ========================================================= def _debug(msg: str): """Print + GUI log (non-breaking, minimal noise).""" print(msg) push_ui(msg) def set_abort(book_id: str): """Enable abort mode for this book.""" key = f"abort:{book_id}" r.set(key, "1") if ABORT_DEBUG: _debug(f"[ABORT] SET {key}") def clear_abort(book_id: str): """Clear abort flag.""" key = f"abort:{book_id}" r.delete(key) if ABORT_DEBUG: _debug(f"[ABORT] CLEAR {key}") def abort_requested(book_id: str, redis_client=None) -> bool: """ Return True if abort flag is set. redis_client: - Docker workers → None → use default Redis (r) - Local macOS audio → passes Redis(host=127.0.0.1) """ client = redis_client or r key = f"abort:{book_id}" try: exists = client.exists(key) if ABORT_DEBUG: # Log once per key if key not in _seen_debug_keys: try: conn = client.connection_pool.connection_kwargs host = conn.get("host") port = conn.get("port") db = conn.get("db") _debug( f"[ABORT_DEBUG] first check book_id={book_id} " f"redis={host}:{port} db={db}" ) except Exception: _debug(f"[ABORT_DEBUG] first check book_id={book_id}") _seen_debug_keys.add(key) # Only log abort ACTIVE if exists == 1: _debug(f"[ABORT] ACTIVE for {book_id}") return exists == 1 except Exception as e: if ABORT_DEBUG: _debug(f"[ABORT_DEBUG] ERROR checking {key}: {e}") return False # ========================================================= # PER-CHAPTER STATE # ========================================================= def mark_chapter_started(book_id: str, chapter_num: int): key = f"started:{book_id}:{chapter_num}" r.set(key, "1") def chapter_started(book_id: str, chapter_num: int) -> bool: key = f"started:{book_id}:{chapter_num}" return r.exists(key) == 1 # ========================================================= # UTILITY: RESET FOR A BOOK # ========================================================= def reset_book_state(book_id: str): """ Remove abort flag and all chapter-start markers. """ key = f"abort:{book_id}" r.delete(key) pattern = f"started:{book_id}:*" for k in r.scan_iter(pattern): r.delete(k)