import os import runpy import sys from typing import Dict, Sequence, Tuple ROOT_DIR = os.path.abspath(os.path.dirname(__file__)) SCRIPTS_DIR = os.path.join(ROOT_DIR, "Scripts") EXTRACT_DIR = os.path.join(SCRIPTS_DIR, "Extract Pixiv") RESET = "\x1b[0m" BOLD = "\x1b[1m" FG_PRIMARY = "\x1b[38;5;81m" FG_ACCENT = "\x1b[38;5;214m" FG_MUTED = "\x1b[38;5;245m" FG_OK = "\x1b[38;5;120m" FG_ERROR = "\x1b[38;5;203m" RULE = "-" * 56 EXIT_KEYS = {"q", "quit", "exit"} MENU_SECTIONS: Sequence[Tuple[str, Sequence[Tuple[str, str, str]]]] = ( ( "Extract Pixiv", ( ("1", "Search (all tags)", os.path.join(EXTRACT_DIR, "search.py")), ("2", "Search (AI only)", os.path.join(EXTRACT_DIR, "ai_search.py")), ("3", "Search (real only)", os.path.join(EXTRACT_DIR, "real_search.py")), ("4", "Users", os.path.join(EXTRACT_DIR, "user.py")), ), ), ( "Hunt", ( ("5", "Hunt", os.path.join(SCRIPTS_DIR, "hunt.py")), ("6", "Scan existing exif", os.path.join(SCRIPTS_DIR, "scan_existing_exif.py")), ), ), ( "Maintenance", ( ("7", "Clear text logs", os.path.join(SCRIPTS_DIR, "clear_texts.py")), ("8", "Clear DB empty rows", os.path.join(SCRIPTS_DIR, "clear_db.py")), ("9", "Clear images (except Stash)", os.path.join(SCRIPTS_DIR, "clear_images.py")), ), ), ) ACTION_MAP: Dict[str, Tuple[str, str]] = { key: (label, path) for _, items in MENU_SECTIONS for key, label, path in items } def style(text: str, *codes: str) -> str: if not codes: return text return "".join(codes) + text + RESET def print_menu() -> None: print(style("Pixif", FG_PRIMARY, BOLD)) print(style(RULE, FG_MUTED)) for section, items in MENU_SECTIONS: print(style(section, FG_ACCENT, BOLD)) for key, label, _ in items: print(f" {style(key, FG_PRIMARY, BOLD)} {label}") print(style(RULE, FG_MUTED)) print(f" {style('q', FG_MUTED)} Quit") def run_script(path: str) -> int: if not os.path.isfile(path): print(style(f"Missing: {path}", FG_ERROR)) return 1 original_cwd = os.getcwd() original_sys_path = list(sys.path) script_dir = os.path.dirname(path) sys.path.insert(0, script_dir) if ROOT_DIR not in sys.path: sys.path.insert(0, ROOT_DIR) try: os.chdir(ROOT_DIR) try: runpy.run_path(path, run_name="__main__") except SystemExit as exc: if isinstance(exc.code, int): return exc.code return 0 return 0 except KeyboardInterrupt: print(style("Cancelled.", FG_ERROR)) return 130 except Exception as exc: print(style(f"Error: {exc}", FG_ERROR)) return 1 finally: os.chdir(original_cwd) sys.path[:] = original_sys_path def main() -> int: os.chdir(ROOT_DIR) while True: print_menu() choice = input(style("Select an option: ", FG_MUTED)).strip().lower() if choice in EXIT_KEYS: return 0 action = ACTION_MAP.get(choice) if not action: print(style("Unknown option.", FG_ERROR)) continue label, path = action print(style(f"Running {label}...", FG_PRIMARY, BOLD)) code = run_script(path) if code == 0: print(style("Done.", FG_OK)) else: print(style(f"Exit code: {code}", FG_ERROR)) input(style("Press Enter to return to menu.", FG_MUTED)) if __name__ == "__main__": raise SystemExit(main())