show progress for imports and deletions
This commit is contained in:
parent
38eea05789
commit
5e342b6fe5
13 changed files with 343 additions and 115 deletions
|
|
@ -7,45 +7,16 @@ const { newGame, newGameId, addGame } = require('./library')
|
|||
const { extractIconFromExe } = require('./icons')
|
||||
const { listPrefixes, prefixDriveRoot } = require('./prefixes')
|
||||
const { rewireSymlinks } = require('./symlinks')
|
||||
const { copyTree, treeSize } = require('./fsutil')
|
||||
|
||||
// Directories that typically hold installers/redistributables, not the game.
|
||||
const SKIPPED_DIRS = new Set(['redist', 'directx', '_commonredist', 'support', 'dotnet', 'vc'])
|
||||
|
||||
// A game folder has to arrive as it was: symlinks are recreated (copyFile
|
||||
// silently dropped them, so a linked asset or save folder vanished) and the
|
||||
// permission bits are carried over (copyFile writes 0644, killing scripts and
|
||||
// anything the game executes itself).
|
||||
function copyRecursive(src, dst, onProgress) {
|
||||
return fsp.readdir(src, { withFileTypes: true }).then(async entries => {
|
||||
await fsp.mkdir(dst, { recursive: true })
|
||||
for (const e of entries) {
|
||||
const s = path.join(src, e.name)
|
||||
const d = path.join(dst, e.name)
|
||||
if (e.isSymbolicLink()) {
|
||||
let target = null
|
||||
try {
|
||||
target = await fsp.readlink(s)
|
||||
} catch {}
|
||||
if (!target) continue
|
||||
try {
|
||||
await fsp.symlink(target, d)
|
||||
} catch {}
|
||||
if (onProgress) onProgress(s)
|
||||
} else if (e.isDirectory()) {
|
||||
await copyRecursive(s, d, onProgress)
|
||||
} else if (e.isFile()) {
|
||||
await fsp.copyFile(s, d)
|
||||
try {
|
||||
await fsp.chmod(d, (await fsp.stat(s)).mode & 0o777)
|
||||
} catch {}
|
||||
if (onProgress) onProgress(s)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// The copy itself lives in fsutil (copyTree): symlinks recreated, permissions
|
||||
// carried over, byte-level progress. moveFolder only falls back to it across
|
||||
// filesystems; a same-device rename reports nothing (nothing is copied).
|
||||
function moveFolder(src, dst, onProgress) {
|
||||
const fallback = () => copyRecursive(src, dst, onProgress).then(() => fsp.rm(src, { recursive: true, force: true }))
|
||||
const fallback = () => copyTree(src, dst, onProgress).then(() => fsp.rm(src, { recursive: true, force: true }))
|
||||
if (path.parse(src).root !== path.parse(dst).root) return fallback()
|
||||
return fsp.rename(src, dst).catch(fallback)
|
||||
}
|
||||
|
|
@ -62,8 +33,15 @@ async function importGame(srcDir, mode, onProgress) {
|
|||
let workdir = srcDir
|
||||
if (mode === 'copy' || mode === 'move') {
|
||||
const dst = path.join(DIRS.APPS, id)
|
||||
if (mode === 'copy') await copyRecursive(srcDir, dst, onProgress)
|
||||
else await moveFolder(srcDir, dst, onProgress)
|
||||
if (mode === 'copy') {
|
||||
// measured before it starts, so the bar can follow the bytes landing
|
||||
const emit = (phase, detail) => {
|
||||
if (onProgress) onProgress({ phase, label: name, ...detail })
|
||||
}
|
||||
emit('count', { files: 0, bytes: 0 })
|
||||
const total = await treeSize(srcDir)
|
||||
await copyTree(srcDir, dst, p => emit('copy', { files: p.done, bytes: p.bytes, totalFiles: total.files, totalBytes: total.bytes }))
|
||||
} else await moveFolder(srcDir, dst, onProgress)
|
||||
workdir = dst
|
||||
// anything the game linked to INSIDE the launcher data (its own folder, an
|
||||
// icon, a shared prefix file) becomes a relative link, so the imported app
|
||||
|
|
@ -156,4 +134,4 @@ async function findDefaultExe(dir) {
|
|||
return best
|
||||
}
|
||||
|
||||
module.exports = { importGame, importGameFromPrefix, findDefaultExe, copyRecursive, moveFolder }
|
||||
module.exports = { importGame, importGameFromPrefix, findDefaultExe, moveFolder }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue