From d02152ca06e0f2307fa28e19270caade5d9172c6 Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Mon, 7 Sep 2026 21:32:50 -0400 Subject: [PATCH] fix NoPayStation PSP parsing + detail-panel UnboundLocalError MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PSP_GAMES.tsv and PSX_GAMES.tsv use different headers (PSP adds Type/RAP columns, PSX adds Original Name). Parse the header row and map columns by name instead of hardcoding positions — PSP names were showing as 'Minis'/ 'PC Engine' (the Type column) with broken download URLs. - GameDetailsPanel.set_item referenced is_ps1 before it was assigned, raising UnboundLocalError and leaving the detail panel stale. --- app/gui.py | 2 +- app/nopaystation.py | 55 +++++++++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/app/gui.py b/app/gui.py index 4aecf4a..97f89dd 100644 --- a/app/gui.py +++ b/app/gui.py @@ -682,13 +682,13 @@ class GameDetailsPanel(QWidget): self.system = item.get("system") self._ps1_kind = item.get("ps1_kind") path = item.get("path") or "" + is_ps1 = item.get("system") == "ps1" if (is_ps1 and item.get("ps1_kind") == "eboot") or item.get("psp_kind") == "eboot": self.filename.setText(Path(path).parent.name if path else "") else: self.filename.setText(Path(path).name if path else "") self.filename.setToolTip(path) self.filename.setVisible(bool(path)) - is_ps1 = item.get("system") == "ps1" is_ps1_disc = is_ps1 and item.get("ps1_kind") == "disc" self.play_btn.setEnabled( (item.get("system") == "games" and bool(path) and Path(path).suffix.lower() == ".iso") diff --git a/app/nopaystation.py b/app/nopaystation.py index 6ba924f..e700376 100644 --- a/app/nopaystation.py +++ b/app/nopaystation.py @@ -23,11 +23,24 @@ log = logging.getLogger("nopaystation") _TSV_NAMES = {"psp": "PSP_GAMES.tsv", "psx": "PSX_GAMES.tsv"} -# TSV columns (tab-separated), in order. -_COLUMNS = ( - "title_id", "region", "name", "pkg_url", "zrif", "content_id", - "date", "size", "sha256", -) +# NoPayStation TSVs are tab-separated with a header row whose exact columns +# differ per platform (PSP has "Type"/"RAP" columns, PSX has "Original Name"). +# Map header names to canonical keys instead of relying on fixed positions. +_HEADER_ALIASES = { + "title id": "title_id", + "region": "region", + "name": "name", + "pkg direct link": "pkg_url", + "content id": "content_id", + "last modification date": "date", + "file size": "size", + "sha256": "sha256", + "zrif": "zrif", + "rap": "rap", + "download .rap file": "rap_url", + "type": "content_kind", + "original name": "original_name", +} def tsv_url(content_type: str, base: str = "") -> str: @@ -57,29 +70,32 @@ def _parse_date(value: str) -> str: def parse_tsv(text: str, content_type: str) -> List[dict]: """Parse a NoPayStation TSV into store item dicts. - Each item carries the fields the Store page expects (``name``, ``size``, + The header row drives column mapping (PSP and PSX TSVs differ). Each item + carries the fields the Store page expects (``name``, ``size``, ``release_date``, ``mtime_ns``) plus ``title_id``, ``pkg_url``, ``sha256``, ``content_type`` and the cover ``system`` ("games" for PSP, "ps1" for PSX). """ system = "games" if content_type == "psp" else "ps1" items: List[dict] = [] - lines = text.splitlines() - for line in lines: + header = None + for line in text.splitlines(): line = line.rstrip("\r\n") if not line.strip(): continue cols = line.split("\t") - # Skip the header row. - if cols and cols[0].strip().lower() == "title id": + if header is None: + header = [c.strip().lower() for c in cols] continue - if len(cols) < 5: - continue - row = dict(zip(_COLUMNS, cols)) - name = (row.get("name") or "").strip() - pkg_url = (row.get("pkg_url") or "").strip() - title_id = (row.get("title_id") or "").strip() + row = {} + for i, key in enumerate(header): + canon = _HEADER_ALIASES.get(key) + if canon and i < len(cols): + row[canon] = cols[i].strip() + name = row.get("name") or "" + pkg_url = row.get("pkg_url") or "" if not name or not pkg_url: continue + title_id = row.get("title_id") or "" try: size = int(float(row.get("size") or 0)) except (TypeError, ValueError): @@ -92,16 +108,17 @@ def parse_tsv(text: str, content_type: str) -> List[dict]: items.append({ "name": name, "title_id": title_id, - "region": (row.get("region") or "").strip(), + "region": row.get("region") or "", "pkg_url": pkg_url, - "zrif": (row.get("zrif") or "").strip(), - "content_id": (row.get("content_id") or "").strip(), + "zrif": row.get("zrif") or "", + "content_id": row.get("content_id") or "", "size": size, "sha256": (row.get("sha256") or "").strip().lower(), "release_date": date, "mtime_ns": mtime_ns, "content_type": content_type, "system": system, + "content_kind": row.get("content_kind") or "", }) return items