[hotfix] download mod zip in buffer to avoid eprem error

This commit is contained in:
MathieuG-P
2023-05-22 22:33:48 +02:00
parent 1d8a68f665
commit ba3de199fa
5 changed files with 76 additions and 42 deletions
+7 -18
View File
@@ -24,6 +24,7 @@
"fs-extra": "^11.1.1",
"history": "^5.3.0",
"is-online": "^9.0.1",
"jszip": "^3.10.1",
"md5-file": "^5.0.0",
"node-fetch": "^2.6.7",
"node-stream-zip": "^1.15.0",
@@ -9872,8 +9873,7 @@
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"dev": true
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"node_modules/immutable": {
"version": "4.1.0",
@@ -12158,7 +12158,6 @@
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dev": true,
"dependencies": {
"lie": "~3.3.0",
"pako": "~1.0.2",
@@ -12170,7 +12169,6 @@
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dev": true,
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@@ -12297,7 +12295,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"dev": true,
"dependencies": {
"immediate": "~3.0.5"
}
@@ -13831,8 +13828,7 @@
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"node_modules/param-case": {
"version": "3.0.4",
@@ -16213,8 +16209,7 @@
"node_modules/setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
"dev": true
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
},
"node_modules/setprototypeof": {
"version": "1.2.0",
@@ -26235,8 +26230,7 @@
"immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
"dev": true
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
"immutable": {
"version": "4.1.0",
@@ -28053,7 +28047,6 @@
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dev": true,
"requires": {
"lie": "~3.3.0",
"pako": "~1.0.2",
@@ -28065,7 +28058,6 @@
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
@@ -28178,7 +28170,6 @@
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
"dev": true,
"requires": {
"immediate": "~3.0.5"
}
@@ -29308,8 +29299,7 @@
"pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
"dev": true
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
"param-case": {
"version": "3.0.4",
@@ -31014,8 +31004,7 @@
"setimmediate": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
"dev": true
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
},
"setprototypeof": {
"version": "1.2.0",
+1
View File
@@ -244,6 +244,7 @@
"fs-extra": "^11.1.1",
"history": "^5.3.0",
"is-online": "^9.0.1",
"jszip": "^3.10.1",
"md5-file": "^5.0.0",
"node-fetch": "^2.6.7",
"node-stream-zip": "^1.15.0",
+23
View File
@@ -0,0 +1,23 @@
import JSZip from 'jszip';
import { pathExist } from './fs.helpers';
import path from 'path';
import { mkdir, writeFile } from 'fs/promises';
export async function extractZip(zip: JSZip, dest: string): Promise<string[]>{
if(!await pathExist(dest)){ throw new Error(`Path ${dest} does not exist`); }
const files: string[] = [];
await zip.forEach(async (relativePath, entry) => {
if(entry.dir){ return; }
const content = await entry.async("nodebuffer");
const outPath = path.join(dest, relativePath);
const outDir = path.dirname(outPath);
await mkdir(outDir, { recursive: true });
await writeFile(outPath, content);
files.push(outPath);
});
return files;
}
@@ -6,13 +6,15 @@ import path from "path";
import { UtilsService } from "../utils.service";
import md5File from "md5-file";
import fs from "fs"
import StreamZip from "node-stream-zip";
import { RequestService } from "../request.service";
import { spawn } from "child_process";
import { BS_EXECUTABLE } from "../../constants";
import log from "electron-log";
import { deleteFolder, ensureFolderExist, pathExist, unlinkPath } from "../../helpers/fs.helpers";
import { deleteFolder, pathExist, unlinkPath } from "../../helpers/fs.helpers";
import { lastValueFrom } from "rxjs";
import JSZip from "jszip"
import { extractZip } from "../../helpers/zip.helpers";
import { ensureFolderExist } from "../../helpers/fs.helpers";
export class BsModsManagerService {
@@ -96,17 +98,20 @@ export class BsModsManagerService {
return this.getIpaFromHash(injectorMd5);
}
private async downloadZip(zipUrl: string): Promise<{zip: StreamZip.StreamZipAsync, zipPath: string}>{
private async downloadZip(zipUrl: string): Promise<JSZip>{
zipUrl = path.join(this.beatModsApi.BEAT_MODS_URL, zipUrl);
const fileName = path.basename(zipUrl);
const tempPath = this.utilsService.getTempPath();
await ensureFolderExist(this.utilsService.getTempPath());
const dest = path.join(tempPath, fileName);
const zipPath = (await lastValueFrom(this.requestService.downloadFile(zipUrl, dest))).data;
const zip = new StreamZip.async({file : zipPath});
const buffer = await lastValueFrom(this.requestService.downloadBuffer(zipUrl)).then(progress => progress.data).catch(e => {
log.error("ZIP", "Error while downloading zip", e);
return undefined
});
return {zip, zipPath};
if(!buffer){ return null; }
return JSZip.loadAsync(buffer).catch(e => {
log.error("ZIP", "Error while loading zip", e);
return null
});
}
private async executeBSIPA(version: BSVersion, args: string[]): Promise<boolean>{
@@ -135,23 +140,22 @@ export class BsModsManagerService {
}
private async installMod(mod: Mod, version: BSVersion): Promise<boolean>{
this.utilsService.ipcSend<ModInstallProgression>("mod-installed", {success: true, data: {name: mod.name, progression: ((this.nbInstalledMods + 1) / this.nbModsToInstall) * 100}})
const download = this.getModDownload(mod, version);
if(!download){ return false; }
const {zip, zipPath} = await this.downloadZip(download.url);
const zip = await this.downloadZip(download.url);
if(!zip){ return false; }
const crypto = require('crypto');
const entries = await zip.entries();
const files = await zip.files;
const checkedEntries = (await Promise.all(Object.values(entries).map(async (entry) => {
if(!entry.isFile){ return undefined; }
const data = await zip.entryData(entry);
const checkedEntries = (await Promise.all(Object.values(files).map(async (entry) => {
const data = await entry.async("nodebuffer");
const entryMd5 = crypto.createHash('md5').update(data).digest('hex')
return download.hashMd5.some(md5 => md5.hash === entryMd5) ? entry : undefined;
}))).filter(entry => !!entry);
@@ -162,12 +166,16 @@ export class BsModsManagerService {
const isBSIPA = mod.name.toLowerCase() === "bsipa";
const destDir = isBSIPA ? verionPath : path.join(verionPath, ModsInstallFolder.PENDING);
const extracted = await zip.extract(null, destDir).then(() => true).catch(err => {log.error(err); return false});
await ensureFolderExist(destDir);
const extracted = await extractZip(zip, destDir).then(() => true).catch(e => {
log.error("EXTRACT MOD ZIP", e);
return false;
})
await zip.close();
await unlinkPath(zipPath);
const res = isBSIPA ? (extracted && (await this.executeBSIPA(version, ["-n"]))) : extracted;
const res = isBSIPA ? (extracted && (await this.executeBSIPA(version, ["-n"]).catch(e => {
log.error(e);
return false;
}))) : extracted;
res && this.nbInstalledMods++;
+16 -3
View File
@@ -61,17 +61,30 @@ export class RequestService {
}
public downloadBuffer(url: string): Observable<Buffer>{
public downloadBuffer(url: string): Observable<Progression<Buffer>>{
return new Observable<Progression<Buffer>>(subscriber => {
const progress: Progression<Buffer> = {
current: 0,
total: 0,
data: null
};
return new Observable<Buffer>(subscriber => {
const allChunks: Buffer[] = [];
const req = get(url, res => {
progress.total = parseInt(res.headers?.["content-length"] || "0", 10);
res.on("data", chunk => {
allChunks.push(chunk);
progress.current += chunk.length;
subscriber.next(progress);
});
res.on('end', () => {
subscriber.next(Buffer.concat(allChunks));
progress.data = Buffer.concat(allChunks);
subscriber.next(progress);
subscriber.complete();
});
res.on('error', (err) => subscriber.error(err))