diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 5d2f773..60caa43 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -22,19 +22,19 @@ jobs: pip install -r requirements-build.txt - name: Build - run: pyinstaller --clean --noconfirm psp-preserve.spec + run: pyinstaller --clean --noconfirm psppt.spec - name: Package tarball run: | - cp packaging/install.sh dist/psp-preserve/ - cp packaging/psp-preserve.desktop dist/psp-preserve/ - cp img/app-icon.png dist/psp-preserve/ - tar -czf psp-preserve-linux-x86_64.tar.gz -C dist psp-preserve + cp packaging/install.sh dist/psppt/ + cp packaging/psppt.desktop dist/psppt/ + cp img/app-icon.png dist/psppt/ + tar -czf psppt-linux-x86_64.tar.gz -C dist psppt - uses: actions/upload-artifact@v3 with: - name: psp-preserve-linux-x86_64 - path: psp-preserve-linux-x86_64.tar.gz + name: psppt-linux-x86_64 + path: psppt-linux-x86_64.tar.gz windows: name: Windows x64 (Wine) @@ -48,13 +48,13 @@ jobs: winpython -m pip install -r requirements-build.txt - name: Build - run: winpython -m PyInstaller --clean --noconfirm psp-preserve.spec + run: winpython -m PyInstaller --clean --noconfirm psppt.spec - name: Package zip run: | - cd dist && zip -rq ../psp-preserve-windows-x64.zip psp-preserve + cd dist && zip -rq ../psppt-windows-x64.zip psppt - uses: actions/upload-artifact@v3 with: - name: psp-preserve-windows-x64 - path: psp-preserve-windows-x64.zip + name: psppt-windows-x64 + path: psppt-windows-x64.zip diff --git a/Caddyfile b/Caddyfile index 47abf19..a8b173d 100644 --- a/Caddyfile +++ b/Caddyfile @@ -3,5 +3,5 @@ # Port-forward 80 and 443 on your router to this host. hostname.com { - reverse_proxy psp-preserve:8000 + reverse_proxy psppt:8000 } diff --git a/README.md b/README.md index 3d1e6e3..bd8b12e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# PSP Preservation Tool +# psppt the name is terrible, i know. @@ -15,7 +15,7 @@ save-data with a real PSP, can pull missing discs from a Redump 7z archive collection, downloads PSN content from NoPayStation, and converts PlayStation 1 ISO/CUE/BIN rips into PSP `EBOOT.PBP` homebrew. -![PSP Preservation Tool](img/qt-gui.png) +![psppt](img/qt-gui.png) * Dark theme with a blue accent — the Library tab in cover view. * ## Features @@ -73,8 +73,8 @@ see the note inside). python3 -m venv .venv-build . .venv-build/bin/activate pip install -r requirements-build.txt -pyinstaller --clean --noconfirm psp-preserve.spec -# output: dist/psp-preserve/ → run dist/psp-preserve/psp-preserve +pyinstaller --clean --noconfirm psppt.spec +# output: dist/psppt/ → run dist/psppt/psppt ``` Notes: @@ -182,7 +182,7 @@ Discs are matched by hash first (most precise), then serial, then filename. to drawn art when they're absent. - `data/nps/` — NoPayStation TSV lists (cached here on first store scan in dev; a packaged build stores them under the state dir instead). -- State dir (`~/.local/state/psp-preserve/` on Linux, `%LOCALAPPDATA%\PSPPreserve` +- State dir (`~/.local/state/psppt/` on Linux, `%LOCALAPPDATA%\psppt` on Windows, or `$PSP_STATE_DIR`): - `library.sqlite` — the catalog + library database. - `covers/` — downloaded cover cache. @@ -196,13 +196,13 @@ bundled with the app; user-writable data lives in the state dir. ## Configuration Everything is driven by environment variables, with GUI settings (stored in -QSettings under `PSPPreserve`) taking precedence where noted. +QSettings under `psppt`) taking precedence where noted. | Variable | Default | Purpose | |---|---|---| | `PSP_DATA_DIR` | `./data` | data root (DATs, credentials, manual covers) | | `PSP_DATS_DIR` | `$PSP_DATA_DIR/dats` | DAT folder | -| `PSP_STATE_DIR` | `~/.local/state/psp-preserve` | local state root | +| `PSP_STATE_DIR` | `~/.local/state/psppt` | local state root | | `PSP_DB_PATH` | `$PSP_STATE_DIR/library.sqlite` | SQLite library | | `PSP_COVERS_DIR` | `$PSP_STATE_DIR/covers` | cover cache | | `PSP_GAMES_ROOTS` | `/data/umd_games` | comma-separated game disc roots | @@ -427,7 +427,7 @@ Token protection: set `PSP_API_TOKEN`; the client sends macvlan bridge so it gets its own LAN IP (e.g. `192.168.1.100`, plain HTTP on 8000): ```bash -# in /path/to/psp-preserve/ +# in /path/to/psppt/ cp .env.example .env # then edit: LAN IP, share paths, API token docker compose up -d --build ``` diff --git a/app/__init__.py b/app/__init__.py index 461830d..83eb2f5 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1 +1 @@ -"""PSP Preservation Tool application package.""" +"""psppt application package.""" diff --git a/app/api.py b/app/api.py index de15ef4..3a9e7ca 100644 --- a/app/api.py +++ b/app/api.py @@ -251,7 +251,7 @@ def _library(conn, system=None, refresh=False): class Handler(BaseHTTPRequestHandler): - server_version = "PSPPreserve/0.1" + server_version = "psppt/0.1" def do_GET(self): parsed = urlparse(self.path) @@ -262,7 +262,7 @@ class Handler(BaseHTTPRequestHandler): try: if path == "/": _json(self, { - "name": "PSP Preservation Tool API", + "name": "psppt API", "endpoints": [ "/api/summary", "/api/releases?system=games|videos&q=&limit=&offset=", diff --git a/app/config.py b/app/config.py index 22812ca..e12d282 100644 --- a/app/config.py +++ b/app/config.py @@ -42,10 +42,10 @@ def _default_state_dir(): """ if os.name == "nt": base = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA") or str(Path.home()) - return Path(base) / "PSPPreserve" + return Path(base) / "psppt" xdg = os.environ.get("XDG_STATE_HOME") base = Path(xdg) if xdg else Path.home() / ".local" / "state" - return base / "psp-preserve" + return base / "psppt" STATE_DIR = Path(os.environ.get("PSP_STATE_DIR", str(_default_state_dir()))) diff --git a/app/covers.py b/app/covers.py index b75442c..c24e45e 100644 --- a/app/covers.py +++ b/app/covers.py @@ -62,7 +62,7 @@ _WIKI_SUMMARY = "https://en.wikipedia.org/api/rest_v1/page/summary/" _PNG_MAGIC = b"\x89PNG\r\n\x1a\n" _JPEG_MAGIC = b"\xff\xd8\xff" _MAX_HOPS = 3 # follow libretro symlink text bodies at most this many times -_UA = {"User-Agent": "psp-preserve/0.1 (https://github.com; local tool)"} +_UA = {"User-Agent": "psppt/0.1 (https://github.com; local tool)"} _igdb_token_state = {"token": "", "expires": 0.0} # IGDB OAuth2 token cache _igdb_lock = threading.Lock() _igdb_last = 0.0 # monotonic clock of the last IGDB API call @@ -610,7 +610,7 @@ def _fetch_screenscraper(name, timeout, system="games"): params = { "devid": devid, "devpassword": devpassword, - "softname": creds.get("screenscraper_softname") or "psp-preserve", + "softname": creds.get("screenscraper_softname") or "psppt", "output": "json", "systemeid": _platform(system)["ss_id"], "romtype": "rom", diff --git a/app/gui.py b/app/gui.py index efd1a96..eca7d8d 100644 --- a/app/gui.py +++ b/app/gui.py @@ -1,4 +1,4 @@ -"""Native Linux GUI for the PSP Preservation Tool (PySide6 / Qt). +"""Native Linux GUI for psppt (PySide6 / Qt). Run with:: @@ -1117,7 +1117,7 @@ class SettingsPage(QWidget): iso_category, ps1_category, psp_category, ps1_dir, ps1_output_dir, ps1_base_pbp, nps_dir="", parent=None): super().__init__(parent) - s = QSettings("PSPPreserve", "PSPPreserve") + s = QSettings("psppt", "psppt") outer = QVBoxLayout(self) outer.setContentsMargins(16, 16, 16, 16) @@ -1289,7 +1289,7 @@ class SettingsPage(QWidget): note = QLabel( "Your library, covers, and credentials live here — outside the app " "bundle, so they survive upgrades. On Linux this is the " - "~/.local/state/psp-preserve folder." + "~/.local/state/psppt folder." ) note.setWordWrap(True) note.setStyleSheet("color: #9a9ba5;") @@ -1444,7 +1444,7 @@ class SettingsPage(QWidget): def _save(self): sources.save({key: edit.text() for key, edit in self.fields.items()}) covers.clear_miss_cache() - s = QSettings("PSPPreserve", "PSPPreserve") + s = QSettings("psppt", "psppt") s.setValue("auto_scan", self.auto_scan.isChecked()) s.setValue("scan_workers", self.workers_spin.value()) s.setValue("auto_sync_saves", self.auto_sync.isChecked()) @@ -4287,7 +4287,7 @@ class Ps1Page(QWidget): class MainWindow(QMainWindow): def __init__(self): super().__init__() - self.setWindowTitle("psp preservation tool ~ quoije") + self.setWindowTitle("psppt ~ quoije") self.resize(1240, 780) self._rows = [] self._renames = [] @@ -4310,7 +4310,7 @@ class MainWindow(QMainWindow): self._library_workers = [] self._library_loading = False - self.settings = QSettings("PSPPreserve", "PSPPreserve") + self.settings = QSettings("psppt", "psppt") self._games_dir = self._first_existing(config.GAMES_ROOTS) or self.settings.value("games_dir", "") or "" self._videos_dir = self._first_existing(config.VIDEO_ROOTS) or self.settings.value("videos_dir", "") or "" self._store_dir = str(self.settings.value("store_dir", str(config.STORE_ROOT)) or "") @@ -4353,7 +4353,7 @@ class MainWindow(QMainWindow): pm = QPixmap(str(icon_path)) pm = pm.scaled(28, 28, Qt.KeepAspectRatio, Qt.SmoothTransformation) logo.setPixmap(pm) - logo.setToolTip("PSP Preservation Tool") + logo.setToolTip("psppt") self.tabs.setCornerWidget(logo, Qt.TopRightCorner) self.queue = queue.TransferQueue(self) @@ -5676,11 +5676,11 @@ def main(): n = ingest.ensure_catalog() if n: log.info("ingested %d DAT(s) into the catalog", n) - log.info("starting psp-preserve (db: %s)", config.DB_PATH) + log.info("starting psppt (db: %s)", config.DB_PATH) app = QApplication(sys.argv) style.apply_theme(app) - app.setApplicationName("PSP Preservation Tool") - app.setDesktopFileName("psp-preserve") + app.setApplicationName("psppt") + app.setDesktopFileName("psppt") icon_path = config.PROJECT_ROOT / "img" / "app-icon.png" if icon_path.is_file(): app.setWindowIcon(QIcon(str(icon_path))) diff --git a/app/main.py b/app/main.py index 6a3d5ff..81d4955 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,4 @@ -"""PSP Preservation Tool entry point (native GUI). +"""psppt entry point (native GUI). Run with:: diff --git a/app/nopaystation.py b/app/nopaystation.py index 38b4e5c..a4396aa 100644 --- a/app/nopaystation.py +++ b/app/nopaystation.py @@ -151,7 +151,7 @@ def fetch_items(content_type: str, url: str = "", timeout: int = 60, force: bool return items try: - req = urllib.request.Request(url, headers={"User-Agent": "psp-preserve/0.1"}) + req = urllib.request.Request(url, headers={"User-Agent": "psppt/0.1"}) with urllib.request.urlopen(req, timeout=timeout) as resp: data = resp.read() text = data.decode("utf-8", errors="replace") diff --git a/app/openvgdb.py b/app/openvgdb.py index 17b2d9a..a75e57e 100644 --- a/app/openvgdb.py +++ b/app/openvgdb.py @@ -155,7 +155,7 @@ def scrape(limit=None, workers=8, rate=3.0): log.info("downloading OpenVGDB dump (%s)…", OVGDB_ZIP_URL) tmpdir = Path(tempfile.mkdtemp(prefix="openvgdb-")) dump_zip = tmpdir / "openvgdb.zip" - req = urllib.request.Request(OVGDB_ZIP_URL, headers={"User-Agent": "psp-preserve/0.1"}) + req = urllib.request.Request(OVGDB_ZIP_URL, headers={"User-Agent": "psppt/0.1"}) with urllib.request.urlopen(req, timeout=60) as r, open(dump_zip, "wb") as f: f.write(r.read()) diff --git a/app/transfers.py b/app/transfers.py index 788eee6..c7c82cc 100644 --- a/app/transfers.py +++ b/app/transfers.py @@ -284,7 +284,7 @@ class RemoteStoreSource(Source): self.token = token or "" def _headers(self): - headers = {"User-Agent": "psp-preserve/0.1"} + headers = {"User-Agent": "psppt/0.1"} if self.token: headers["Authorization"] = f"Bearer {self.token}" return headers @@ -347,7 +347,7 @@ class ArchiveOrgSource(Source): return f"https://archive.org/download/{self.item}" def _headers(self): - headers = {"User-Agent": "psp-preserve/0.1"} + headers = {"User-Agent": "psppt/0.1"} cookie = "; ".join( p for p in ( f"logged-in-sig={self.sig}" if self.sig else "", diff --git a/docker-compose.yml b/docker-compose.yml index 57262fa..8f551cb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: - psp-preserve: + psppt: build: . - container_name: psp-preserve + container_name: psppt # Attach to Unraid's macvlan bridge so the API gets its own LAN IP. # Plain HTTP for now; see Caddyfile to add Let's Encrypt TLS later. networks: diff --git a/packaging/install.sh b/packaging/install.sh index a118df6..b62cf95 100755 --- a/packaging/install.sh +++ b/packaging/install.sh @@ -1,15 +1,15 @@ #!/bin/sh -# Install the PSP Preservation Tool to a system prefix. +# Install psppt to a system prefix. # # FHS-friendly layout: -# $PREFIX/bin/psp-preserve symlink -> payload binary -# $PREFIX/lib/psp-preserve/ app payload (binary + lib/) -# $PREFIX/share/applications/ desktop entry +# $PREFIX/bin/psppt symlink -> payload binary +# $PREFIX/lib/psppt/ app payload (binary + lib/) +# $PREFIX/share/applications/ desktop entry # $PREFIX/share/icons/hicolor/256x256/apps/ app icon # # The PyInstaller onedir build keeps the executable and its `lib/` contents -# directory side by side, so both live under $PREFIX/lib/psp-preserve/ and a -# symlink exposes the binary at $PREFIX/bin/psp-preserve. +# directory side by side, so both live under $PREFIX/lib/psppt/ and a +# symlink exposes the binary at $PREFIX/bin/psppt. # # Usage (run from inside the extracted tarball): # sudo ./install.sh # install to /usr/local (default) @@ -29,11 +29,11 @@ for arg in "$@"; do done SRC_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) -LIBDIR="$PREFIX/lib/psp-preserve" +LIBDIR="$PREFIX/lib/psppt" BINDIR="$PREFIX/bin" APPDIR="$PREFIX/share/applications" ICONDIR="$PREFIX/share/icons/hicolor/256x256/apps" -APP_BIN="$LIBDIR/psp-preserve" +APP_BIN="$LIBDIR/psppt" require_root() { if [ "$(id -u)" -ne 0 ]; then @@ -43,9 +43,9 @@ require_root() { } uninstall() { - rm -f "$BINDIR/psp-preserve" - rm -f "$APPDIR/psp-preserve.desktop" - rm -f "$ICONDIR/psp-preserve.png" + rm -f "$BINDIR/psppt" + rm -f "$APPDIR/psppt.desktop" + rm -f "$ICONDIR/psppt.png" rm -rf "$LIBDIR" echo "Uninstalled from $PREFIX" } @@ -59,7 +59,7 @@ fi require_root # The tarball ships these next to this script. -for f in psp-preserve lib psp-preserve.desktop app-icon.png; do +for f in psppt lib psppt.desktop app-icon.png; do if [ ! -e "$SRC_DIR/$f" ]; then echo "missing $f (run install.sh from inside the extracted tarball)" >&2 exit 1 @@ -69,13 +69,13 @@ done mkdir -p "$LIBDIR" "$BINDIR" "$APPDIR" "$ICONDIR" rm -rf "$LIBDIR" mkdir -p "$LIBDIR" -cp -a "$SRC_DIR/psp-preserve" "$LIBDIR/psp-preserve" +cp -a "$SRC_DIR/psppt" "$LIBDIR/psppt" cp -a "$SRC_DIR/lib" "$LIBDIR/lib" -install -m 0644 "$SRC_DIR/psp-preserve.desktop" "$APPDIR/psp-preserve.desktop" -install -m 0644 "$SRC_DIR/app-icon.png" "$ICONDIR/psp-preserve.png" -ln -sfn "$APP_BIN" "$BINDIR/psp-preserve" +install -m 0644 "$SRC_DIR/psppt.desktop" "$APPDIR/psppt.desktop" +install -m 0644 "$SRC_DIR/app-icon.png" "$ICONDIR/psppt.png" +ln -sfn "$APP_BIN" "$BINDIR/psppt" echo "Installed to $PREFIX" -echo " binary: $BINDIR/psp-preserve -> $APP_BIN" +echo " binary: $BINDIR/psppt -> $APP_BIN" echo " payload: $LIBDIR" -echo " desktop: $APPDIR/psp-preserve.desktop" +echo " desktop: $APPDIR/psppt.desktop" diff --git a/packaging/psp-preserve.desktop b/packaging/psppt.desktop similarity index 57% rename from packaging/psp-preserve.desktop rename to packaging/psppt.desktop index 7d02c9d..d97d7d4 100644 --- a/packaging/psp-preserve.desktop +++ b/packaging/psppt.desktop @@ -1,9 +1,9 @@ [Desktop Entry] Type=Application -Name=PSP Preservation Tool +Name=psppt Comment=Preserve and manage your PSP library -Exec=psp-preserve -Icon=psp-preserve +Exec=psppt +Icon=psppt Terminal=false Categories=Utility;AudioVideo; -StartupWMClass=psp-preserve +StartupWMClass=psppt diff --git a/psp-preserve.spec b/psppt.spec similarity index 87% rename from psp-preserve.spec rename to psppt.spec index 20fc587..aff27d0 100644 --- a/psp-preserve.spec +++ b/psppt.spec @@ -1,13 +1,13 @@ # -*- mode: python ; coding: utf-8 -*- -"""PyInstaller spec: build the PSP Preservation Tool (onedir). +"""PyInstaller spec: build psppt (onedir). Bundles the read-only resources (DATs, PS1 loader, offline cover bundles, icons) under ``lib/`` next to the executable. User-writable data (credentials, manual covers, PS1 templates, NPS cache, library DB, cover cache) lives in the OS state dir at runtime — see ``app/config.py``. -Usage: pyinstaller --clean --noconfirm psp-preserve.spec -Output: dist/psp-preserve/ +Usage: pyinstaller --clean --noconfirm psppt.spec +Output: dist/psppt/ """ from PyInstaller.utils.hooks import collect_dynamic_libs, collect_submodules @@ -24,7 +24,7 @@ datas = [ ("data/covers/UMD_GAME_COVER_TEMPLATE.png", "data/covers"), ("img/app-icon.png", "img"), ("img/chevron.png", "img"), - ("packaging/psp-preserve.desktop", "."), + ("packaging/psppt.desktop", "."), ] a = Analysis( @@ -47,7 +47,7 @@ exe = EXE( a.scripts, [], exclude_binaries=True, - name="psp-preserve", + name="psppt", debug=False, bootloader_ignore_signals=False, strip=False, @@ -63,5 +63,5 @@ coll = COLLECT( a.datas, strip=False, upx=False, - name="psp-preserve", + name="psppt", )