diff --git a/app/downloads/DownloadRow.jsx b/app/downloads/DownloadRow.jsx index 950defd..d3dfaa0 100644 --- a/app/downloads/DownloadRow.jsx +++ b/app/downloads/DownloadRow.jsx @@ -23,6 +23,15 @@ import { Text, View } from '@ps5-react/core'; var PRIMARY = { width: 198 }; var ARM_MS = 3e3; var primaryKey = (id) => `downloads:${id}`; + // A source the catalog lists without a format (FileKeeper's, mostly) showed "BINARY" until the file + // was done. Its kind is known long before: from the header the queue reads in the first megabytes, or + // from the name the host gave the file. + var FILE_KIND = /\.(exfat|ffpfsc|ffpfs|ffpkg|fpkg|pkg|rar|7z|zip)$/i; + function shownFormat(job2, release) { + const format = job2.artifact.format || release.format; + if (format && format !== "binary") return format; + return job2.inspected || FILE_KIND.exec(job2.fileName || "")?.[1] || format || "binary"; + } function primary({ state, error: error2 }, run, openGame) { if (state === "pausing") return { icon: "pause", label: "downloads.action.pausing", onPress: () => { } }; @@ -71,7 +80,7 @@ import { Text, View } from '@ps5-react/core'; const openGame = () => onOpenGame(job2); const main = primary(job2, (name) => run(job2, name), openGame); const onFocus = () => onFocusRow(job2); - const meta = [(job2.artifact.format || release.format).toUpperCase(), `${release.provider} ${release.version || ""}`.trim(), job2.root].join(" \xB7 "); + const meta = [shownFormat(job2, release).toUpperCase(), `${release.provider} ${release.version || ""}`.trim(), job2.root].join(" \xB7 "); const problem = job2.error && explainError(job2.error, Boolean(release.parts)); // The files' own title id differs from the game chosen: said before anything else but an error. const mismatch = job2.titleMismatch ? t2("downloads.titleMismatch", { found: job2.titleMismatch, expected: job2.titleId }) : ""; diff --git a/app/files/useFiles.js b/app/files/useFiles.js index ea7023f..5b7a402 100644 --- a/app/files/useFiles.js +++ b/app/files/useFiles.js @@ -72,6 +72,10 @@ import { FileSystem, Http, Packages, Platform } from '@ps5-react/core'; task.done.then( () => { setInstalling((state) => ({ ...state, fraction: 1, done: true })); + try { + downloadQueue().packageInstalled(file.path); + } catch { + } rescan(); }, (error2) => setInstalling((state) => ({ ...state, error: error2.message })) diff --git a/app/state/download-queue.js b/app/state/download-queue.js index 3e3168e..99efe0f 100644 --- a/app/state/download-queue.js +++ b/app/state/download-queue.js @@ -1646,6 +1646,21 @@ import { VIKING_PAGE_PREFIX, VIKING_PREFIX, describeVikingCapture, linkPageMetad }, /** Marks finished images (all, or job `id`'s) ShadowMount has installed since as installed; false when none or it can't be asked. */ syncInstalls, + /** A package the Files screen installed by itself: the download it came from no longer offers "Install + * PKG" (testers, 2026-10-18). Only a download still waiting for its install moves. */ + packageInstalled(path) { + let found = false; + for (const job2 of jobs) { + const paths = [job2.artifact?.destination, job2.downloadDestination, job2.output, ...job2.packages ?? []]; + if (job2.state !== "installer-required" || !paths.includes(path)) continue; + job2.state = "installed"; + job2.message = t("downloads.installed"); + job2.error = ""; + found = true; + } + if (found) change(); + return found; + }, /** Frees the engine's one archive task, for the updater: a set extracting while it downloads extracts after its download instead. */ async yieldArchives() { const streamed = active2?.streamed;