store: add NoPayStation source (PSP + PS1 Classics .pkg)
New 'NoPayStation' option in the Store tab lists PSP_GAMES.tsv and PSX_GAMES.tsv, downloads each item's encrypted .pkg and decrypts/extracts it with pkg2zip into a configurable output dir (<dir>/PSP/<TITLE_ID>/ and <dir>/PSX/<TITLE_ID>/). Downloaded PSP games and PS1 Classics appear in the Library (games/eboot and ps1/eboot respectively) and copy to the PSP as PSP/GAME folders. - app/nopaystation.py: TSV fetch/parse (cached) + pkg2zip discovery/extract. - app/transfers.py: NoPayStationSource (scan + download). - Settings: NPS output dir + pkg2zip path (QSettings nps_dir/pkg2zip_path). - StorePage: NPS source + content-type combo (PSP Games / PS1 Classics), per-item cover system, TSV release dates, nps-download job kind. - Library: _nps_library_items ingest, on-PSP detection, folder copy/delete, PPSSPP play disabled for encrypted PSP EBOOTs. - README: document the source, pkg2zip + npdrm_free requirements.
This commit is contained in:
@@ -463,3 +463,49 @@ class LocalShareSource(Source):
|
||||
return dest
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
class NoPayStationSource(Source):
|
||||
"""Browse NoPayStation .pkg lists and install PSP/PSX PSN content.
|
||||
|
||||
``content_type`` is ``"psp"`` (PSP_GAMES.tsv) or ``"psx"`` (PSX_GAMES.tsv).
|
||||
Downloading decrypts the ``.pkg`` with the ``pkg2zip`` binary and writes a
|
||||
``<TITLE_ID>/EBOOT.PBP`` game folder into ``dest_dir``.
|
||||
"""
|
||||
|
||||
label = "NoPayStation"
|
||||
|
||||
def __init__(self, content_type, tsv_url="", pkg2zip_path=""):
|
||||
from . import nopaystation
|
||||
self.content_type = content_type
|
||||
self.tsv_url = tsv_url or nopaystation.tsv_url(content_type)
|
||||
self.pkg2zip = pkg2zip_path or ""
|
||||
|
||||
def scan(self, root=None):
|
||||
from . import nopaystation
|
||||
return nopaystation.fetch_items(self.content_type, self.tsv_url)
|
||||
|
||||
def download(self, item, dest_dir, progress_cb=None, cancel_event=None):
|
||||
from . import nopaystation
|
||||
dest_dir = Path(dest_dir)
|
||||
dest_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
pkg2zip = nopaystation.find_pkg2zip(self.pkg2zip)
|
||||
if pkg2zip is None:
|
||||
raise RuntimeError(
|
||||
"pkg2zip not found — install it (https://github.com/mmozeiko/pkg2zip) "
|
||||
"or set its path in Settings → Store."
|
||||
)
|
||||
title_id = item.get("title_id") or item.get("content_id") or ""
|
||||
pkg_name = re.sub(r"[^A-Za-z0-9_-]", "", title_id) or "pkg"
|
||||
tmp = Path(tempfile.mkdtemp(prefix="nps-download-"))
|
||||
pkg = tmp / f"{pkg_name}.pkg"
|
||||
try:
|
||||
if progress_cb is not None:
|
||||
progress_cb("Downloading", 0.0, "")
|
||||
download_url(item["pkg_url"], pkg, None, progress_cb, cancel_event)
|
||||
folder = nopaystation.extract_pkg(pkg, title_id, dest_dir, pkg2zip,
|
||||
progress_cb, cancel_event)
|
||||
return folder
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
Reference in New Issue
Block a user