Files
bs-manager/src/main/services/bs-local-version.service.ts
T
2022-08-06 20:57:13 +02:00

184 lines
7.6 KiB
TypeScript

import { BSVersionLibService } from "./bs-version-lib.service";
import { BSVersion } from 'shared/bs-version.interface';
import { InstallationLocationService } from "./installation-location.service";
import { SteamService } from "./steam.service";
import { UtilsService } from "./utils.service";
import { BS_APP_ID } from "../constants";
import path from "path";
import { createInterface } from "readline";
import { createReadStream } from "fs";
import fs from "fs-extra";
import { ConfigurationService } from "./configuration.service";
import { rename } from "fs/promises";
import { BsmException } from "shared/models/bsm-exception.model";
export class BSLocalVersionService{
private static instance: BSLocalVersionService;
private readonly CUSTOM_VERSIONS_KEY = "custom-versions";
private readonly installLocationService: InstallationLocationService;
private readonly utilsService: UtilsService;
private readonly steamService: SteamService;
private readonly remoteVersionService: BSVersionLibService;
private readonly configService: ConfigurationService;
public static getInstance(): BSLocalVersionService{
if(!BSLocalVersionService.instance){ BSLocalVersionService.instance = new BSLocalVersionService(); }
return BSLocalVersionService.instance;
}
private constructor(){
this.installLocationService = InstallationLocationService.getInstance();
this.utilsService = UtilsService.getInstance();
this.steamService = SteamService.getInstance();
this.remoteVersionService = BSVersionLibService.getInstance();
this.configService = ConfigurationService.getInstance();
}
private async getVersionOfBSFolder(bsPath: string): Promise<string>{
const versionFilePath = path.join(bsPath, 'Beat Saber_Data', 'globalgamemanagers');
if(!this.utilsService.pathExist(versionFilePath)){ return null; }
const versionsAvailable = await this.remoteVersionService.getAvailableVersions();
return new Promise<string>(resolve => {
const readLine = createInterface({ input: createReadStream(versionFilePath) });
let findVersion: string = null;
readLine.on('line', (line) => {
for(const version of versionsAvailable){
if(line.includes(version.BSVersion)){
findVersion = version.BSVersion;
readLine.close();
break;
}
}
});
readLine.on('close', () => {
if(findVersion){ resolve(findVersion) }
resolve(findVersion);
});
})
}
private setCustomVersions(versions: BSVersion[]): void{
this.configService.set(this.CUSTOM_VERSIONS_KEY, versions);
}
private addCustomVersion(version: BSVersion): void{
this.setCustomVersions([...this.getCustomVersions() ?? [], version]);
}
private getCustomVersions(): BSVersion[]{
return this.configService.get<BSVersion[]>(this.CUSTOM_VERSIONS_KEY) || [];
}
private deleteCustomVersion(version: BSVersion): void{
const customVersions = this.getCustomVersions() || [];
this.setCustomVersions(customVersions.filter(v => (v.name !== version.name || v.BSVersion !== version.BSVersion || v.color !== version.color)));
}
public async getVersionPath(version: BSVersion): Promise<string>{
if(version.steam){ return this.steamService.getGameFolder(BS_APP_ID, "Beat Saber") }
return path.join(
this.installLocationService.versionsDirectory,
this.getVersionFolder(version)
);
}
private removeSpecialChar(seq: string): string{
// eslint-disable-next-line no-useless-escape
return seq.replace( /[<>:"\/\\|?*]+/g, '' );
}
public getVersionFolder(version: BSVersion){
return version.name ? `${version.BSVersion}-${version.name}` : version.BSVersion;
}
public async getInstalledVersions(): Promise<BSVersion[]>{
const versions: BSVersion[] = [];
const steamBsFolder = await this.steamService.getGameFolder(BS_APP_ID, "Beat Saber")
if(steamBsFolder && this.utilsService.pathExist(steamBsFolder)){
const steamBsVersion = await this.getVersionOfBSFolder(steamBsFolder);
if(steamBsVersion){
const steamVersionDetails = this.remoteVersionService.getVersionDetails(steamBsVersion);
versions.push(steamVersionDetails ? {...steamVersionDetails, steam: true} : {BSVersion: steamBsVersion, steam: true});
}
}
if(!this.utilsService.pathExist(this.installLocationService.versionsDirectory)){ return versions }
const folderInInstallation = this.utilsService.listDirsInDir(this.installLocationService.versionsDirectory);
folderInInstallation.forEach(f => {
let version = this.remoteVersionService.getVersionDetails(f);
if(version){ version = this.getCustomVersions().find(v => v.BSVersion === version.BSVersion && v.name === version.name) ?? version; }
else { version = this.getCustomVersions().find(v => {
const [version, ...rest] = f.split("-");
if(rest.length < 1){ return false; }
const name = rest.join("-");
return name === v.name && version === v.BSVersion;
})
}
version && versions.push(version);
});
this.setCustomVersions(versions.filter(v => !!v.name || !!v.color));
return versions;
}
public async deleteVersion(version: BSVersion): Promise<boolean>{
if(version.steam){ return false; }
const versionFolder = await this.getVersionPath(version);
if(!this.utilsService.pathExist(versionFolder)){ return true; }
return this.utilsService.deleteFolder(versionFolder)
.then(() => { return true; })
.catch(() => { return false; })
}
public async editVersion(version: BSVersion, name: string, color: string): Promise<BSVersion>{
if(version.steam){ throw {title: "CantEditSteam", msg: "CantEditSteam"} as BsmException; }
const oldPath = await this.getVersionPath(version);
const editedVersion: BSVersion = version.BSVersion === name
? {...version, name: undefined, color}
: {...version, name: this.removeSpecialChar(name), color};
const newPath = await this.getVersionPath(editedVersion);
if(oldPath === newPath){
this.deleteCustomVersion(version);
this.addCustomVersion(editedVersion);
return editedVersion;
}
if(this.utilsService.pathExist(newPath)){ throw {title: "VersionAlreadExist"} as BsmException; }
return rename(oldPath, newPath).then(() => {
this.deleteCustomVersion(version);
this.addCustomVersion(editedVersion);
return editedVersion;
}).catch((err: Error) => {
throw {title: "CantRename", error: err} as BsmException;
});
}
public async cloneVersion(version: BSVersion, name: string, color: string): Promise<BSVersion>{
const originPath = await this.getVersionPath(version);
const cloneVersion: BSVersion = version.BSVersion === name
? {...version, name: undefined, color}
: {...version, name: this.removeSpecialChar(name), color, steam: false};
const newPath = await this.getVersionPath(cloneVersion);
if(originPath === newPath){
this.deleteCustomVersion(version);
this.addCustomVersion(cloneVersion);
return cloneVersion;
}
if(this.utilsService.pathExist(newPath)){ throw {title: "VersionAlreadExist"} as BsmException; }
return fs.copy(originPath, newPath).then(() => {
this.addCustomVersion(cloneVersion);
return cloneVersion;
}).catch((err: Error) => {
throw {title: "CantClone", error: err} as BsmException
})
}
}