UNPKG

igir

Version:

🕹 A zero-setup ROM collection manager that sorts, filters, extracts or archives, patches, and reports on collections of any size on any OS.

149 lines (148 loc) • 6.71 kB
import { isNotJunk } from "junk"; import FileFactory from "../factories/fileFactory.js"; import ArchiveEntry from "../models/files/archives/archiveEntry.js"; import Gzip from "../models/files/archives/gzip.js"; import Tar from "../models/files/archives/tar.js"; import { ChecksumBitmask } from "../models/files/fileChecksums.js"; import ArrayUtil from "../utils/arrayUtil.js"; import FsUtil from "../utils/fsUtil.js"; import Module from "./module.js"; class Scanner extends Module { options; mappableSemaphore; fileFactory; constructor(options, progressBar, fileFactory, mappableSemaphore, loggerPrefix) { super(progressBar, loggerPrefix); this.options = options; this.mappableSemaphore = mappableSemaphore; this.fileFactory = fileFactory; } async getFilesFromPaths(filePaths, checksumBitmask, shouldChecksumArchives = false) { return (await this.mappableSemaphore.map(filePaths, async (inputFile) => { this.progressBar.incrementInProgress(); const childBar = this.progressBar.addChildBar({ name: inputFile, total: await FsUtil.size(inputFile), progressFormatter: FsUtil.sizeReadable.bind(FsUtil) }); let files; try { files = await this.getFilesFromPath( inputFile, checksumBitmask, shouldChecksumArchives, childBar ); } finally { childBar.delete(); } if (checksumBitmask) { files = files.map((file) => { return file.withProps({ crc32: /^[0-9a-f]{8}$/.test(file.getCrc32() ?? "") ? file.getCrc32() : void 0, crc32WithoutHeader: /^[0-9a-f]{8}$/.test(file.getCrc32WithoutHeader() ?? "") ? file.getCrc32WithoutHeader() : void 0, md5: /^[0-9a-f]{32}$/.test(file.getMd5() ?? "") ? file.getMd5() : void 0, md5WithoutHeader: /^[0-9a-f]{32}$/.test(file.getMd5WithoutHeader() ?? "") ? file.getMd5WithoutHeader() : void 0, sha1: /^[0-9a-f]{40}$/.test(file.getSha1() ?? "") ? file.getSha1() : void 0, sha1WithoutHeader: /^[0-9a-f]{40}$/.test(file.getSha1WithoutHeader() ?? "") ? file.getSha1WithoutHeader() : void 0, sha256: /^[0-9a-f]{64}$/.test(file.getSha256() ?? "") ? file.getSha256() : void 0, sha256WithoutHeader: /^[0-9a-f]{64}$/.test(file.getSha256WithoutHeader() ?? "") ? file.getSha256WithoutHeader() : void 0 }); }); files = files.map((file) => { if (file instanceof ArchiveEntry && this.options.getInputChecksumQuick()) { return file; } return file.withProps({ crc32: checksumBitmask & ChecksumBitmask.CRC32 ? file.getCrc32() : void 0, crc32WithoutHeader: checksumBitmask & ChecksumBitmask.CRC32 ? file.getCrc32WithoutHeader() : void 0, md5: checksumBitmask & ChecksumBitmask.MD5 ? file.getMd5() : void 0, md5WithoutHeader: checksumBitmask & ChecksumBitmask.MD5 ? file.getMd5WithoutHeader() : void 0, sha1: checksumBitmask & ChecksumBitmask.SHA1 ? file.getSha1() : void 0, sha1WithoutHeader: checksumBitmask & ChecksumBitmask.SHA1 ? file.getSha1WithoutHeader() : void 0, sha256: checksumBitmask & ChecksumBitmask.SHA256 ? file.getSha256() : void 0, sha256WithoutHeader: checksumBitmask & ChecksumBitmask.SHA256 ? file.getSha256WithoutHeader() : void 0, checksumBitmask }); }); } this.logWarnings(files); this.progressBar.incrementCompleted(); return files; })).flat(); } async getUniqueFilesFromPaths(filePaths, checksumBitmask) { if (checksumBitmask === ChecksumBitmask.NONE) { throw new Error("must provide ChecksumBitmask when getting unique files"); } const foundFiles = await this.getFilesFromPaths(filePaths, checksumBitmask); return foundFiles.filter(ArrayUtil.filterUniqueMapped((file) => file.hashCode())); } async getFilesFromPath(filePath, checksumBitmask, shouldChecksumArchives, progressBar) { try { if (await FsUtil.isSymlink(filePath)) { const realFilePath = await FsUtil.readlinkResolved(filePath); if (!await FsUtil.exists(realFilePath)) { this.prefixedLogger.warn(`${filePath}: broken symlink, '${realFilePath}' doesn't exist`); return []; } } const filesFromPath = await this.fileFactory.filesFrom( filePath, checksumBitmask, this.options.getInputChecksumQuick() ? ChecksumBitmask.NONE : checksumBitmask, (progress, total) => { progressBar.setCompleted(progress); if (total !== void 0) { progressBar.setTotal(total); } } ); for (const fileFromPath of filesFromPath) { if (fileFromPath instanceof ArchiveEntry && FileFactory.isExtensionArchive(fileFromPath.getExtractedFilePath())) { this.prefixedLogger.warn( `${filePath}: can't scan archives within archives: ${fileFromPath.getExtractedFilePath()}` ); } } const isFileAnArchive = filesFromPath.some((file) => file instanceof ArchiveEntry); if (shouldChecksumArchives && isFileAnArchive) { filesFromPath.push( await this.fileFactory.fileFrom(filePath, checksumBitmask, (progress) => { progressBar.setCompleted(progress); }) ); } if (filesFromPath.length === 0) { if (this.options.getInputChecksumQuick()) { this.prefixedLogger.warn( `${filePath}: didn't find any files in the archive, try disabling --input-checksum-quick` ); } else { this.prefixedLogger.warn(`${filePath}: didn't find any files in the archive`); } } return filesFromPath.filter( (fileFromPath) => isNotJunk(fileFromPath.getFilePath()) && (!(fileFromPath instanceof ArchiveEntry) || isNotJunk(fileFromPath.getExtractedFilePath())) ); } catch (error) { this.prefixedLogger.error(`${filePath}: failed to parse file: ${error}`); return []; } } logWarnings(files) { if (!this.options.getInputChecksumQuick()) { return; } const archiveWithoutChecksums = files.filter((file) => file instanceof ArchiveEntry).map((archiveEntry) => archiveEntry.getArchive()).find((archive) => archive instanceof Gzip || archive instanceof Tar); if (archiveWithoutChecksums !== void 0) { this.prefixedLogger.warn( `${archiveWithoutChecksums.getFilePath()}: quick checksums will skip ${archiveWithoutChecksums.getExtension()} files` ); } } } export { Scanner as default }; //# sourceMappingURL=scanner.js.map