Skip to content

Commit 44856ca

Browse files
committed
1.4.6
1 parent 7aad2b5 commit 44856ca

16 files changed

Lines changed: 747 additions & 81 deletions

File tree

.github/workflows/app-package.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
workflow_dispatch:
77
inputs:
88
tag:
9-
description: "Version tag for naming (example: v1.4.5)"
9+
description: "Version tag for naming (example: v1.4.6)"
1010
required: false
1111
default: ""
1212

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,25 @@
33
All notable changes to this project are documented here.
44
This project follows Semantic Versioning.
55

6+
## [1.4.6] - 2026-02-04
7+
8+
### Added
9+
- Payload/desktop/app startup verification for payload send flows (upload success now also validates payload startup/version).
10+
- New **Mad Max** payload fast path for large single-file uploads using direct raw streaming.
11+
- New parallel offset fast path (`UPLOAD_FAST_OFFSET`) for Mad Max large-file chunk uploads.
12+
- New **Mad Max** UI mode in the Transfer source panel (grouped with Optimize/Deep Optimize).
13+
14+
### Changed
15+
- Desktop "current" payload reload now prefers local `payload/ps5upload.elf` before release download.
16+
- Mad Max now runs fixed max settings (locked profile): payload connections/workers, chunk size, no compression, unlimited bandwidth.
17+
- Mad Max is now selectable for archive sources from the same optimize controls area.
18+
- Payload startup defaults to a safer initialization profile to reduce firmware-dependent load crashes.
19+
- Payload upload pipeline defaults tuned for higher throughput (larger recv buffer and deeper worker queues).
20+
21+
### Fixed
22+
- Reduced false-positive payload send success by failing early when payload startup verification does not pass.
23+
- Improved diagnostics around payload reload/startup mismatches (for example sending new ELF while old payload keeps running).
24+
625
## [1.4.5] - 2026-02-03
726

827
### Added

FAQ.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# PS5 Upload FAQ
22

33
Welcome! This FAQ covers setup, features, troubleshooting, and platform‑specific tips.
4-
Latest release: **v1.4.5**.
4+
Latest release: **v1.4.6**.
55

66
---
77

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</p>
66

77
PS5 Upload is a fast, reliable way to move apps and homebrew to your PS5 without the pain of slow transfers.
8-
Current release: **v1.4.5**.
8+
Current release: **v1.4.6**.
99

1010
New UI highlights:
1111
- Cleaner Transfer and Manage layouts with clearer transfer settings.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.4.5
1+
1.4.6

app/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "ps5upload-app",
33
"private": true,
4-
"version": "1.4.5",
4+
"version": "1.4.6",
55
"description": "PS5Upload remote app service (backend + frontend)",
66
"scripts": {
77
"start": "node server.js",

app/server.js

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function createTransferStatus(overrides = {}) {
5252
const defaultConfig = {
5353
address: '192.168.0.100',
5454
storage: '/data',
55-
connections: 4,
55+
connections: 6,
5656
ftp_connections: 6,
5757
use_temp: false,
5858
auto_connect: false,
@@ -1403,6 +1403,22 @@ function payloadPathIsElf(filepath) {
14031403
return ext === '.elf' || ext === '.bin';
14041404
}
14051405

1406+
function findLocalPayloadElf() {
1407+
const candidates = [
1408+
path.resolve(__dirname, '../payload/ps5upload.elf'),
1409+
path.resolve(process.cwd(), 'payload/ps5upload.elf'),
1410+
path.resolve(process.cwd(), 'ps5upload.elf'),
1411+
];
1412+
for (const p of candidates) {
1413+
try {
1414+
if (fs.existsSync(p) && payloadPathIsElf(p)) return p;
1415+
} catch {
1416+
// ignore
1417+
}
1418+
}
1419+
return null;
1420+
}
1421+
14061422
function probePayloadFile(filepath) {
14071423
if (!payloadPathIsElf(filepath)) {
14081424
return { is_ps5upload: false, code: 'payload_probe_invalid_ext' };
@@ -1435,6 +1451,26 @@ async function sendPayloadFile(ip, filepath) {
14351451
});
14361452
}
14371453

1454+
async function waitForPayloadStartup(ip, expectedVersion = null, timeoutMs = 15000, pollMs = 500) {
1455+
const startedAt = Date.now();
1456+
let lastErr = null;
1457+
while (Date.now() - startedAt < timeoutMs) {
1458+
try {
1459+
const version = await getPayloadVersion(ip, TRANSFER_PORT);
1460+
if (!expectedVersion || String(version) === String(expectedVersion)) {
1461+
return { ok: true, version };
1462+
}
1463+
return { ok: false, version, error: `Running ${version}, expected ${expectedVersion}` };
1464+
} catch (err) {
1465+
lastErr = err;
1466+
}
1467+
// eslint-disable-next-line no-await-in-loop
1468+
// eslint-disable-next-line no-await-in-loop
1469+
await new Promise((resolve) => setTimeout(resolve, Math.max(100, Number(pollMs) || 500)));
1470+
}
1471+
return { ok: false, version: null, error: `Payload did not start in ${Math.round(timeoutMs / 1000)}s: ${lastErr && lastErr.message ? lastErr.message : String(lastErr || 'timeout')}` };
1472+
}
1473+
14381474
function compareVersions(a, b) {
14391475
const ma = String(a || '').match(VERSION_RE);
14401476
const mb = String(b || '').match(VERSION_RE);
@@ -1728,12 +1764,31 @@ async function handleInvoke(cmd, args, runtime) {
17281764
const filepath = (args && args.path ? String(args.path) : '').trim();
17291765
if (!ip) throw new Error('Enter a PS5 address first.');
17301766
if (!filepath) throw new Error('Select a payload (.elf/.bin) file first.');
1731-
return sendPayloadFile(ip, filepath);
1767+
const sent = await sendPayloadFile(ip, filepath);
1768+
const probe = probePayloadFile(filepath);
1769+
if (probe && probe.is_ps5upload) {
1770+
const startup = await waitForPayloadStartup(ip, runtime.version, 15000, 500);
1771+
if (!startup.ok) {
1772+
throw new Error(`Payload upload completed but startup verification failed: ${startup.error}`);
1773+
}
1774+
}
1775+
return sent;
17321776
}
17331777
case 'payload_download_and_send': {
17341778
const ip = (args && args.ip ? String(args.ip) : '').trim();
17351779
const fetchMode = (args && args.fetch ? String(args.fetch) : 'latest').trim();
17361780
if (!ip) throw new Error('Enter a PS5 address first.');
1781+
if (fetchMode === 'current') {
1782+
const localPayload = findLocalPayloadElf();
1783+
if (localPayload) {
1784+
const sent = await sendPayloadFile(ip, localPayload);
1785+
const startup = await waitForPayloadStartup(ip, runtime.version, 15000, 500);
1786+
if (!startup.ok) {
1787+
throw new Error(`Payload upload completed but startup verification failed: ${startup.error}`);
1788+
}
1789+
return sent;
1790+
}
1791+
}
17371792
let release;
17381793
if (fetchMode === 'current') {
17391794
try {
@@ -1750,7 +1805,13 @@ async function handleInvoke(cmd, args, runtime) {
17501805
if (!asset || !asset.browser_download_url) throw new Error('Payload asset not found in release');
17511806
const tmpPath = path.join(os.tmpdir(), `ps5upload_${fetchMode}.elf`);
17521807
await downloadAsset(asset.browser_download_url, tmpPath);
1753-
return sendPayloadFile(ip, tmpPath);
1808+
const sent = await sendPayloadFile(ip, tmpPath);
1809+
const expected = fetchMode === 'current' ? runtime.version : null;
1810+
const startup = await waitForPayloadStartup(ip, expected, 15000, 500);
1811+
if (!startup.ok) {
1812+
throw new Error(`Payload upload completed but startup verification failed: ${startup.error}`);
1813+
}
1814+
return sent;
17541815
}
17551816
case 'payload_check': {
17561817
const ip = (args && args.ip ? String(args.ip) : '').trim();

0 commit comments

Comments
 (0)