mirror of
https://github.com/knutwurst/patchdl.git
synced 2026-10-23 12:01:12 +02:00
Library - Rescan when /user/app or Sony's app.db changes, after an install, and on the Refresh button (which also asks Sony for updates again). Later scans keep a handle to the system root and use relative paths instead of swapping the process root while other threads run; app.db is copied out first because SQLite resolves relative paths with getcwd(). The scan thread gets a 1 MB stack (the default made reads fail with EFAULT). - Take the newer of app.db and appmeta param.json as installed version: app.db lags behind until the game is started. - A package header decides "fake"; the installer's "fake": true marker only says the package didn't come from Sony's CDN, and PatchDL's own installs set it too. - PS4 titles are listed as not supported. Downloads - Fetch the manifest's .crc next to the package; the console asks for it. - Keep the sidecar (with the version) after a download completes, so a restart still knows the package is whole. - A package already on disk without a matching sidecar is hashed against the manifest piece by piece; only pieces that don't match are fetched. Install refuses an unverified package. Installs - One install per title: a second request (another tab, a double click) gets install_running; an installed version gets already_installed. Download, Cancel and Delete wait until the console is done reading. - Progress comes from the bytes PatchDL serves to the console (distinct ranges), since Sony's status query fails for these installs. Phases: waiting, reading with percent, finishing, done or stalled. Each title is tracked on its own. - After an install: check that the installed version matches and the new app.json names this package and its size. With "Delete PKG after install" on, delete the package only then. The setting existed but was never read; the old cleanup trusted app.db alone and deleted packages that were never applied. UI - Settings list every package on the console with size, version and whether it is installed, newer or for a game that's gone, with Delete. The storage tile links there. - The list reloads quietly when the server reports a change. - Bigger covers on wide screens, "Updates on/off" on the switch, no "Shared master" badge, no endless auto-install retries.
267 lines
11 KiB
HTML
267 lines
11 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||
<title>PatchDL</title>
|
||
<link rel="stylesheet" href="styles.css" />
|
||
</head>
|
||
<body>
|
||
<svg class="icon-sprite" aria-hidden="true">
|
||
<symbol id="icon-download" viewBox="0 0 24 24">
|
||
<path d="M12 3v11" /><path d="M7 10l5 5 5-5" /><path d="M5 20h14" />
|
||
</symbol>
|
||
<symbol id="icon-refresh" viewBox="0 0 24 24">
|
||
<path d="M20 6v6h-6" /><path d="M4 18v-6h6" />
|
||
<path d="M19 12A7 7 0 0 0 7 7" /><path d="M5 12a7 7 0 0 0 12 5" />
|
||
</symbol>
|
||
<symbol id="icon-settings" viewBox="0 0 24 24">
|
||
<path d="M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8z" />
|
||
<path d="M4 12h2M18 12h2M12 4v2M12 18v2M6.5 6.5 8 8M16 16l1.5 1.5M17.5 6.5 16 8M8 16l-1.5 1.5" />
|
||
</symbol>
|
||
<symbol id="icon-search" viewBox="0 0 24 24">
|
||
<path d="M10.5 18a7.5 7.5 0 1 1 0-15 7.5 7.5 0 0 1 0 15z" /><path d="M16 16l5 5" />
|
||
</symbol>
|
||
<symbol id="icon-save" viewBox="0 0 24 24">
|
||
<path d="M5 4h12l2 2v14H5V4z" /><path d="M8 4v6h8V4" /><path d="M8 20v-6h8v6" />
|
||
</symbol>
|
||
<symbol id="icon-log" viewBox="0 0 24 24">
|
||
<path d="M7 4h10l3 3v13H7V4z" /><path d="M17 4v4h4" /><path d="M10 12h7M10 16h5" />
|
||
</symbol>
|
||
<symbol id="icon-grid" viewBox="0 0 24 24">
|
||
<path d="M4 4h7v7H4zM13 4h7v7h-7zM4 13h7v7H4zM13 13h7v7h-7z" />
|
||
</symbol>
|
||
</svg>
|
||
|
||
<div class="app-shell">
|
||
<aside class="rail" aria-label="Navigation">
|
||
<div class="brand">
|
||
<div class="brand-mark">PD</div>
|
||
<div>
|
||
<strong>PatchDL</strong>
|
||
<span>by Knutwurst · <span id="brandVersion">--</span></span>
|
||
</div>
|
||
</div>
|
||
|
||
<nav class="nav-list" aria-label="Sections">
|
||
<button class="nav-link is-active" data-view="games" aria-label="Games" aria-current="page">
|
||
<svg><use href="#icon-grid"></use></svg><span>Games</span>
|
||
</button>
|
||
<button class="nav-link" data-view="settings" aria-label="Settings">
|
||
<svg><use href="#icon-settings"></use></svg><span>Settings</span>
|
||
</button>
|
||
<button class="nav-link" data-view="logs" aria-label="Logs">
|
||
<svg><use href="#icon-log"></use></svg><span>Logs</span>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="rail-foot">
|
||
<span id="railFw">FW --</span>
|
||
<span id="railSpace">-- free</span>
|
||
</div>
|
||
</aside>
|
||
|
||
<main class="main">
|
||
<!-- ============ GAMES ============ -->
|
||
<section class="view is-active" data-view="games">
|
||
<header class="topbar">
|
||
<div>
|
||
<p class="eyebrow">PS5 update manager</p>
|
||
<h1>Games</h1>
|
||
</div>
|
||
<div class="topbar-actions">
|
||
<button class="primary-button" id="updateAllBtn" title="Download (and optionally install) every game that has an available, allowed update">
|
||
<svg><use href="#icon-download"></use></svg>
|
||
Update all
|
||
</button>
|
||
<button class="icon-button" id="refreshBtn" title="Refresh status and games" aria-label="Refresh">
|
||
<svg><use href="#icon-refresh"></use></svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<aside id="globalDl" class="global-dl" hidden aria-live="polite">
|
||
<div class="global-dl-row">
|
||
<span class="global-dl-pulse" aria-hidden="true"></span>
|
||
<div class="global-dl-text">
|
||
<div class="global-dl-line1">
|
||
<span class="global-dl-eyebrow" id="globalDlState">Downloading</span>
|
||
<strong class="global-dl-name" id="globalDlName">—</strong>
|
||
<span class="global-dl-position" id="globalDlPosition" hidden></span>
|
||
</div>
|
||
<div class="global-dl-line2">
|
||
<span id="globalDlPct">0%</span>
|
||
<span class="global-dl-sep" aria-hidden="true">·</span>
|
||
<span id="globalDlSpeed">—</span>
|
||
<span class="global-dl-sep" aria-hidden="true">·</span>
|
||
<span id="globalDlEta">—</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="global-dl-track">
|
||
<i class="global-dl-bar" id="globalDlBar" style="width:0%"></i>
|
||
</div>
|
||
</aside>
|
||
|
||
<section class="status-strip" aria-label="System status">
|
||
<article class="metric">
|
||
<span>Firmware</span>
|
||
<strong id="firmwareValue">--</strong>
|
||
<em id="firmwareBuild">System version</em>
|
||
</article>
|
||
<article class="metric">
|
||
<span>DNS Guard</span>
|
||
<strong id="dnsValue">--</strong>
|
||
<em>System DNS left untouched</em>
|
||
</article>
|
||
<article class="metric">
|
||
<span>CDN Access</span>
|
||
<strong id="resolverValue">--</strong>
|
||
<em>PatchDL resolver only</em>
|
||
</article>
|
||
<article class="metric">
|
||
<span>Storage</span>
|
||
<strong id="spaceValue">--</strong>
|
||
<em id="downloadDirValue">Download target</em>
|
||
</article>
|
||
</section>
|
||
|
||
<div class="toolbar">
|
||
<div class="search-box">
|
||
<svg><use href="#icon-search"></use></svg>
|
||
<input id="searchInput" type="search" placeholder="Search title, Title ID, or Content ID" />
|
||
</div>
|
||
<div class="segmented" role="group" aria-label="Filter">
|
||
<button class="is-selected" data-filter="updates" aria-pressed="true">Updates <span class="chip-count"></span></button>
|
||
<button data-filter="uptodate" aria-pressed="false">Up to date <span class="chip-count"></span></button>
|
||
<button data-filter="blocked" aria-pressed="false">Not supported <span class="chip-count"></span></button>
|
||
<button data-filter="all" aria-pressed="false">All <span class="chip-count"></span></button>
|
||
</div>
|
||
</div>
|
||
|
||
<section class="game-grid" id="gameGrid"></section>
|
||
</section>
|
||
|
||
<!-- ============ SETTINGS ============ -->
|
||
<section class="view" data-view="settings">
|
||
<header class="topbar">
|
||
<div>
|
||
<p class="eyebrow">Policy</p>
|
||
<h1>Settings</h1>
|
||
</div>
|
||
<div class="topbar-actions">
|
||
<button class="primary-button" id="saveBtn">
|
||
<svg><use href="#icon-save"></use></svg>
|
||
Save
|
||
</button>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="panel">
|
||
<div class="settings-grid">
|
||
<label class="field">
|
||
<span>Default Policy</span>
|
||
<select id="defaultPolicy">
|
||
<option value="deny">Deny by default</option>
|
||
<option value="allow">Allow by default</option>
|
||
</select>
|
||
</label>
|
||
<label class="field">
|
||
<span>Download Folder</span>
|
||
<input id="downloadDir" type="text" spellcheck="false" readonly
|
||
title="Patches always download to the console's internal storage" />
|
||
</label>
|
||
|
||
<label class="switch-row">
|
||
<span>
|
||
<strong>Install after download</strong>
|
||
<em>Global default, overridable per game</em>
|
||
</span>
|
||
<span class="toggle">
|
||
<input id="installAfterDownload" type="checkbox" aria-label="Install after download" />
|
||
<span class="track"></span>
|
||
</span>
|
||
</label>
|
||
<label class="switch-row">
|
||
<span>
|
||
<strong>Delete PKG after install</strong>
|
||
<em>Only after a successful install</em>
|
||
</span>
|
||
<span class="toggle">
|
||
<input id="deleteAfterInstall" type="checkbox" aria-label="Delete package after install" />
|
||
<span class="track"></span>
|
||
</span>
|
||
</label>
|
||
<label class="switch-row">
|
||
<span>
|
||
<strong>Verify downloaded pieces (SHA-256)</strong>
|
||
<em>Checks each manifest piece; off by default, verify on-device first</em>
|
||
</span>
|
||
<span class="toggle">
|
||
<input id="verifyDownloads" type="checkbox" aria-label="Verify downloaded pieces" />
|
||
<span class="track"></span>
|
||
</span>
|
||
</label>
|
||
<label class="switch-row">
|
||
<span>
|
||
<strong>Home-screen shortcut</strong>
|
||
<em id="shortcutHint">Add a PS5 home-screen icon that opens this UI in the browser</em>
|
||
</span>
|
||
<span class="toggle">
|
||
<input id="homeShortcut" type="checkbox" aria-label="Install home-screen shortcut" />
|
||
<span class="track"></span>
|
||
</span>
|
||
</label>
|
||
<div class="switch-row">
|
||
<span>
|
||
<strong>Parallel download connections</strong>
|
||
<em>1–16 · applies live, no payload restart</em>
|
||
</span>
|
||
<div class="stepper" role="group" aria-label="Parallel download connections">
|
||
<button type="button" class="stepper-btn" id="connMinus" aria-label="Fewer connections">−</button>
|
||
<output class="stepper-value" id="connValue" aria-live="polite">4</output>
|
||
<button type="button" class="stepper-btn" id="connPlus" aria-label="More connections">+</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="allowlist">
|
||
<span>Internal CDN Allowlist</span>
|
||
<div id="allowlistHosts"></div>
|
||
</div>
|
||
|
||
<div class="source-policy">
|
||
<span>Install Protection</span>
|
||
<div id="sourcePolicyList"></div>
|
||
</div>
|
||
|
||
<div class="storage-list">
|
||
<span>Downloads on console <em id="storageTotal"></em></span>
|
||
<div id="storageList"></div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============ LOGS ============ -->
|
||
<section class="view" data-view="logs">
|
||
<header class="topbar">
|
||
<div>
|
||
<p class="eyebrow">Runtime</p>
|
||
<h1>Logs</h1>
|
||
</div>
|
||
<div class="topbar-actions">
|
||
<button class="icon-button" id="clearLogBtn" title="Clear log" aria-label="Clear log">
|
||
<svg><use href="#icon-log"></use></svg>
|
||
</button>
|
||
</div>
|
||
</header>
|
||
<pre id="logOutput" tabindex="0"></pre>
|
||
</section>
|
||
</main>
|
||
</div>
|
||
|
||
<div class="toast" id="toast" role="status" aria-live="polite"></div>
|
||
<script src="app.js"></script>
|
||
</body>
|
||
</html>
|