mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 80167a7f3f | |||
| 91cb80bbe2 | |||
| df1c7ca9d2 | |||
| 0f96db8d29 | |||
| 8042f1b9f6 | |||
| 757eb1f655 | |||
| 49fb694aaf | |||
| 508e290a24 |
@@ -818,6 +818,11 @@
|
|||||||
"multiple": "These maps will also be removed from versions using shared maps"
|
"multiple": "These maps will also be removed from versions using shared maps"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"delete-duplicate-maps": {
|
||||||
|
"title": "Delete maps?",
|
||||||
|
"desc": "Only the map \"{map}\" is a duplicate. Are you sure you want to delete it?",
|
||||||
|
"desc-plural": "{nb} duplicate maps have been found. Are you sure you want to delete them?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"link-contents":{
|
"link-contents":{
|
||||||
|
|||||||
@@ -818,6 +818,11 @@
|
|||||||
"multiple": "Queste mappe saranno rimosse anche dalle versioni che usano mappe condivise"
|
"multiple": "Queste mappe saranno rimosse anche dalle versioni che usano mappe condivise"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"delete-duplicate-maps": {
|
||||||
|
"title": "Eliminare le mappe?",
|
||||||
|
"desc": "Solo la mappa \"{map}\" è un duplicato. Sei sicuro di volerla eliminare?",
|
||||||
|
"desc-plural": "Sono state trovate {nb} mappe duplicate. Sei sicuro di volerle eliminare?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"link-contents":{
|
"link-contents":{
|
||||||
|
|||||||
@@ -818,6 +818,11 @@
|
|||||||
"multiple": "Esses mapas também serão removidos de suas versões utilizando mapas compartilhados"
|
"multiple": "Esses mapas também serão removidos de suas versões utilizando mapas compartilhados"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"delete-duplicate-maps": {
|
||||||
|
"title": "Excluir mapas?",
|
||||||
|
"desc": "Apenas o mapa \"{map}\" é um duplicado. Tem certeza de que deseja excluí-lo?",
|
||||||
|
"desc-plural": "{nb} mapas duplicados foram encontrados. Tem certeza de que deseja excluí-los?"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"link-contents":{
|
"link-contents":{
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
"name": "bs-manager",
|
"name": "bs-manager",
|
||||||
"description": "Manage maps, mods and more for Beat Saber",
|
"description": "Manage maps, mods and more for Beat Saber",
|
||||||
"main": "./.erb/dll/main.bundle.dev.js",
|
"main": "./.erb/dll/main.bundle.dev.js",
|
||||||
"version": "1.5.0",
|
"version": "1.5.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build-rust-scripts": "ts-node ./.erb/scripts/build-rust-scripts.js",
|
"build-rust-scripts": "ts-node ./.erb/scripts/build-rust-scripts.js",
|
||||||
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
|
"build": "concurrently \"npm run build:main\" \"npm run build:renderer\"",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bs-manager",
|
"name": "bs-manager",
|
||||||
"version": "1.5.0",
|
"version": "1.5.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bs-manager",
|
"name": "bs-manager",
|
||||||
"version": "1.5.0",
|
"version": "1.5.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "bs-manager",
|
"name": "bs-manager",
|
||||||
"version": "1.5.0",
|
"version": "1.5.1",
|
||||||
"description": "BSManager",
|
"description": "BSManager",
|
||||||
"main": "./dist/main/main.js",
|
"main": "./dist/main/main.js",
|
||||||
"author": {
|
"author": {
|
||||||
|
|||||||
@@ -2,6 +2,15 @@ import { mkdir, pathExistsSync, rm, writeFile } from "fs-extra";
|
|||||||
import { getSize } from "main/helpers/fs.helpers";
|
import { getSize } from "main/helpers/fs.helpers";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
|
jest.mock("electron", () => ({ app: {
|
||||||
|
getPath: () => "",
|
||||||
|
getName: () => "",
|
||||||
|
}}));
|
||||||
|
jest.mock("electron-log", () => ({
|
||||||
|
info: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
const TEST_FOLDER = path.resolve(__dirname, "..", "assets", "fs");
|
const TEST_FOLDER = path.resolve(__dirname, "..", "assets", "fs");
|
||||||
|
|
||||||
describe("Test fs.helpers getSize", () => {
|
describe("Test fs.helpers getSize", () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CopyOptions, MoveOptions, copy, createReadStream, ensureDir, move, pathExists, pathExistsSync, realpath, stat, symlink } from "fs-extra";
|
import { CopyOptions, MoveOptions, RmOptions, copy, createReadStream, ensureDir, move, pathExists, pathExistsSync, realpath, rm, stat, symlink, unlink, unlinkSync } from "fs-extra";
|
||||||
import { access, mkdir, rm, readdir, unlink, lstat, readlink } from "fs/promises";
|
import { access, mkdir, readdir, lstat, readlink } from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { Observable, concatMap, from } from "rxjs";
|
import { Observable, concatMap, from } from "rxjs";
|
||||||
import log from "electron-log";
|
import log from "electron-log";
|
||||||
@@ -28,18 +28,53 @@ export async function ensureFolderExist(path: string): Promise<void> {
|
|||||||
.then(() => {});
|
.then(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteFolder(folderPath: string): Promise<void> {
|
export async function deleteFile(filepath: string) {
|
||||||
|
try {
|
||||||
|
log.info("Deleting file", `"${filepath}"`);
|
||||||
|
await unlink(filepath);
|
||||||
|
} catch (error: any) {
|
||||||
|
log.error("Could not delete file", `"${filepath}"`);
|
||||||
|
throw CustomError.fromError(error, "generic.fs.delete-file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteFileSync(filepath: string) {
|
||||||
|
try {
|
||||||
|
log.info("Deleting file", `"${filepath}"`);
|
||||||
|
unlinkSync(filepath);
|
||||||
|
} catch (error: any) {
|
||||||
|
log.error("Could not delete file", `"${filepath}"`);
|
||||||
|
throw CustomError.fromError(error, "generic.fs.delete-file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteFolder(folderPath: string, options?: RmOptions) {
|
||||||
if (!(await pathExist(folderPath))) {
|
if (!(await pathExist(folderPath))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return rm(folderPath, { recursive: true, force: true });
|
|
||||||
|
try {
|
||||||
|
options = options || { recursive: true, force: true };
|
||||||
|
log.info("Deleting folder", `"${folderPath}"`, options);
|
||||||
|
await rm(folderPath, options);
|
||||||
|
} catch (error: any) {
|
||||||
|
log.error("Could not delete folder", `"${folderPath}"`);
|
||||||
|
throw CustomError.fromError(error, "generic.fs.delete-folder");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function unlinkPath(path: string): Promise<void> {
|
export function deleteFolderSync(folderPath: string, options?: RmOptions) {
|
||||||
if (!(await pathExist(path))) {
|
if (!pathExistsSync(folderPath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return unlink(path);
|
|
||||||
|
try {
|
||||||
|
options = options || { recursive: true, force: true };
|
||||||
|
log.info("Deleting folder", `"${folderPath}"`, options);
|
||||||
|
} catch (error: any) {
|
||||||
|
log.error("Could not delete folder", `"${folderPath}"`);
|
||||||
|
throw CustomError.fromError(error, "generic.fs.delete-folder");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getFoldersInFolder(folderPath: string, opts?: { ignoreSymlinkTargetError?: boolean }): Promise<string[]> {
|
export async function getFoldersInFolder(folderPath: string, opts?: { ignoreSymlinkTargetError?: boolean }): Promise<string[]> {
|
||||||
@@ -97,23 +132,22 @@ export function moveFolderContent(src: string, dest: string, option?: MoveOption
|
|||||||
const files = await readdir(src, { encoding: "utf-8", withFileTypes: true });
|
const files = await readdir(src, { encoding: "utf-8", withFileTypes: true });
|
||||||
progress.total = files.length;
|
progress.total = files.length;
|
||||||
|
|
||||||
for(const file of files){
|
for (const file of files) {
|
||||||
const srcFullPath = path.join(src, file.name);
|
const srcFullPath = path.join(src, file.name);
|
||||||
const destFullPath = path.join(dest, file.name);
|
const destFullPath = path.join(dest, file.name);
|
||||||
|
|
||||||
const srcChilds = file.isDirectory() ? await readdir(srcFullPath, { encoding: "utf-8", recursive: true }) : [];
|
const srcChilds = file.isDirectory() ? await readdir(srcFullPath, { encoding: "utf-8", recursive: true }) : [];
|
||||||
const allChildsAlreadyExist = srcChilds.every(child => pathExistsSync(path.join(destFullPath, child)));
|
const allChildsAlreadyExist = srcChilds.every(child => pathExistsSync(path.join(destFullPath, child)));
|
||||||
|
|
||||||
if(file.isFile() || !allChildsAlreadyExist){
|
if (file.isFile() || !allChildsAlreadyExist) {
|
||||||
const prevSize = await getSize(srcFullPath);
|
const prevSize = await getSize(srcFullPath);
|
||||||
await move(srcFullPath, destFullPath, option);
|
await move(srcFullPath, destFullPath, option);
|
||||||
const afterSize = await getSize(destFullPath);
|
const afterSize = await getSize(destFullPath);
|
||||||
|
|
||||||
// The size after moving should be the same or greater than the size before moving but never less
|
// The size after moving should be the same or greater than the size before moving but never less
|
||||||
if(afterSize < prevSize){
|
if (afterSize < prevSize) {
|
||||||
throw new CustomError(`File size mismath. before: ${prevSize}, after: ${afterSize} (${srcFullPath})`, "FILE_SIZE_MISMATCH");
|
throw new CustomError(`File size mismath. before: ${prevSize}, after: ${afterSize} (${srcFullPath})`, "FILE_SIZE_MISMATCH");
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.info(`Skipping ${srcFullPath} to ${destFullPath}, all child already exist in destination`);
|
log.info(`Skipping ${srcFullPath} to ${destFullPath}, all child already exist in destination`);
|
||||||
}
|
}
|
||||||
@@ -121,7 +155,9 @@ export function moveFolderContent(src: string, dest: string, option?: MoveOption
|
|||||||
progress.current++;
|
progress.current++;
|
||||||
subscriber.next(progress);
|
subscriber.next(progress);
|
||||||
}
|
}
|
||||||
})().catch(err => subscriber.error(CustomError.fromError(err, err?.code))).finally(() => subscriber.complete());
|
})()
|
||||||
|
.catch(err => subscriber.error(CustomError.fromError(err, err?.code)))
|
||||||
|
.finally(() => subscriber.complete());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +196,7 @@ export async function copyDirectoryWithJunctions(src: string, dest: string, opti
|
|||||||
await copy(sourcePath, destinationPath, options);
|
await copy(sourcePath, destinationPath, options);
|
||||||
} else if (item.isSymbolicLink()) {
|
} else if (item.isSymbolicLink()) {
|
||||||
if (options?.overwrite) {
|
if (options?.overwrite) {
|
||||||
await unlinkPath(destinationPath);
|
await deleteFile(destinationPath);
|
||||||
}
|
}
|
||||||
const symlinkTarget = await readlink(sourcePath);
|
const symlinkTarget = await readlink(sourcePath);
|
||||||
const relativePath = path.relative(src, symlinkTarget);
|
const relativePath = path.relative(src, symlinkTarget);
|
||||||
@@ -180,8 +216,7 @@ export function hashFile(filePath: string, algorithm = "sha256"): Promise<string
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function dirSize(dirPath: string): Promise<number>{
|
export async function dirSize(dirPath: string): Promise<number> {
|
||||||
|
|
||||||
const entries = await readdir(dirPath);
|
const entries = await readdir(dirPath);
|
||||||
|
|
||||||
const paths = entries.map(async entry => {
|
const paths = entries.map(async entry => {
|
||||||
@@ -200,11 +235,10 @@ export async function dirSize(dirPath: string): Promise<number>{
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (await Promise.all(paths)).flat(Infinity).reduce((acc, size ) => acc + size, 0);
|
return (await Promise.all(paths)).flat(Infinity).reduce((acc, size) => acc + size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function rxCopy(src: string, dest: string, option?: CopyOptions): Observable<Progression> {
|
export function rxCopy(src: string, dest: string, option?: CopyOptions): Observable<Progression> {
|
||||||
|
|
||||||
const dirSizePromise = dirSize(src).catch(err => {
|
const dirSizePromise = dirSize(src).catch(err => {
|
||||||
log.error("dirSizePromise", err);
|
log.error("dirSizePromise", err);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -216,15 +250,19 @@ export function rxCopy(src: string, dest: string, option?: CopyOptions): Observa
|
|||||||
|
|
||||||
return new Observable<Progression>(sub => {
|
return new Observable<Progression>(sub => {
|
||||||
sub.next(progress);
|
sub.next(progress);
|
||||||
copy(src, dest, {...option, filter: (src) => {
|
copy(src, dest, {
|
||||||
stat(src).then(stats => {
|
...option,
|
||||||
progress.current += stats.size;
|
filter: src => {
|
||||||
sub.next(progress);
|
stat(src).then(stats => {
|
||||||
});
|
progress.current += stats.size;
|
||||||
return true;
|
sub.next(progress);
|
||||||
}})
|
});
|
||||||
.then(() => sub.complete()).catch(err => sub.error(err))
|
return true;
|
||||||
})
|
},
|
||||||
|
})
|
||||||
|
.then(() => sub.complete())
|
||||||
|
.catch(err => sub.error(err));
|
||||||
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -257,7 +295,7 @@ export function ensurePathNotAlreadyExistSync(path: string): string {
|
|||||||
return destPath;
|
return destPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function isJunction(path: string): Promise<boolean>{
|
export async function isJunction(path: string): Promise<boolean> {
|
||||||
const [stats, lstats] = await Promise.all([stat(path), lstat(path)]);
|
const [stats, lstats] = await Promise.all([stat(path), lstat(path)]);
|
||||||
return lstats.isSymbolicLink() && stats.isDirectory();
|
return lstats.isSymbolicLink() && stats.isDirectory();
|
||||||
}
|
}
|
||||||
@@ -265,7 +303,7 @@ export async function isJunction(path: string): Promise<boolean>{
|
|||||||
export function resolveGUIDPath(guidPath: string): string {
|
export function resolveGUIDPath(guidPath: string): string {
|
||||||
const guidVolume = path.parse(guidPath).root;
|
const guidVolume = path.parse(guidPath).root;
|
||||||
const command = `powershell -command "(Get-WmiObject -Class Win32_Volume | Where-Object { $_.DeviceID -like '${guidVolume}' }).DriveLetter"`;
|
const command = `powershell -command "(Get-WmiObject -Class Win32_Volume | Where-Object { $_.DeviceID -like '${guidVolume}' }).DriveLetter"`;
|
||||||
const {result: driveLetter, error} = tryit(() => execSync(command).toString().trim());
|
const { result: driveLetter, error } = tryit(() => execSync(command).toString().trim());
|
||||||
if (!driveLetter || error) {
|
if (!driveLetter || error) {
|
||||||
throw new Error("Unable to resolve GUID path", error);
|
throw new Error("Unable to resolve GUID path", error);
|
||||||
}
|
}
|
||||||
@@ -292,7 +330,7 @@ export async function getSize(targetPath: string, maxDepth = 5): Promise<number>
|
|||||||
const visited = new Set<string>();
|
const visited = new Set<string>();
|
||||||
|
|
||||||
const computeSize = async (currentPath: string, depth: number): Promise<number> => {
|
const computeSize = async (currentPath: string, depth: number): Promise<number> => {
|
||||||
if (visited.has(currentPath)){
|
if (visited.has(currentPath)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,9 +347,7 @@ export async function getSize(targetPath: string, maxDepth = 5): Promise<number>
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entries = await readdir(currentPath);
|
const entries = await readdir(currentPath);
|
||||||
const sizes = await Promise.all(
|
const sizes = await Promise.all(entries.map(entry => computeSize(path.join(currentPath, entry), depth + 1)));
|
||||||
entries.map((entry) => computeSize(path.join(currentPath, entry), depth + 1))
|
|
||||||
);
|
|
||||||
return sizes.reduce((acc, cur) => acc + cur, 0);
|
return sizes.reduce((acc, cur) => acc + cur, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+5
-13
@@ -23,9 +23,11 @@ import { LivShortcut } from "./services/liv/liv-shortcut.service";
|
|||||||
import { SteamLauncherService } from "./services/bs-launcher/steam-launcher.service";
|
import { SteamLauncherService } from "./services/bs-launcher/steam-launcher.service";
|
||||||
import { FileAssociationService } from "./services/file-association.service";
|
import { FileAssociationService } from "./services/file-association.service";
|
||||||
import { SongDetailsCacheService } from "./services/additional-content/maps/song-details-cache.service";
|
import { SongDetailsCacheService } from "./services/additional-content/maps/song-details-cache.service";
|
||||||
import { Dirent, readdirSync, rmSync, unlinkSync } from "fs-extra";
|
import { Dirent, readdirSync } from "fs-extra";
|
||||||
import { StaticConfigurationService } from "./services/static-configuration.service";
|
import { StaticConfigurationService } from "./services/static-configuration.service";
|
||||||
import { configureProxy } from './helpers/proxy.helpers';
|
import { configureProxy } from './helpers/proxy.helpers';
|
||||||
|
import { deleteFileSync, deleteFolderSync } from "./helpers/fs.helpers";
|
||||||
|
import { tryit } from "shared/helpers/error.helpers";
|
||||||
|
|
||||||
const isDebug = process.env.NODE_ENV === "development" || process.env.DEBUG_PROD === "true";
|
const isDebug = process.env.NODE_ENV === "development" || process.env.DEBUG_PROD === "true";
|
||||||
const staticConfig = StaticConfigurationService.getInstance();
|
const staticConfig = StaticConfigurationService.getInstance();
|
||||||
@@ -238,12 +240,7 @@ function deleteOldLogs(): void {
|
|||||||
|
|
||||||
for (const folder of deleteLogFolders) {
|
for (const folder of deleteLogFolders) {
|
||||||
const folderPath = path.join(folder.parentPath, folder.name);
|
const folderPath = path.join(folder.parentPath, folder.name);
|
||||||
try {
|
tryit(() => deleteFolderSync(folderPath));
|
||||||
rmSync(folderPath, { recursive: true, force: true });
|
|
||||||
log.info("Deleted log folder:", folderPath);
|
|
||||||
} catch (error) {
|
|
||||||
log.error("Error deleting folder:", folderPath, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,12 +252,7 @@ function deleteOldestLogs(): void {
|
|||||||
|
|
||||||
for (const file of logs) {
|
for (const file of logs) {
|
||||||
const filepath = path.join(file.parentPath, file.name);
|
const filepath = path.join(file.parentPath, file.name);
|
||||||
try {
|
tryit(() => deleteFileSync(filepath));
|
||||||
unlinkSync(filepath);
|
|
||||||
log.info("Deleted log file:", filepath);
|
|
||||||
} catch (error) {
|
|
||||||
log.error("Error deleting file:", filepath, error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import path from "path";
|
|||||||
import { RequestService } from "../request.service";
|
import { RequestService } from "../request.service";
|
||||||
import { copyFileSync } from "fs-extra";
|
import { copyFileSync } from "fs-extra";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
import { Progression, ensureFolderExist, unlinkPath } from "../../helpers/fs.helpers";
|
import { Progression, deleteFile, ensureFolderExist } from "../../helpers/fs.helpers";
|
||||||
import { MODEL_FILE_EXTENSIONS, MODEL_TYPES, MODEL_TYPE_FOLDERS } from "../../../shared/models/models/constants";
|
import { MODEL_FILE_EXTENSIONS, MODEL_TYPES, MODEL_TYPE_FOLDERS } from "../../../shared/models/models/constants";
|
||||||
import { InstallationLocationService } from "../installation-location.service";
|
import { InstallationLocationService } from "../installation-location.service";
|
||||||
import { Observable, Subscription, lastValueFrom } from "rxjs";
|
import { Observable, Subscription, lastValueFrom } from "rxjs";
|
||||||
@@ -105,7 +105,9 @@ export class LocalModelsManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async oneClickDownloadModel(model: MSModel): Promise<void> {
|
public async oneClickDownloadModel(model: MSModel): Promise<void> {
|
||||||
if (!model) { return; }
|
if (!model) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const versions = await this.localVersion.getInstalledVersions();
|
const versions = await this.localVersion.getInstalledVersions();
|
||||||
const downloaded = await lastValueFrom(this.downloadModel(model, versions.pop()));
|
const downloaded = await lastValueFrom(this.downloadModel(model, versions.pop()));
|
||||||
@@ -198,7 +200,7 @@ export class LocalModelsManagerService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (const model of models) {
|
for (const model of models) {
|
||||||
await unlinkPath(model.path);
|
await deleteFile(model.path);
|
||||||
progression.data.push(model);
|
progression.data.push(model);
|
||||||
progression.current = progression.data.length;
|
progression.current = progression.data.length;
|
||||||
subscriber.next(progression);
|
subscriber.next(progression);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { BPList, DownloadPlaylistProgressionData, PlaylistSong } from "shared/mo
|
|||||||
import { readFileSync, Stats } from "fs";
|
import { readFileSync, Stats } from "fs";
|
||||||
import { BeatSaverService } from "../thrid-party/beat-saver/beat-saver.service";
|
import { BeatSaverService } from "../thrid-party/beat-saver/beat-saver.service";
|
||||||
import { copy, ensureDir, pathExists, pathExistsSync, realpath, writeFileSync } from "fs-extra";
|
import { copy, ensureDir, pathExists, pathExistsSync, realpath, writeFileSync } from "fs-extra";
|
||||||
import { Progression, getUniqueFileNamePath, unlinkPath } from "../../helpers/fs.helpers";
|
import { Progression, deleteFile, getUniqueFileNamePath } from "../../helpers/fs.helpers";
|
||||||
import { FileAssociationService } from "../file-association.service";
|
import { FileAssociationService } from "../file-association.service";
|
||||||
import { SongDetailsCacheService } from "./maps/song-details-cache.service";
|
import { SongDetailsCacheService } from "./maps/song-details-cache.service";
|
||||||
import { sToMs } from "shared/helpers/time.helpers";
|
import { sToMs } from "shared/helpers/time.helpers";
|
||||||
@@ -409,7 +409,7 @@ export class LocalPlaylistsManagerService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public deletePlaylistFile(bpList: LocalBPList): Observable<void>{
|
public deletePlaylistFile(bpList: LocalBPList): Observable<void>{
|
||||||
return from(unlinkPath(bpList.path));
|
return from(deleteFile(bpList.path));
|
||||||
}
|
}
|
||||||
|
|
||||||
public exportPlaylists(opt: {version?: BSVersion, bpLists: LocalBPList[], dest: string, playlistsMaps?: BsmLocalMap[]}): Observable<Progression<string>> {
|
public exportPlaylists(opt: {version?: BSVersion, bpLists: LocalBPList[], dest: string, playlistsMaps?: BsmLocalMap[]}): Observable<Progression<string>> {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { InstallationLocationService } from "../../installation-location.service
|
|||||||
import { UtilsService } from "../../utils.service";
|
import { UtilsService } from "../../utils.service";
|
||||||
import crypto, { BinaryLike } from "crypto";
|
import crypto, { BinaryLike } from "crypto";
|
||||||
import { lstatSync } from "fs";
|
import { lstatSync } from "fs";
|
||||||
import { copy, createReadStream, ensureDir, pathExists, pathExistsSync, realpath, unlink } from "fs-extra";
|
import { copy, createReadStream, ensureDir, pathExists, pathExistsSync, realpath } from "fs-extra";
|
||||||
import { RequestService } from "../../request.service";
|
import { RequestService } from "../../request.service";
|
||||||
import sanitize from "sanitize-filename";
|
import sanitize from "sanitize-filename";
|
||||||
import { DeepLinkService } from "../../deep-link.service";
|
import { DeepLinkService } from "../../deep-link.service";
|
||||||
@@ -15,7 +15,7 @@ import log from "electron-log";
|
|||||||
import { WindowManagerService } from "../../window-manager.service";
|
import { WindowManagerService } from "../../window-manager.service";
|
||||||
import { Observable, Subject, lastValueFrom } from "rxjs";
|
import { Observable, Subject, lastValueFrom } from "rxjs";
|
||||||
import { Archive } from "../../../models/archive.class";
|
import { Archive } from "../../../models/archive.class";
|
||||||
import { Progression, deleteFolder, ensureFolderExist, getFilesInFolder, getFoldersInFolder, pathExist } from "../../../helpers/fs.helpers";
|
import { Progression, deleteFile, deleteFolder, ensureFolderExist, getFilesInFolder, getFoldersInFolder, pathExist } from "../../../helpers/fs.helpers";
|
||||||
import { readFile } from "fs/promises";
|
import { readFile } from "fs/promises";
|
||||||
import { FolderLinkerService } from "../../folder-linker.service";
|
import { FolderLinkerService } from "../../folder-linker.service";
|
||||||
import { allSettled } from "../../../../shared/helpers/promise.helpers";
|
import { allSettled } from "../../../../shared/helpers/promise.helpers";
|
||||||
@@ -297,7 +297,7 @@ export class LocalMapsManagerService {
|
|||||||
observer.next(progress);
|
observer.next(progress);
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
.catch(e => {observer.error(e); console.log("AAAA", e)})
|
.catch(e => observer.error(e))
|
||||||
.finally(() => observer.complete());
|
.finally(() => observer.complete());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -449,7 +449,7 @@ export class LocalMapsManagerService {
|
|||||||
const zip = await BsmZipExtractor.fromPath(zipPath);
|
const zip = await BsmZipExtractor.fromPath(zipPath);
|
||||||
await zip.extract(mapPath);
|
await zip.extract(mapPath);
|
||||||
zip.close();
|
zip.close();
|
||||||
await unlink(zipPath);
|
await deleteFile(zipPath);
|
||||||
|
|
||||||
const localMap = await this.loadMapInfoFromPath(mapPath);
|
const localMap = await this.loadMapInfoFromPath(mapPath);
|
||||||
localMap.songDetails = this.songDetailsCache.getSongDetails(localMap.hash);
|
localMap.songDetails = this.songDetailsCache.getSongDetails(localMap.hash);
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { InstallationLocationService } from "./installation-location.service";
|
import { InstallationLocationService } from "./installation-location.service";
|
||||||
import log from "electron-log";
|
import log from "electron-log";
|
||||||
import { deleteFolder, ensureFolderExist, moveFolderContent, pathExist, unlinkPath } from "../helpers/fs.helpers";
|
import { deleteFile, deleteFileSync, deleteFolder, deleteFolderSync, ensureFolderExist, moveFolderContent, pathExist } from "../helpers/fs.helpers";
|
||||||
import { lstat, symlink } from "fs/promises";
|
import { lstat, symlink } from "fs/promises";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { copy, mkdirSync, readlink, rmSync, symlinkSync, unlinkSync } from "fs-extra";
|
import { copy, mkdirSync, readlink, symlinkSync } from "fs-extra";
|
||||||
import { lastValueFrom } from "rxjs";
|
import { lastValueFrom } from "rxjs";
|
||||||
import { noop } from "shared/helpers/function.helpers";
|
import { noop } from "shared/helpers/function.helpers";
|
||||||
import { StaticConfigurationService } from "./static-configuration.service";
|
import { StaticConfigurationService } from "./static-configuration.service";
|
||||||
@@ -56,8 +56,8 @@ export class FolderLinkerService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tryit(() => {
|
tryit(() => {
|
||||||
rmSync(testFolder, { force: true, recursive: true });
|
deleteFolderSync(testFolder);
|
||||||
unlinkSync(testLink);
|
deleteFileSync(testLink);
|
||||||
});
|
});
|
||||||
|
|
||||||
if(resLink.error){
|
if(resLink.error){
|
||||||
@@ -113,7 +113,7 @@ export class FolderLinkerService {
|
|||||||
if (isTargetedToSharedPath) {
|
if (isTargetedToSharedPath) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await unlinkPath(folderPath);
|
await deleteFile(folderPath);
|
||||||
|
|
||||||
log.info(`Linking ${folderPath} to ${sharedPath}; type: ${this.linkingType}`);
|
log.info(`Linking ${folderPath} to ${sharedPath}; type: ${this.linkingType}`);
|
||||||
return symlink(sharedPath, folderPath, this.getLinkingType());
|
return symlink(sharedPath, folderPath, this.getLinkingType());
|
||||||
@@ -141,7 +141,7 @@ export class FolderLinkerService {
|
|||||||
if (!(await this.isFolderSymlink(folderPath))) {
|
if (!(await this.isFolderSymlink(folderPath))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await unlinkPath(folderPath);
|
await deleteFile(folderPath);
|
||||||
|
|
||||||
const sharedPath = await this.getSharedFolder(folderPath, options?.intermediateFolder);
|
const sharedPath = await this.getSharedFolder(folderPath, options?.intermediateFolder);
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import md5File from "md5-file";
|
|||||||
import { RequestService } from "../request.service";
|
import { RequestService } from "../request.service";
|
||||||
import { BS_EXECUTABLE } from "../../constants";
|
import { BS_EXECUTABLE } from "../../constants";
|
||||||
import log from "electron-log";
|
import log from "electron-log";
|
||||||
import { deleteFolder, pathExist, Progression, unlinkPath } from "../../helpers/fs.helpers";
|
import { deleteFile, deleteFolder, pathExist, Progression } from "../../helpers/fs.helpers";
|
||||||
import { lastValueFrom, Observable } from "rxjs";
|
import { lastValueFrom, Observable } from "rxjs";
|
||||||
import recursiveReadDir from "recursive-readdir";
|
import recursiveReadDir from "recursive-readdir";
|
||||||
import { sToMs } from "../../../shared/helpers/time.helpers";
|
import { sToMs } from "../../../shared/helpers/time.helpers";
|
||||||
import { copyFile, ensureDir, pathExistsSync, readdirSync, rm, unlink } from "fs-extra";
|
import { copyFile, ensureDir, pathExistsSync, readdirSync } from "fs-extra";
|
||||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||||
import { popElement } from "shared/helpers/array.helpers";
|
import { popElement } from "shared/helpers/array.helpers";
|
||||||
import { LinuxService } from "../linux.service";
|
import { LinuxService } from "../linux.service";
|
||||||
@@ -290,11 +290,10 @@ export class BsModsManagerService {
|
|||||||
|
|
||||||
const contentPath = path.join(ipaPath, content.name);
|
const contentPath = path.join(ipaPath, content.name);
|
||||||
|
|
||||||
const res = await tryit(() => content.isDirectory() ? (
|
const res = await tryit(() => content.isDirectory()
|
||||||
rm(contentPath, { force: true, recursive: true })
|
? deleteFolder(contentPath)
|
||||||
) : (
|
: deleteFile(contentPath)
|
||||||
unlink(contentPath)
|
);
|
||||||
));
|
|
||||||
|
|
||||||
if(res.error){
|
if(res.error){
|
||||||
log.error("Error while clearing IPA folder content", content.name, res.error);
|
log.error("Error while clearing IPA folder content", content.name, res.error);
|
||||||
@@ -318,7 +317,7 @@ export class BsModsManagerService {
|
|||||||
|
|
||||||
const promises = mod.version.contentHashes.map(content => {
|
const promises = mod.version.contentHashes.map(content => {
|
||||||
const file = content.path.replaceAll("IPA/", "").replaceAll("Data", "Beat Saber_Data");
|
const file = content.path.replaceAll("IPA/", "").replaceAll("Data", "Beat Saber_Data");
|
||||||
return unlinkPath(path.join(verionPath, file));
|
return deleteFile(path.join(verionPath, file));
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
@@ -331,7 +330,10 @@ export class BsModsManagerService {
|
|||||||
const versionPath = await this.bsLocalService.getVersionPath(version);
|
const versionPath = await this.bsLocalService.getVersionPath(version);
|
||||||
|
|
||||||
const promises = mod.version.contentHashes.map(async content => {
|
const promises = mod.version.contentHashes.map(async content => {
|
||||||
return Promise.all([unlinkPath(path.join(versionPath, content.path)), unlinkPath(path.join(versionPath, "IPA", "Pending", content.path))]);
|
return Promise.all([
|
||||||
|
deleteFile(path.join(versionPath, content.path)),
|
||||||
|
deleteFile(path.join(versionPath, "IPA", "Pending", content.path))
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { createWriteStream, WriteStream } from 'fs';
|
import { createWriteStream, WriteStream } from 'fs';
|
||||||
import { Progression } from 'main/helpers/fs.helpers';
|
import { deleteFileSync, Progression } from 'main/helpers/fs.helpers';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { shareReplay, tap } from 'rxjs/operators';
|
import { shareReplay, tap } from 'rxjs/operators';
|
||||||
import log from 'electron-log';
|
import log from 'electron-log';
|
||||||
import got from 'got';
|
import got from 'got';
|
||||||
import { IncomingHttpHeaders, IncomingMessage } from 'http';
|
import { IncomingHttpHeaders, IncomingMessage } from 'http';
|
||||||
import { unlinkSync } from 'fs-extra';
|
|
||||||
import { tryit } from 'shared/helpers/error.helpers';
|
import { tryit } from 'shared/helpers/error.helpers';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { pipeline } from 'stream/promises';
|
import { pipeline } from 'stream/promises';
|
||||||
@@ -84,7 +83,7 @@ export class RequestService {
|
|||||||
|
|
||||||
pipeline(stream, file).catch(err => {
|
pipeline(stream, file).catch(err => {
|
||||||
file?.destroy();
|
file?.destroy();
|
||||||
tryit(() => unlinkSync(dest));
|
tryit(() => deleteFileSync(dest));
|
||||||
subscriber.error(err);
|
subscriber.error(err);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { PlaylistsManagerService } from "renderer/services/playlists-manager.ser
|
|||||||
import { MapsManagerService } from "renderer/services/maps-manager.service";
|
import { MapsManagerService } from "renderer/services/maps-manager.service";
|
||||||
import { map } from "rxjs";
|
import { map } from "rxjs";
|
||||||
|
|
||||||
export const LinkContentModal: ModalComponent<boolean, {version: BSVersion, contentType: "maps"|"playlists"|"avatars"|"sabers"|"platforms"|"blocks"}> = ({options: { data: { version, contentType } }, resolver }) => {
|
export const LinkContentModal: ModalComponent<void, {version: BSVersion, contentType: "maps"|"playlists"|"avatars"|"sabers"|"platforms"|"blocks"}> = ({options: { data: { version, contentType } }, resolver }) => {
|
||||||
const { text: t, element: te } = useTranslationV2();
|
const { text: t, element: te } = useTranslationV2();
|
||||||
|
|
||||||
const versionLinker = useService(VersionFolderLinkerService);
|
const versionLinker = useService(VersionFolderLinkerService);
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ export class ModelsManagerService {
|
|||||||
return this.versionFolderLinked.linkVersionFolder({
|
return this.versionFolderLinked.linkVersionFolder({
|
||||||
version,
|
version,
|
||||||
relativeFolder: MODEL_TYPE_FOLDERS[type],
|
relativeFolder: MODEL_TYPE_FOLDERS[type],
|
||||||
options: { keepContents: res.data !== false },
|
options: { keepContents: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export class PlaylistsManagerService {
|
|||||||
return this.linker.linkVersionFolder({
|
return this.linker.linkVersionFolder({
|
||||||
version,
|
version,
|
||||||
relativeFolder: PlaylistsManagerService.RELATIVE_PLAYLISTS_FOLDER,
|
relativeFolder: PlaylistsManagerService.RELATIVE_PLAYLISTS_FOLDER,
|
||||||
options: { keepContents: !!modalRes.data },
|
options: { keepContents: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user