110 lines
5.0 KiB
Python
110 lines
5.0 KiB
Python
"""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
|
|
|
|
# Frozen bundles don't ship the system CA store, so Python's urllib would fail
|
|
# TLS verification against the store server / NoPayStation / TMDB / archive.org.
|
|
# Point OpenSSL at the certifi bundle (a curl_cffi dependency) when frozen only;
|
|
# source runs keep using the OS CA store.
|
|
if IS_FROZEN:
|
|
try:
|
|
import certifi
|
|
_CA_BUNDLE = certifi.where()
|
|
os.environ.setdefault("SSL_CERT_FILE", _CA_BUNDLE)
|
|
os.environ.setdefault("REQUESTS_CA_BUNDLE", _CA_BUNDLE)
|
|
except Exception: # noqa: BLE001
|
|
pass
|
|
|
|
|
|
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) / "psppt"
|
|
xdg = os.environ.get("XDG_STATE_HOME")
|
|
base = Path(xdg) if xdg else Path.home() / ".local" / "state"
|
|
return base / "psppt"
|
|
|
|
|
|
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", "/data/store"))
|
|
|
|
# Remote store server (HTTPS). Used by the Store tab as the primary source.
|
|
STORE_SERVER_URL = os.environ.get("PSP_STORE_SERVER", "https://psp.example.com")
|
|
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/<name>.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 <NPS_OUTPUT_DIR>/PSP/<TITLE_ID>/; 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/")
|
|
|
|
# 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"))
|