[chore] updating naming sheme of downloaded maps to avoid duplication when downloading maps from other tools

+ updating naming sheme of downloading playlists
+ updating download models due to function changes
This commit is contained in:
MathieuG-P
2024-10-26 21:22:35 +02:00
parent 27f36dfa54
commit 4bbfe72381
4 changed files with 76 additions and 22 deletions
@@ -141,9 +141,11 @@ export class LocalMapsManagerService {
const fileName = `${path.basename(zipUrl, ".zip")}-${crypto.randomUUID()}.zip`;
const tempPath = this.utils.getTempPath();
await ensureFolderExist(this.utils.getTempPath());
const dest = path.join(tempPath, fileName);
const zipPath = (await lastValueFrom(this.reqService.downloadFile(zipUrl, dest))).data;
const zipPath = (await lastValueFrom(this.reqService.downloadFile(zipUrl, {
destFolder: tempPath,
filename: fileName
}))).data;
const zip = new StreamZip.async({ file: zipPath });
return { zip, zipPath };
@@ -263,7 +265,7 @@ export class LocalMapsManagerService {
}
const zipUrl = map.versions.at(0).downloadURL;
const mapFolderName = sanitize(`${map.id}-${map.name}`);
const mapFolderName = sanitize(`${map.id} (${map.metadata.songName} - ${map.metadata.levelAuthorName})`);
const mapsFolder = await this.getMapsFolderPath(version);
const mapPath = path.join(mapsFolder, mapFolderName);
@@ -87,12 +87,14 @@ export class LocalModelsManagerService {
(async () => {
const modelFolder = await this.getModelFolderPath(model.type, version);
const modelDest = path.join(modelFolder, sanitize(path.basename(model.download)));
const url = model.download.split("/");
url[url.length - 1] = encodeURIComponent(url[url.length - 1]);
const download$ = this.request.downloadFile(url.join("/"), modelDest);
const download$ = this.request.downloadFile(url.join("/"), {
destFolder: modelFolder,
filename: sanitize(path.basename(model.download))
});
subs.push(download$.subscribe({ next: value => subscriber.next({ ...value, data: undefined }), error: e => subscriber.error(e) }));
@@ -77,9 +77,13 @@ export class LocalPlaylistsManagerService {
if (isLocalFile) {
return copyFile(bslistSource, destFile).then(() => destFile);
}
return lastValueFrom(this.request.downloadFile(bslistSource, destFile)).then(res => res.data);
return lastValueFrom(this.request.downloadFile(bslistSource, {
destFolder: playlistFolder,
filename,
preferContentDisposition: true,
})).then(res => res.data);
}
private async readPlaylistFile(path: string): Promise<BPList> {
if (!(await pathExist(path))) {
@@ -102,8 +106,8 @@ export class LocalPlaylistsManagerService {
const bpListFilePath = await this.installBPListFile(bpListUrl, version);
const bpList = await this.readPlaylistFile(bpListFilePath);
const progress: Progression<DownloadPlaylistProgressionData> = {
const progress: Progression<DownloadPlaylistProgressionData> = {
total: bpList.songs.length,
current: 0,
data: {
+59 -13
View File
@@ -1,11 +1,14 @@
import { Agent, RequestOptions, get } from "https";
import { createWriteStream, unlink } from "fs";
import { Progression } from "main/helpers/fs.helpers";
import { createWriteStream } from "fs";
import { Progression, unlinkPath } from "../helpers/fs.helpers";
import { Observable, shareReplay, tap } from "rxjs";
import log from "electron-log";
import fetch, { RequestInfo, RequestInit } from "node-fetch";
import { app } from "electron";
import os from "os";
import path from "path";
import { tryit } from "../../shared/helpers/error.helpers";
import sanitize from "sanitize-filename";
export class RequestService {
private static instance: RequestService;
@@ -56,22 +59,65 @@ export class RequestService {
}
}
public downloadFile(url: string, dest: string): Observable<Progression<string>> {
private getFilenameFromContentDisposition(disposition: string): string | null {
if(!disposition) {
return null;
}
const utf8FilenameRegex = /filename\*=UTF-8''([\w%\-\.]+)(?:; ?|$)/i;
const asciiFilenameRegex = /^filename=(["']?)(.*?[^\\])\1(?:; ?|$)/i;
const utf8Match = utf8FilenameRegex.exec(disposition);
if (utf8Match) {
return decodeURIComponent(utf8Match[1]);
}
const filenameStart = disposition.toLowerCase().indexOf('filename=');
if (filenameStart >= 0) {
const partialDisposition = disposition.slice(filenameStart);
const asciiMatch = asciiFilenameRegex.exec(partialDisposition);
if (asciiMatch && asciiMatch[2]) {
return asciiMatch[2];
}
}
return null;
}
public downloadFile(url: string, opt: { destFolder: string, filename: string, preferContentDisposition?: boolean }): Observable<Progression<string>> {
return new Observable<Progression<string>>(subscriber => {
const progress: Progression<string> = { current: 0, total: 0 };
const file = createWriteStream(dest);
file.on("close", () => {
progress.data = dest;
subscriber.next(progress);
subscriber.complete();
});
file.on("error", err => unlink(dest, () => subscriber.error(err)));
const req = get(url, this.requestOptionsFromDefaultInit(), res => {
const filePath = (() => {
if(opt.preferContentDisposition && res.headers["content-disposition"]) {
const filename = this.getFilenameFromContentDisposition(res.headers["content-disposition"]);
return path.join(opt.destFolder, sanitize(filename || opt.filename));
}
return path.join(opt.destFolder, opt.filename);
})();
const file = createWriteStream(filePath);
file.on("close", () => {
subscriber.next(progress);
subscriber.complete();
});
file.on("error", async err => {
log.error(err);
const res = await tryit(() => unlinkPath(filePath));
if(res.error) {
log.error(res.error);
}
});
progress.data = filePath;
progress.total = parseInt(res.headers?.["content-length"] || "0", 10);
subscriber.next(progress);
res.on("data", chunk => {
progress.current += chunk.length;
subscriber.next(progress);
@@ -83,7 +129,7 @@ export class RequestService {
req.on("error", err => {
subscriber.error(err);
});
}).pipe(tap({ error: e => log.error(e, url, dest) }), shareReplay(1));
}).pipe(tap({ error: e => log.error(e, url) }), shareReplay(1));
}
public downloadBuffer(url: string): Observable<Progression<Buffer>> {