"""Central configuration, driven by environment variables.""" import os import sys from pathlib import Path # PyInstaller sets ``sys.frozen`` and unpacks bundled files to ``sys._MEIPASS``. # Read-only resources (DATs, loader, icons) then live in the bundle; otherwise # they live in the project checkout. IS_FROZEN = getattr(sys, "frozen", False) _BUNDLE_DIR = getattr(sys, "_MEIPASS", None) PROJECT_ROOT = Path(_BUNDLE_DIR).resolve() if _BUNDLE_DIR else Path(__file__).resolve().parent.parent def resource_path(*parts): """Absolute path to a read-only bundled resource (works when frozen).""" return PROJECT_ROOT.joinpath(*parts) DATA_DIR = Path(os.environ.get("PSP_DATA_DIR", str(PROJECT_ROOT / "data"))) DATS_DIR = Path(os.environ.get("PSP_DATS_DIR", str(DATA_DIR / "dats"))) def _default_state_dir(): """Local state directory (fast, reliable local disk). SQLite locking is unreliable over network filesystems (SMB/FUSE), so the library DB and the cover cache live on local storage while read-only DATs stay bundled with the app. """ if os.name == "nt": base = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA") or str(Path.home()) return Path(base) / "PSPPreserve" xdg = os.environ.get("XDG_STATE_HOME") base = Path(xdg) if xdg else Path.home() / ".local" / "state" return base / "psp-preserve" STATE_DIR = Path(os.environ.get("PSP_STATE_DIR", str(_default_state_dir()))) DB_PATH = Path(os.environ.get("PSP_DB_PATH", str(STATE_DIR / "library.sqlite"))) COVERS_DIR = Path(os.environ.get("PSP_COVERS_DIR", str(STATE_DIR / "covers"))) # User-writable data (credentials, manual covers, PS1 templates, NPS TSV cache). # In dev this is the checkout's ``data/``; when frozen the bundle is read-only, # so it points at local state that survives app upgrades. USER_DATA_DIR = Path(os.environ.get( "PSP_USER_DATA_DIR", str(DATA_DIR if not IS_FROZEN else STATE_DIR / "data"))) LEGACY_DB_PATH = DATA_DIR / "library.sqlite" LEGACY_COVERS_DIR = DATA_DIR / "covers" def user_data_path(*parts): """Absolute path to a user-writable data file/folder (works when frozen).""" return USER_DATA_DIR.joinpath(*parts) # Redump 7z store (browsable in the Store tab). Env override; GUI setting wins. STORE_ROOT = Path(os.environ.get("PSP_STORE_DIR", "/media/downloads/Sony - PlayStation Portable")) # Remote store server (HTTPS). Used by the Store tab as the primary source. STORE_SERVER_URL = os.environ.get("PSP_STORE_SERVER", "https://psp.ceaf.dev") STORE_TOKEN = os.environ.get("PSP_STORE_TOKEN", "") # Optional direct URLs for the offline cover bundle zips. When unset, the GUI # derives them from the store server URL (/api/covers/.zip). LIBRETRO_COVERS_URL = os.environ.get("PSP_LIBRETRO_COVERS_URL", "") OVGDB_COVERS_URL = os.environ.get("PSP_OVGDB_COVERS_URL", "") # archive.org item hosting the same Redump 7z set (identifier or full URL). ARCHIVE_ITEM = os.environ.get("PSP_ARCHIVE_ITEM", "psp-redump-roms321.com") # Optional archive.org login cookies (for items whose files are "private" / # login-required). Grab these from a logged-in browser session on archive.org. ARCHIVE_LOGGED_IN_SIG = os.environ.get("PSP_ARCHIVE_LOGGED_IN_SIG", "") ARCHIVE_LOGGED_IN_USER = os.environ.get("PSP_ARCHIVE_LOGGED_IN_USER", "") # NoPayStation: where extracted .pkg content lands, and the TSV source base. # PSP games go to /PSP//; PS1 Classics to .../PSX/. NPS_OUTPUT_DIR = Path(os.environ.get("PSP_NPS_DIR", str(STATE_DIR / "nps"))) NPS_TSV_BASE = os.environ.get("PSP_NPS_TSV_BASE", "https://nopaystation.com/tsv/") # pkg2zip binary used to decrypt/extract NPS .pkg files (blank = auto-detect). PKG2ZIP_PATH = os.environ.get("PSP_PKG2ZIP_PATH", "") # Scan roots (used by the scanner phase). Comma-separated in env. GAMES_ROOTS = [Path(p) for p in os.environ.get("PSP_GAMES_ROOTS", "/data/umd_games").split(",") if p.strip()] VIDEO_ROOTS = [Path(p) for p in os.environ.get("PSP_VIDEO_ROOTS", "/data/umd_videos").split(",") if p.strip()] # PS1: source folder (raw ISO/BIN/CUE) and converted EBOOT.PBP output folder. PS1_ROOTS = [Path(p) for p in os.environ.get("PSP_PS1_ROOTS", "/data/ps1").split(",") if p.strip()] PS1_OUTPUT_DIR = Path(os.environ.get("PSP_PS1_OUTPUT_DIR", "/data/ps1_converted")) # Only .iso discs are tracked; everything below is explicitly ignored. DISC_EXTENSIONS = {".iso"} IGNORED_EXTENSIONS = {".pkg", ".pbp", ".cso"} # PS1 source images (scanned separately from PSP .iso discs). PS1_EXTENSIONS = {".cue", ".iso", ".img", ".bin"} # Parallel hashing workers for the scanner. Keep at 1 on spinning disks # (concurrent reads cause seek thrash); raise it for SSD / fast arrays. SCAN_WORKERS = int(os.environ.get("PSP_SCAN_WORKERS", "1"))