refactor: makes queues extends one class with better error handling
This commit is contained in:
parent
4337b2b1d7
commit
9359f0c20a
10 changed files with 151 additions and 225 deletions
|
|
@ -47,6 +47,7 @@ export async function AssetsDownloader(
|
|||
const pool = new DownloadPool(files, {
|
||||
pQueueOptions: { concurrency: 5 },
|
||||
overwrite: false,
|
||||
updateSize: true
|
||||
});
|
||||
|
||||
return pool;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ export async function LibrariesDownloader(
|
|||
const dPool = new DownloadPool(libs, {
|
||||
pQueueOptions: { concurrency: 5 },
|
||||
overwrite: false,
|
||||
updateSize: true
|
||||
});
|
||||
|
||||
return dPool;
|
||||
|
|
|
|||
|
|
@ -240,6 +240,8 @@ export async function ModrinthDownloader(
|
|||
pQueueOptions: {
|
||||
concurrency: 5,
|
||||
},
|
||||
updateSize: true,
|
||||
overwrite: true
|
||||
});
|
||||
|
||||
modrinthDownloader.on("completed", () => {
|
||||
|
|
@ -305,16 +307,13 @@ export async function OverridesCopier(
|
|||
instancePath: string,
|
||||
emit: Instance["emit"],
|
||||
) {
|
||||
const overridesCopyQueue = new CopyQueue(
|
||||
addedOverrides,
|
||||
overridesFolder,
|
||||
instancePath,
|
||||
{
|
||||
pQueueOptions: {
|
||||
concurrency: 5,
|
||||
},
|
||||
const overridesCopyQueue = new CopyQueue(addedOverrides, {
|
||||
pQueueOptions: {
|
||||
concurrency: 5,
|
||||
},
|
||||
);
|
||||
source: overridesFolder,
|
||||
destination: instancePath,
|
||||
});
|
||||
overridesCopyQueue.on("completed", () => {
|
||||
emit(
|
||||
"progress",
|
||||
|
|
|
|||
|
|
@ -9,18 +9,24 @@ import { DownloadAndUnzipPool } from "../utils/unzip";
|
|||
function getClassifier(library: Library): Native {
|
||||
switch (os()) {
|
||||
case "osx":
|
||||
return library.downloads!.classifiers!["natives-osx"] ||
|
||||
library.downloads!.classifiers!["natives-macos"];
|
||||
return (
|
||||
library.downloads!.classifiers!["natives-osx"] ||
|
||||
library.downloads!.classifiers!["natives-macos"]
|
||||
);
|
||||
case "linux":
|
||||
return library.downloads!.classifiers!["natives-linux"];
|
||||
case "windows":
|
||||
switch (arch()) {
|
||||
case "x86":
|
||||
return library.downloads!.classifiers!["natives-windows-32"] ||
|
||||
library.downloads!.classifiers!["natives-windows"];
|
||||
return (
|
||||
library.downloads!.classifiers!["natives-windows-32"] ||
|
||||
library.downloads!.classifiers!["natives-windows"]
|
||||
);
|
||||
case "x64":
|
||||
return library.downloads!.classifiers!["natives-windows-64"] ||
|
||||
library.downloads!.classifiers!["natives-windows"];
|
||||
return (
|
||||
library.downloads!.classifiers!["natives-windows-64"] ||
|
||||
library.downloads!.classifiers!["natives-windows"]
|
||||
);
|
||||
default:
|
||||
return library.downloads!.classifiers!["natives-windows"];
|
||||
}
|
||||
|
|
@ -36,7 +42,7 @@ async function getNatives(versionManifest: Version, tempNativesPath: string) {
|
|||
if (library.downloads && library.downloads.classifiers) {
|
||||
if (!isNeeded(library)) continue;
|
||||
|
||||
const native = getClassifier(library)
|
||||
const native = getClassifier(library);
|
||||
|
||||
const destination = path.join(
|
||||
tempNativesPath,
|
||||
|
|
@ -60,18 +66,14 @@ export async function NativesDownloader(
|
|||
|
||||
const natives = await getNatives(versionManifest, tempNativesPath);
|
||||
|
||||
const pool = new DownloadAndUnzipPool(
|
||||
natives,
|
||||
nativesPath,
|
||||
tempNativesPath,
|
||||
[".git", "META-INF", ".sha1"],
|
||||
"skip",
|
||||
{
|
||||
pQueueOptions: {
|
||||
concurrency: 5,
|
||||
},
|
||||
const pool = new DownloadAndUnzipPool(natives, nativesPath, tempNativesPath, {
|
||||
pQueueOptions: {
|
||||
concurrency: 5,
|
||||
},
|
||||
);
|
||||
overwrite: false,
|
||||
filters: [".git", "META-INF", ".sha1"],
|
||||
updateSize: true
|
||||
});
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export async function importModrinthModpack(
|
|||
}
|
||||
|
||||
await ensureDir(path.join(tempFolder, "pack"), true);
|
||||
await unzipAll(pack, path.join(tempFolder, "pack"), [], "overwrite");
|
||||
await unzipAll(pack, path.join(tempFolder, "pack"), [], true);
|
||||
const manifest = await readJson<ModrinthManifest>(
|
||||
path.join(tempFolder, "pack", "modrinth.index.json"),
|
||||
);
|
||||
|
|
@ -53,7 +53,7 @@ export async function importModrinthModpack(
|
|||
updateFrom,
|
||||
path.join(tempFolder, "oldPack"),
|
||||
[],
|
||||
"overwrite",
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import fs from "fs";
|
||||
import { pipeline } from "stream/promises";
|
||||
import { PoolFile, PoolOptions } from "./types";
|
||||
import { DownloadPoolOptions, PoolFile } from "./types";
|
||||
import { ensureDir, exists } from "./fs";
|
||||
import path from "path";
|
||||
import PQueue from "p-queue";
|
||||
import { Queue } from "./queue";
|
||||
|
||||
export async function fetchJson<T>(url: string): Promise<T> {
|
||||
return await (await fetch(url)).json();
|
||||
|
|
@ -27,47 +27,14 @@ export async function downloadFile(file: PoolFile, overwrite = true) {
|
|||
}
|
||||
}
|
||||
|
||||
export class DownloadPool extends PQueue {
|
||||
done = 0;
|
||||
total = 0;
|
||||
doneSize = 0;
|
||||
totalSize = 0;
|
||||
elements: PoolFile[];
|
||||
cleanup?: () => Promise<void>;
|
||||
overwrite: boolean;
|
||||
|
||||
export class DownloadPool extends Queue<PoolFile> {
|
||||
constructor(
|
||||
elements: PoolFile[],
|
||||
options: PoolOptions = { pQueueOptions: {}, overwrite: true },
|
||||
options: DownloadPoolOptions,
|
||||
) {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
super(options.pQueueOptions);
|
||||
this.elements = elements;
|
||||
this.total = this.elements.length;
|
||||
this.cleanup = options.cleanup;
|
||||
if (options.overwrite) {
|
||||
this.overwrite = true;
|
||||
} else {
|
||||
this.overwrite = false;
|
||||
}
|
||||
super(elements, options);
|
||||
}
|
||||
|
||||
async run() {
|
||||
for (const element of this.elements) {
|
||||
this.totalSize += element.size!;
|
||||
|
||||
this.add(async () => {
|
||||
await downloadFile(element, this.overwrite);
|
||||
// update status here to ensure that it is run before "completed" events listeners
|
||||
this.done++;
|
||||
this.doneSize += element.size!;
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await Promise.race([this.onError(), this.onIdle()]);
|
||||
|
||||
if (this.cleanup) {
|
||||
await this.cleanup();
|
||||
}
|
||||
async action(element: PoolFile, options: DownloadPoolOptions): Promise<void> {
|
||||
await downloadFile(element, options.overwrite);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
144
src/utils/fs.ts
144
src/utils/fs.ts
|
|
@ -1,7 +1,7 @@
|
|||
import fs from "fs/promises";
|
||||
import PQueue from "p-queue";
|
||||
import { type Options as PQueueOptions } from "p-queue";
|
||||
import path from "path";
|
||||
import { Queue } from "./queue";
|
||||
import { CopyQueueOptions } from "./types";
|
||||
|
||||
export async function exists(path: string) {
|
||||
try {
|
||||
|
|
@ -23,134 +23,32 @@ export async function readJson<T>(destination: string): Promise<T> {
|
|||
return JSON.parse(await fs.readFile(destination, { encoding: "utf-8" }));
|
||||
}
|
||||
|
||||
export class DeleteQueue extends PQueue {
|
||||
done = 0;
|
||||
total = 0;
|
||||
doneSize = 0;
|
||||
totalSize = 0;
|
||||
elements: string[];
|
||||
cleanup?: () => Promise<void>;
|
||||
export class DeleteQueue extends Queue<string> {
|
||||
async action(element: string): Promise<void> {
|
||||
await fs.rm(element, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyQueue extends Queue<string> {
|
||||
constructor(
|
||||
elements: string[],
|
||||
options: {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
pQueueOptions: PQueueOptions<null, null>;
|
||||
cleanup?: () => Promise<void>;
|
||||
} = {
|
||||
pQueueOptions: {},
|
||||
},
|
||||
options: CopyQueueOptions,
|
||||
) {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
super(options.pQueueOptions);
|
||||
this.elements = elements;
|
||||
this.total = this.elements.length;
|
||||
this.cleanup = options.cleanup;
|
||||
super(elements, options);
|
||||
}
|
||||
|
||||
async run() {
|
||||
for (const element of this.elements) {
|
||||
this.add(async () => {
|
||||
await fs.rm(element, { recursive: true, force: true });
|
||||
this.done++;
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await Promise.race([this.onError(), this.onIdle()]);
|
||||
if (this.cleanup) {
|
||||
await this.cleanup();
|
||||
}
|
||||
async action(element: string, options: CopyQueueOptions): Promise<void> {
|
||||
await fs.cp(
|
||||
path.join(options.source, element),
|
||||
path.join(options.destination, element),
|
||||
{
|
||||
force: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class CopyQueue extends PQueue {
|
||||
done = 0;
|
||||
total = 0;
|
||||
doneSize = 0;
|
||||
totalSize = 0;
|
||||
files: string[];
|
||||
source: string;
|
||||
destination: string;
|
||||
cleanup?: () => Promise<void>;
|
||||
|
||||
constructor(
|
||||
files: string[],
|
||||
source: string,
|
||||
destination: string,
|
||||
options: {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
pQueueOptions: PQueueOptions<null, null>;
|
||||
cleanup?: () => Promise<void>;
|
||||
} = {
|
||||
pQueueOptions: {},
|
||||
},
|
||||
) {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
super(options.pQueueOptions);
|
||||
this.files = files;
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
this.total = this.files.length;
|
||||
this.cleanup = options.cleanup;
|
||||
}
|
||||
|
||||
async run() {
|
||||
for (const file of this.files) {
|
||||
this.add(async () => {
|
||||
await fs.cp(
|
||||
path.join(this.source, file),
|
||||
path.join(this.destination, file),
|
||||
{
|
||||
force: true,
|
||||
},
|
||||
);
|
||||
this.done++;
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await Promise.race([this.onError(), this.onIdle()]);
|
||||
if (this.cleanup) {
|
||||
await this.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class TreeMakerQueue extends PQueue {
|
||||
done = 0;
|
||||
total = 0;
|
||||
doneSize = 0;
|
||||
totalSize = 0;
|
||||
addedDirs: string[];
|
||||
cleanup?: () => Promise<void>;
|
||||
|
||||
constructor(
|
||||
addedDirs: string[],
|
||||
options: {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
pQueueOptions: PQueueOptions<null, null>;
|
||||
cleanup?: () => Promise<void>;
|
||||
} = {
|
||||
pQueueOptions: {},
|
||||
},
|
||||
) {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
super(options.pQueueOptions);
|
||||
this.addedDirs = addedDirs;
|
||||
this.total = this.addedDirs.length;
|
||||
this.cleanup = options.cleanup;
|
||||
}
|
||||
|
||||
async run() {
|
||||
for (const dir of this.addedDirs) {
|
||||
this.add(async () => {
|
||||
await ensureDir(dir, true);
|
||||
this.done++;
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
await Promise.race([this.onError(), this.onIdle()]);
|
||||
if (this.cleanup) {
|
||||
await this.cleanup();
|
||||
}
|
||||
export class TreeMakerQueue extends Queue<string> {
|
||||
async action(element: string): Promise<void> {
|
||||
await ensureDir(element, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
56
src/utils/queue.ts
Normal file
56
src/utils/queue.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import PQueue from "p-queue";
|
||||
import { PoolOptions } from "./types";
|
||||
|
||||
export abstract class Queue<element> extends PQueue {
|
||||
done = 0;
|
||||
total = 0;
|
||||
doneSize = 0;
|
||||
totalSize = 0;
|
||||
elements: element[];
|
||||
options: PoolOptions;
|
||||
|
||||
constructor(
|
||||
elements: element[],
|
||||
options: PoolOptions = { pQueueOptions: {} },
|
||||
) {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
super(options.pQueueOptions);
|
||||
this.elements = elements;
|
||||
this.total = this.elements.length;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
protected abstract action(
|
||||
element: element,
|
||||
options: PoolOptions,
|
||||
): Promise<void>;
|
||||
|
||||
async run() {
|
||||
for (const element of this.elements) {
|
||||
if (this.options.updateSize) {
|
||||
// @ts-expect-error this.updateSize === true
|
||||
this.totalSize += element.size;
|
||||
|
||||
this.add(async () => {
|
||||
await this.action(element, this.options);
|
||||
// update status here to ensure that it is run before "completed" events listeners
|
||||
this.done++;
|
||||
// @ts-expect-error this.updateSize === true
|
||||
this.doneSize += element.size;
|
||||
}).catch(() => {});
|
||||
} else {
|
||||
this.add(async () => {
|
||||
await this.action(element, this.options);
|
||||
// update status here to ensure that it is run before "completed" events listeners
|
||||
this.done++;
|
||||
}).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.race([this.onError(), this.onIdle()]);
|
||||
|
||||
if (this.options.cleanup) {
|
||||
await this.options.cleanup();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -227,12 +227,25 @@ export type LauncherProfiles = {
|
|||
};
|
||||
};
|
||||
|
||||
export type PoolOptions = {
|
||||
export interface PoolOptions {
|
||||
//@ts-expect-error can't figure out what to put as arguments of PQueueOptions
|
||||
pQueueOptions: PQueueOptions<null, null>;
|
||||
pQueueOptions?: PQueueOptions<null, null>;
|
||||
cleanup?: () => Promise<void>;
|
||||
updateSize?: boolean;
|
||||
}
|
||||
|
||||
export interface DownloadPoolOptions extends PoolOptions {
|
||||
overwrite?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface DownloadAndUnzipPoolOptions extends DownloadPoolOptions {
|
||||
filters: string[];
|
||||
}
|
||||
|
||||
export interface CopyQueueOptions extends PoolOptions {
|
||||
destination: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
export interface InstanceEvents {
|
||||
progress: [
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
import AdmZip from "adm-zip";
|
||||
import { PoolFile, PoolOptions } from "./types";
|
||||
import { downloadFile, DownloadPool } from "./fetch";
|
||||
import { DownloadAndUnzipPoolOptions, PoolFile } from "./types";
|
||||
import { downloadFile } from "./fetch";
|
||||
import path from "path";
|
||||
import { exists } from "./fs";
|
||||
import { Queue } from "./queue";
|
||||
|
||||
export async function unzipAll(from: string, to: string, filters: string[], mode: "overwrite" | "skip") {
|
||||
export async function unzipAll(
|
||||
from: string,
|
||||
to: string,
|
||||
filters: string[],
|
||||
overwrite: boolean,
|
||||
) {
|
||||
const zip = new AdmZip(from);
|
||||
|
||||
for (const entry of zip.getEntries()) {
|
||||
|
|
@ -15,54 +21,37 @@ export async function unzipAll(from: string, to: string, filters: string[], mode
|
|||
}
|
||||
}
|
||||
if (shouldSkip) continue;
|
||||
if (!await exists(path.join(to, entry.entryName)) || mode === "overwrite") {
|
||||
zip.extractEntryTo(entry.entryName, to, true, mode === "overwrite");
|
||||
if (!(await exists(path.join(to, entry.entryName))) || overwrite) {
|
||||
zip.extractEntryTo(entry.entryName, to, true, overwrite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DownloadAndUnzipPool extends DownloadPool {
|
||||
export class DownloadAndUnzipPool extends Queue<PoolFile> {
|
||||
destination: string;
|
||||
tempPath: string;
|
||||
filters: string[];
|
||||
unzipMode: "overwrite" | "skip"
|
||||
|
||||
constructor(
|
||||
elements: PoolFile[],
|
||||
destination: string,
|
||||
tempPath: string,
|
||||
filters: string[] = [],
|
||||
unzipMode: "overwrite" | "skip",
|
||||
options: PoolOptions = { pQueueOptions: {}, overwrite: true },
|
||||
options: DownloadAndUnzipPoolOptions = {
|
||||
pQueueOptions: {},
|
||||
overwrite: true,
|
||||
filters: [],
|
||||
},
|
||||
) {
|
||||
super(elements, options);
|
||||
this.tempPath = tempPath;
|
||||
this.destination = destination;
|
||||
this.filters = filters;
|
||||
this.unzipMode = unzipMode
|
||||
}
|
||||
|
||||
async downloadAndUnzip(file: PoolFile, filters: string[], unzipMode: "overwrite" | "skip") {
|
||||
async action(file: PoolFile, options: DownloadAndUnzipPoolOptions) {
|
||||
const tempFile = path.join(this.tempPath, path.basename(file.path));
|
||||
await downloadFile({
|
||||
url: file.url,
|
||||
path: tempFile,
|
||||
});
|
||||
unzipAll(tempFile, this.destination, filters, unzipMode);
|
||||
}
|
||||
|
||||
async run() {
|
||||
for (const element of this.elements) {
|
||||
this.totalSize += element.size!;
|
||||
|
||||
this.add(async () => {
|
||||
await this.downloadAndUnzip(element, this.filters, this.unzipMode);
|
||||
// update status here to ensure that it is run before "completed" events listeners
|
||||
this.done++;
|
||||
this.doneSize += element.size!;
|
||||
});
|
||||
}
|
||||
|
||||
await this.onIdle();
|
||||
unzipAll(tempFile, this.destination, options.filters, options.overwrite ?? false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue