mirror of
https://github.com/Zagrios/bs-manager.git
synced 2026-07-03 14:08:25 +02:00
[feature-274] Refactoring downloaders service + handle importing Oculus version + logout from steam and oculus in settings + other things
This commit is contained in:
@@ -110,9 +110,9 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
"title": "Steam",
|
||||
"description": "Wenn Du dich von Steam abmeldest, musst Du dich erneut anmelden, um eine neue Version von Beat Saber herunterzuladen.",
|
||||
"steam-and-oculus": {
|
||||
"title": "Steam & Oculus",
|
||||
"description": "Abmelden ermöglicht es dir, beim nächsten Beat Saber Download den Account zu wechseln.",
|
||||
"logout": "Abmelden"
|
||||
},
|
||||
"appearance": {
|
||||
|
||||
@@ -110,9 +110,9 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
"title": "Steam",
|
||||
"description": "If you log out of Steam, you will have to log back in to download new versions of Beat Saber.",
|
||||
"steam-and-oculus": {
|
||||
"title": "Steam & Oculus",
|
||||
"description": "Logging out will allow you to switch accounts on the next Beat Saber download.",
|
||||
"logout": "Log out"
|
||||
},
|
||||
"appearance": {
|
||||
|
||||
@@ -109,9 +109,9 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
"title": "Steam",
|
||||
"description": "Si desconectas tu cuenta de Steam, tendrás que volver a conectarla para descargar una nueva versión de Beat Saber.",
|
||||
"steam-and-oculus": {
|
||||
"title": "Steam & Oculus",
|
||||
"description": "Desconectarte te permitirá cambiar de cuenta en la próxima descarga de Beat Saber.",
|
||||
"logout": "Cerrar sesión"
|
||||
},
|
||||
"appearance": {
|
||||
|
||||
@@ -109,9 +109,9 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
"title": "Steam",
|
||||
"description": "Si tu te déconnectes de Steam, tu devras te reconnecter pour télécharger une nouvelle version de Beat Saber.",
|
||||
"steam-and-oculus": {
|
||||
"title": "Steam & Oculus",
|
||||
"description": "Te déconnecter te permettra de changer de compte au prochain téléchargement de Beat Saber.",
|
||||
"logout": "Déconnexion"
|
||||
},
|
||||
"appearance": {
|
||||
|
||||
@@ -110,9 +110,9 @@
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"steam": {
|
||||
"title": "Steam",
|
||||
"description": "Если вы выйдете из Steam, вам придётся снова войти, если вы хотите скачать другие версии Beat Saber.",
|
||||
"steam-and-oculus": {
|
||||
"title": "Steam & Oculus",
|
||||
"description": "Выход позволит вам сменить учетную запись при следующей загрузке Beat Saber.",
|
||||
"logout": "Выйти"
|
||||
},
|
||||
"appearance": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CopyOptions, copy, createReadStream, ensureDir, move, symlink } from "fs-extra";
|
||||
import { CopyOptions, copy, createReadStream, ensureDir, move, stat, symlink } from "fs-extra";
|
||||
import { access, mkdir, rm, readdir, unlink, lstat, readlink } from "fs/promises";
|
||||
import path from "path";
|
||||
import { Observable } from "rxjs";
|
||||
import { Observable, concatMap, from } from "rxjs";
|
||||
import log from "electron-log";
|
||||
import { BsmException } from "shared/models/bsm-exception.model";
|
||||
import crypto from "crypto";
|
||||
@@ -164,6 +164,59 @@ export function hashFile(filePath: string, algorithm = "sha256"): Promise<string
|
||||
});
|
||||
}
|
||||
|
||||
export async function dirSize(dirPath: string): Promise<number>{
|
||||
const files = await readdir(dirPath, { withFileTypes: true });
|
||||
|
||||
const paths = files.map(async file => {
|
||||
const fullPath = path.join(dirPath, file.name);
|
||||
|
||||
if (file.isDirectory()) return dirSize(fullPath);
|
||||
|
||||
if (file.isFile()) {
|
||||
const { size } = await stat( fullPath );
|
||||
return size;
|
||||
}
|
||||
|
||||
return 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> {
|
||||
|
||||
return from(dirSize(src)).pipe(
|
||||
concatMap(totalSize => {
|
||||
const progress: Progression = { current: 0, total: totalSize };
|
||||
return new Observable<Progression>(sub => {
|
||||
sub.next(progress);
|
||||
copy(src, dest, {...option, filter: (src) => {
|
||||
stat(src).then(stats => {
|
||||
progress.current += stats.size;
|
||||
sub.next(progress);
|
||||
});
|
||||
return true;
|
||||
}})
|
||||
.then(() => sub.complete()).catch(err => sub.error(err))
|
||||
})
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export async function ensurePathNotAlreadyExist(path: string): Promise<string> {
|
||||
let destPath = path;
|
||||
let folderExist = await pathExist(destPath);
|
||||
let i = 0;
|
||||
|
||||
while (folderExist) {
|
||||
i++;
|
||||
destPath = `${path} (${i})`;
|
||||
folderExist = await pathExist(destPath);
|
||||
}
|
||||
|
||||
return destPath;
|
||||
}
|
||||
|
||||
export interface Progression<T = unknown> {
|
||||
total: number;
|
||||
current: number;
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { BSInstallerService, DownloadInfo } from "../../services/bs-installer.service";
|
||||
import { InstallationLocationService } from "../../services/installation-location.service";
|
||||
import { IpcService } from "../../services/ipc.service";
|
||||
import { from, of } from "rxjs";
|
||||
|
||||
const ipc = IpcService.getInstance();
|
||||
|
||||
ipc.on("is-dotnet-6-installed", (_, reply) => {
|
||||
const installer = BSInstallerService.getInstance();
|
||||
reply(from(installer.isDotNet6Installed()));
|
||||
});
|
||||
|
||||
ipc.on("bs-download.installation-folder", (_, reply) => {
|
||||
const installLocation = InstallationLocationService.getInstance();
|
||||
reply(from(installLocation.installationDirectory()));
|
||||
});
|
||||
|
||||
ipc.on<string>("bs-download.set-installation-folder", (req, reply) => {
|
||||
const installerService = InstallationLocationService.getInstance();
|
||||
reply(from(installerService.setInstallationDirectory(req.args)));
|
||||
});
|
||||
|
||||
ipc.on<string>("bs-download.import-version", (req, reply) => {
|
||||
const installer = BSInstallerService.getInstance();
|
||||
reply(from(installer.importVersion(req.args)));
|
||||
});
|
||||
|
||||
ipc.on<DownloadInfo>("auto-download-bs-version", (req, reply) => {
|
||||
const bsInstaller = BSInstallerService.getInstance();
|
||||
reply(bsInstaller.autoDownloadBsVersion(req.args));
|
||||
});
|
||||
|
||||
ipc.on<DownloadInfo>("download-bs-version", (req, reply) => {
|
||||
const bsInstaller = BSInstallerService.getInstance();
|
||||
reply(bsInstaller.downloadBsVersion(req.args))
|
||||
});
|
||||
|
||||
ipc.on<DownloadInfo>("download-bs-version-qr", (req, reply) => {
|
||||
const bsInstaller = BSInstallerService.getInstance();
|
||||
reply(bsInstaller.downloadBsVersionWithQRCode(req.args))
|
||||
});
|
||||
|
||||
ipc.on("stop-download-bs-version", (_, reply) => {
|
||||
const bsInstaller = BSInstallerService.getInstance();
|
||||
reply(of(bsInstaller.stopDownload()));
|
||||
});
|
||||
|
||||
ipc.on<string>("send-input-bs-download", (req, reply) => {
|
||||
const bsInstaller = BSInstallerService.getInstance();
|
||||
reply(of(bsInstaller.sendInput(req.args)));
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
import { BsOculusDownloaderService, OculusDownloadInfo } from "../../services/bs-oculus-downloader.service";
|
||||
import { IpcService } from "../../services/ipc.service";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
|
||||
const ipc = IpcService.getInstance();
|
||||
|
||||
ipc.on<OculusDownloadInfo>("bs-oculus-download", async (req, reply) => {
|
||||
const oculusDownloader = BsOculusDownloaderService.getInstance();
|
||||
reply(oculusDownloader.downloadVersion(req.args));
|
||||
});
|
||||
|
||||
ipc.on<BSVersion>("bs-oculus-auto-download", async (req, reply) => {
|
||||
const oculusDownloader = BsOculusDownloaderService.getInstance();
|
||||
reply(oculusDownloader.autoDownloadVersion(req.args));
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import { BsOculusDownloaderService } from "../../services/bs-version-download/bs-oculus-downloader.service";
|
||||
import { BsSteamDownloaderService, DownloadInfo, DownloadSteamInfo } from "../../services/bs-version-download/bs-steam-downloader.service";
|
||||
import { InstallationLocationService } from "../../services/installation-location.service";
|
||||
import { IpcService } from "../../services/ipc.service";
|
||||
import { from, of } from "rxjs";
|
||||
import { BSLocalVersionService, ImportVersionOptions } from "../../services/bs-local-version.service";
|
||||
|
||||
const ipc = IpcService.getInstance();
|
||||
|
||||
ipc.on<ImportVersionOptions>("import-version", (req, reply) => {
|
||||
const versionManager = BSLocalVersionService.getInstance();
|
||||
reply(versionManager.importVersion(req.args));
|
||||
});
|
||||
|
||||
// #region Steam
|
||||
|
||||
ipc.on("is-dotnet-6-installed", (_, reply) => {
|
||||
const installer = BsSteamDownloaderService.getInstance();
|
||||
reply(from(installer.isDotNet6Installed()));
|
||||
});
|
||||
|
||||
ipc.on("bs-download.installation-folder", (_, reply) => {
|
||||
const installLocation = InstallationLocationService.getInstance();
|
||||
reply(from(installLocation.installationDirectory()));
|
||||
});
|
||||
|
||||
ipc.on<string>("bs-download.set-installation-folder", (req, reply) => {
|
||||
const installerService = InstallationLocationService.getInstance();
|
||||
reply(from(installerService.setInstallationDirectory(req.args)));
|
||||
});
|
||||
|
||||
ipc.on<DownloadSteamInfo>("auto-download-bs-version", (req, reply) => {
|
||||
const bsInstaller = BsSteamDownloaderService.getInstance();
|
||||
reply(bsInstaller.autoDownloadBsVersion(req.args));
|
||||
});
|
||||
|
||||
ipc.on<DownloadSteamInfo>("download-bs-version", (req, reply) => {
|
||||
const bsInstaller = BsSteamDownloaderService.getInstance();
|
||||
reply(bsInstaller.downloadBsVersion(req.args))
|
||||
});
|
||||
|
||||
ipc.on<DownloadSteamInfo>("download-bs-version-qr", (req, reply) => {
|
||||
const bsInstaller = BsSteamDownloaderService.getInstance();
|
||||
reply(bsInstaller.downloadBsVersionWithQRCode(req.args))
|
||||
});
|
||||
|
||||
ipc.on("stop-download-bs-version", (_, reply) => {
|
||||
const bsInstaller = BsSteamDownloaderService.getInstance();
|
||||
reply(of(bsInstaller.stopDownload()));
|
||||
});
|
||||
|
||||
ipc.on<string>("send-input-bs-download", (req, reply) => {
|
||||
const bsInstaller = BsSteamDownloaderService.getInstance();
|
||||
reply(of(bsInstaller.sendInput(req.args)));
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Oculus
|
||||
|
||||
ipc.on<DownloadInfo>("bs-oculus-download", async (req, reply) => {
|
||||
const oculusDownloader = BsOculusDownloaderService.getInstance();
|
||||
reply(oculusDownloader.downloadVersion(req.args));
|
||||
});
|
||||
|
||||
ipc.on<DownloadInfo>("bs-oculus-auto-download", async (req, reply) => {
|
||||
const oculusDownloader = BsOculusDownloaderService.getInstance();
|
||||
reply(oculusDownloader.autoDownloadVersion(req.args));
|
||||
});
|
||||
|
||||
ipc.on("bs-oculus-stop-download", async (_, reply) => {
|
||||
const oculusDownloader = BsOculusDownloaderService.getInstance();
|
||||
reply(of(oculusDownloader.stopDownload()));
|
||||
});
|
||||
|
||||
ipc.on("bs-oculus-has-auth-token", async (_, reply) => {
|
||||
const oculusDownloader = BsOculusDownloaderService.getInstance();
|
||||
reply(from(oculusDownloader.getAuthToken().then(token => !!token)));
|
||||
});
|
||||
|
||||
ipc.on("bs-oculus-clear-auth-token", async (_, reply) => {
|
||||
const oculusDownloader = BsOculusDownloaderService.getInstance();
|
||||
reply(from(oculusDownloader.clearAuthToken()));
|
||||
});
|
||||
|
||||
// #endregion
|
||||
@@ -11,5 +11,4 @@ import "./beat-saver-ipcs";
|
||||
import "./bs-playlist-ipcs";
|
||||
import "./model-saber.ipcs";
|
||||
import "./bs-model-ipcs";
|
||||
import "./bs-downgrade/bs-oculus-download-ipcs";
|
||||
import "./bs-downgrade/bs-download-ipcs";
|
||||
import "./bs-version-download/bs-download-ipcs";
|
||||
|
||||
@@ -20,8 +20,6 @@ import { APP_NAME } from "./constants";
|
||||
import { BSLauncherService } from "./services/bs-launcher.service";
|
||||
import { IpcRequest } from "shared/models/ipc";
|
||||
import { LivShortcut } from "./services/liv/liv-shortcut.service";
|
||||
import { BsOculusDownloaderService } from "./services/bs-oculus-downloader.service";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
|
||||
const isDebug = process.env.NODE_ENV === "development" || process.env.DEBUG_PROD === "true";
|
||||
|
||||
@@ -60,8 +58,6 @@ const createWindow = async (window: AppWindow = "launcher.html") => {
|
||||
await installExtensions();
|
||||
}
|
||||
WindowManagerService.getInstance().openWindow(window);
|
||||
|
||||
BsOculusDownloaderService.getInstance().clearTokenCookie();
|
||||
};
|
||||
|
||||
const initServicesMustBeInitialized = () => {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import JSZip from "jszip";
|
||||
import fetch from "node-fetch";
|
||||
import { CustomError } from "../../shared/models/exceptions/custom-error.class";
|
||||
import { mkdirs, createWriteStream, pathExists, writeFile, WriteStream } from "fs-extra";
|
||||
import { mkdirs, createWriteStream, pathExists, WriteStream } from "fs-extra";
|
||||
import path from "path";
|
||||
import { inflate } from "pako"
|
||||
import { EMPTY, Observable, ReplaySubject, catchError, filter, from, lastValueFrom, map, mergeMap, scan, share, tap } from "rxjs";
|
||||
import { EMPTY, Observable, ReplaySubject, Subscriber, catchError, filter, from, lastValueFrom, mergeMap, scan, share, tap } from "rxjs";
|
||||
import { Progression, hashFile } from "../helpers/fs.helpers";
|
||||
|
||||
export class OculusDownloader {
|
||||
@@ -12,6 +12,8 @@ export class OculusDownloader {
|
||||
private options: OculusDownloaderOptions;
|
||||
private isDownloading: boolean;
|
||||
|
||||
private downloadSubscriber: Subscriber<Progression>;
|
||||
|
||||
private getDownloadManifestUrl(token: string, binaryId: string): string {
|
||||
return `https://securecdn.oculus.com/binaries/download/?id=${binaryId}&access_token=${token}&get_manifest=1`;
|
||||
}
|
||||
@@ -59,7 +61,7 @@ export class OculusDownloader {
|
||||
writeStream = createWriteStream(destination);
|
||||
|
||||
for (const segment of file.segments) {
|
||||
if(canceled || !writeStream.writable){ return; }
|
||||
if(canceled || !writeStream.writable || !this.isDownloading){ return; }
|
||||
|
||||
const arrBuffer = await downloadSegment(segment);
|
||||
const inflated = inflate(arrBuffer);
|
||||
@@ -130,6 +132,8 @@ export class OculusDownloader {
|
||||
|
||||
return new Observable<Progression>(subscriber => {
|
||||
|
||||
this.downloadSubscriber = subscriber;
|
||||
|
||||
if(this.isDownloading){
|
||||
throw new CustomError("Already downloading", "ALREADY_DOWNLOADING");
|
||||
}
|
||||
@@ -150,10 +154,14 @@ export class OculusDownloader {
|
||||
const filesDownloadObservable = from(files).pipe(
|
||||
filter(() => this.isDownloading),
|
||||
mergeMap(([filename, file]) => (
|
||||
from(this.isFileIntegrityValid([filename, file], options.destination)).pipe( map(isValid => ({ filename, file, isValid })))
|
||||
this.isFileIntegrityValid([filename, file], options.destination)).then(isValid => ({ filename, file, isValid })
|
||||
)),
|
||||
filter(({ isValid }) => !isValid),
|
||||
mergeMap(({ filename, file }) => {
|
||||
mergeMap(({ filename, file, isValid }) => {
|
||||
if(isValid){
|
||||
const res: Progression<OculusManifestFile> = { current: file.size, total: file.size, diff: file.size, data: file };
|
||||
return from([res])
|
||||
}
|
||||
|
||||
const target = path.join(options.destination, filename);
|
||||
return this.downloadManifestFile(file, target).pipe(
|
||||
catchError(err => {
|
||||
@@ -161,7 +169,7 @@ export class OculusDownloader {
|
||||
return EMPTY;
|
||||
})
|
||||
);
|
||||
}, 10),
|
||||
}, 5),
|
||||
scan((acc, curr) => acc + curr.diff, 0),
|
||||
);
|
||||
|
||||
@@ -172,8 +180,6 @@ export class OculusDownloader {
|
||||
},
|
||||
})));
|
||||
|
||||
await writeFile(path.join(options.destination, "type.info"), "oculus");
|
||||
|
||||
const integrity = await lastValueFrom(this.verifyIntegrity(manifest, options.destination)).catch(err => CustomError.throw(err, "VERIFY_INTEGRITY_FAILED"));
|
||||
|
||||
if(integrity.data.length > 0){
|
||||
@@ -183,7 +189,6 @@ export class OculusDownloader {
|
||||
})().then(() => subscriber.complete()).catch(err => subscriber.error(err));
|
||||
|
||||
return () => {
|
||||
console.log("C FINI");
|
||||
this.isDownloading = false;
|
||||
}
|
||||
}).pipe(share({connector: () => new ReplaySubject(1)}));
|
||||
@@ -191,6 +196,7 @@ export class OculusDownloader {
|
||||
|
||||
public stopDownload(){
|
||||
this.isDownloading = false;
|
||||
this.downloadSubscriber?.complete();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,17 +11,19 @@ import log from "electron-log";
|
||||
import { OculusService } from "./oculus.service";
|
||||
import { DownloadLinkType } from "shared/models/mods";
|
||||
import sanitize from "sanitize-filename";
|
||||
import { copyDirectoryWithJunctions, deleteFolder, getFoldersInFolder, pathExist } from "../helpers/fs.helpers";
|
||||
import { Progression, copyDirectoryWithJunctions, deleteFolder, ensurePathNotAlreadyExist, getFoldersInFolder, pathExist, rxCopy } from "../helpers/fs.helpers";
|
||||
import { FolderLinkerService } from "./folder-linker.service";
|
||||
import { ReadStream, createReadStream, readFile } from "fs-extra";
|
||||
import { ReadStream, createReadStream, readFile, writeFile } from "fs-extra";
|
||||
import readline from "readline";
|
||||
import { Observable, Subject } from "rxjs";
|
||||
import { Observable, Subject, catchError, finalize, from, map, switchMap, throwError } from "rxjs";
|
||||
import { BsStore } from "../../shared/models/bs-store.enum";
|
||||
import { CustomError } from "../../shared/models/exceptions/custom-error.class";
|
||||
|
||||
export class BSLocalVersionService {
|
||||
private static instance: BSLocalVersionService;
|
||||
|
||||
private readonly CUSTOM_VERSIONS_KEY = "custom-versions";
|
||||
private readonly METADATA_FILE = "metadata.config";
|
||||
|
||||
private readonly installLocationService: InstallationLocationService;
|
||||
private readonly steamService: SteamService;
|
||||
@@ -110,12 +112,8 @@ export class BSLocalVersionService {
|
||||
folderVersion.ino = folderStats.ino;
|
||||
}
|
||||
|
||||
const type: string = await readFile(path.join(bsPath, "type.info"), "utf-8").catch(() => null);
|
||||
if(type === BsStore.OCULUS){
|
||||
folderVersion.downloadedFrom = BsStore.OCULUS;
|
||||
} else {
|
||||
folderVersion.downloadedFrom = BsStore.STEAM;
|
||||
}
|
||||
const versionStore: BsStore = await this.getVersionMetadata(folderVersion, "store");
|
||||
folderVersion.store = versionStore ?? BsStore.STEAM;
|
||||
|
||||
const customVersion = this.getCustomVersions().find(customVersion => {
|
||||
return customVersion.BSVersion === folderVersion.BSVersion && customVersion.name === folderVersion.name;
|
||||
@@ -124,24 +122,57 @@ export class BSLocalVersionService {
|
||||
folderVersion.color = customVersion?.color;
|
||||
|
||||
return folderVersion;
|
||||
}
|
||||
}
|
||||
|
||||
private setCustomVersions(versions: BSVersion[]): void{
|
||||
this.configService.set(this.CUSTOM_VERSIONS_KEY, versions);
|
||||
}
|
||||
public getAllVersionMetadata(version: BSVersion): Promise<Record<string, unknown>|null>{
|
||||
return (async () => {
|
||||
const versionPath = await this.getVersionPath(version);
|
||||
const contents = await readFile(path.join(versionPath, this.METADATA_FILE), "utf-8");
|
||||
return JSON.parse(contents);
|
||||
})().catch(e => {
|
||||
log.warn(e);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private addCustomVersion(version: BSVersion): void{
|
||||
this.setCustomVersions([...this.getCustomVersions() ?? [], version]);
|
||||
}
|
||||
public getVersionMetadata<T = unknown>(version: BSVersion, key: string): Promise<T|null>{
|
||||
return (async () => {
|
||||
const metadata = await this.getAllVersionMetadata(version);
|
||||
return metadata?.[key] as T;
|
||||
})().catch(e => {
|
||||
log.warn(e);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private getCustomVersions(): BSVersion[]{
|
||||
return this.configService.get<BSVersion[]>(this.CUSTOM_VERSIONS_KEY) || [];
|
||||
}
|
||||
public setVersionMetadata(version: BSVersion, key: string, value: unknown): Promise<void>{
|
||||
return (async () => {
|
||||
const versionPath = await this.getVersionPath(version);
|
||||
const metadataPath = path.join(versionPath, this.METADATA_FILE);
|
||||
const metadata = await this.getAllVersionMetadata(version) || {};
|
||||
metadata[key] = value;
|
||||
await writeFile(metadataPath, JSON.stringify(metadata));
|
||||
})().catch(e => {
|
||||
log.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
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)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -309,6 +340,34 @@ export class BSLocalVersionService {
|
||||
})
|
||||
}
|
||||
|
||||
public importVersion(opt: ImportVersionOptions): Observable<Progression<BSVersion>>{
|
||||
const { fromPath, store } = opt;
|
||||
|
||||
let failed = false;
|
||||
let versionDest: {version: BSVersion, dest: string} = null;
|
||||
|
||||
return from(this.getVersionOfBSFolder(fromPath)).pipe(
|
||||
map(version => version || CustomError.throw(new Error("Unable to get BS version of path"), "NOT_BS_FOLDER")),
|
||||
switchMap(version => this.getVersionPath(version).then(dest => ({version, dest}))),
|
||||
switchMap(({version, dest}) => ensurePathNotAlreadyExist(dest).then(uniquePath => {
|
||||
const res = dest === uniquePath ? {version, dest} : {version: {...version, name: path.basename(uniquePath)}, dest: uniquePath} as {version: BSVersion, dest: string};
|
||||
versionDest = res;
|
||||
return res;
|
||||
})),
|
||||
switchMap(({version, dest}) => rxCopy(fromPath, dest, { dereference: true }).pipe(
|
||||
map(progress => ({...progress, data: version}))
|
||||
)),
|
||||
catchError(err => {
|
||||
failed = true;
|
||||
return throwError(() => err);
|
||||
}),
|
||||
finalize(async () => {
|
||||
if(failed){ return; }
|
||||
await this.setVersionMetadata(versionDest.version, "store", store);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public async getLinkedFolders(version: BSVersion): Promise<string[]>{
|
||||
const versionPath = await this.getVersionPath(version);
|
||||
const [rootFolders, beatSaberDataFolders] = await Promise.all([getFoldersInFolder(versionPath), getFoldersInFolder(path.join(versionPath, "Beat Saber_Data"))]);
|
||||
@@ -329,3 +388,8 @@ export class BSLocalVersionService {
|
||||
return this._loadedVersions$.asObservable();
|
||||
}
|
||||
}
|
||||
|
||||
export interface ImportVersionOptions {
|
||||
fromPath: string;
|
||||
store: BsStore
|
||||
}
|
||||
|
||||
+74
-67
@@ -1,13 +1,16 @@
|
||||
import { BSVersion } from "../../shared/bs-version.interface";
|
||||
import { WindowManagerService } from "./window-manager.service";
|
||||
import { minToMs, msToS } from "../../shared/helpers/time.helpers";
|
||||
import { BSVersion } from "../../../shared/bs-version.interface";
|
||||
import { WindowManagerService } from "../window-manager.service";
|
||||
import { minToMs, msToS } from "../../../shared/helpers/time.helpers";
|
||||
import log from "electron-log";
|
||||
import { CustomError } from "../../shared/models/exceptions/custom-error.class";
|
||||
import { OculusDownloader } from "../models/oculus-downloader.class";
|
||||
import { CustomError } from "../../../shared/models/exceptions/custom-error.class";
|
||||
import { OculusDownloader } from "../../models/oculus-downloader.class";
|
||||
import { Cookie, session } from "electron";
|
||||
import { Progression, pathExist } from "../helpers/fs.helpers";
|
||||
import { BSLocalVersionService } from "./bs-local-version.service";
|
||||
import { Observable, Subscription } from "rxjs";
|
||||
import { Progression, ensurePathNotAlreadyExist } from "../../helpers/fs.helpers";
|
||||
import { BSLocalVersionService } from "../bs-local-version.service";
|
||||
import { Observable, catchError, finalize, from, map, switchMap, throwError } from "rxjs";
|
||||
import path from "path";
|
||||
import { DownloadInfo } from "./bs-steam-downloader.service";
|
||||
import { BsStore } from "../../../shared/models/bs-store.enum";
|
||||
|
||||
export class BsOculusDownloaderService {
|
||||
|
||||
@@ -73,8 +76,8 @@ export class BsOculusDownloaderService {
|
||||
return cookie.expirationDate > msToS(Date.now());
|
||||
}
|
||||
|
||||
public async getTokenFromCookie(): Promise<string | undefined> {
|
||||
const cookie = await session.defaultSession.cookies.get({ name: "oc_www_at" }).then(a => a.at(0));
|
||||
public async getAuthToken(): Promise<string | undefined> {
|
||||
const cookie = await session.defaultSession.cookies.get({ name: "oc_www_at" }).then(a => a?.at(0));
|
||||
|
||||
if(this.isCookieValid(cookie) && this.isUserTokenValid(cookie.value)){
|
||||
return cookie.value;
|
||||
@@ -117,7 +120,7 @@ export class BsOculusDownloaderService {
|
||||
clearTimeout(timout);
|
||||
|
||||
if(!keepToken){
|
||||
this.clearTokenCookie();
|
||||
this.clearAuthToken();
|
||||
}
|
||||
|
||||
if(window.isClosable() && !window.isDestroyed()){
|
||||
@@ -128,72 +131,76 @@ export class BsOculusDownloaderService {
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* DUPLICATION FROM BS-INSTALLER.SERVICE (TODO : need to be refactored)
|
||||
* @param path
|
||||
* @returns
|
||||
*/
|
||||
private async getPathNotAleardyExist(path: string): Promise<string> {
|
||||
let destPath = path;
|
||||
let folderExist = await pathExist(destPath);
|
||||
let i = 0;
|
||||
|
||||
while (folderExist) {
|
||||
i++;
|
||||
destPath = `${path} (${i})`;
|
||||
folderExist = await pathExist(destPath);
|
||||
}
|
||||
|
||||
return destPath;
|
||||
private async createDownloadVersion(version: BSVersion): Promise<{version: BSVersion, dest: string}>{
|
||||
const dest = await ensurePathNotAlreadyExist(await this.versions.getVersionPath(version));
|
||||
return {
|
||||
version: {...version, ...(path.basename(dest) !== version.BSVersion && { name: path.basename(dest) })},
|
||||
dest
|
||||
};
|
||||
}
|
||||
|
||||
public downloadVersion(downloadInfo: OculusDownloadInfo){
|
||||
|
||||
return new Observable<Progression>(obs => {
|
||||
|
||||
let sub: Subscription;
|
||||
|
||||
(async () => {
|
||||
const token = await this.getUserTokenFromMetaAuth(downloadInfo.stay);
|
||||
const dest = await this.getPathNotAleardyExist(await this.versions.getVersionPath(downloadInfo.version));
|
||||
|
||||
sub = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: downloadInfo.version.OculusBinaryId, destination: dest }).subscribe(obs);
|
||||
})().catch(err => obs.error(err));
|
||||
|
||||
return () => {
|
||||
sub?.unsubscribe();
|
||||
this.oculusDownloader.stopDownload();
|
||||
}
|
||||
})
|
||||
|
||||
public stopDownload(){
|
||||
this.oculusDownloader.stopDownload();
|
||||
}
|
||||
|
||||
public autoDownloadVersion(version: BSVersion): Observable<Progression>{
|
||||
|
||||
return new Observable<Progression>(obs => {
|
||||
|
||||
let sub: Subscription;
|
||||
|
||||
(async () => {
|
||||
const token = await this.getTokenFromCookie();
|
||||
public downloadVersion(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
|
||||
|
||||
return from(this.getUserTokenFromMetaAuth(downloadInfo.stay)).pipe(
|
||||
map(token => {
|
||||
if(!token){
|
||||
throw new CustomError("No token has been found while try to auto download Beat Saber from Oculus", "OCULUS_TOKEN_NEEDED");
|
||||
}
|
||||
|
||||
const dest = await this.getPathNotAleardyExist(await this.versions.getVersionPath(version));
|
||||
|
||||
sub = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: version.OculusBinaryId, destination: dest }).subscribe(obs);
|
||||
})().catch(err => obs.error(err));
|
||||
|
||||
return () => {
|
||||
sub?.unsubscribe();
|
||||
this.oculusDownloader.stopDownload();
|
||||
}
|
||||
});
|
||||
return token;
|
||||
}),
|
||||
switchMap(token => {
|
||||
if(!downloadInfo.isVerification){
|
||||
return this.createDownloadVersion(downloadInfo.bsVersion).then(({version, dest}) => ({token, version, dest}))
|
||||
}
|
||||
return this.versions.getVersionPath(downloadInfo.bsVersion).then(path => ({token, version: downloadInfo.bsVersion, dest: path}));
|
||||
}),
|
||||
switchMap(({token, version, dest}) => {
|
||||
return this.oculusDownloader.downloadApp({ accessToken: token, binaryId: version.OculusBinaryId, destination: dest }).pipe(map(
|
||||
progress => ({...progress, data: version})
|
||||
));
|
||||
}),
|
||||
finalize(() => this.oculusDownloader.stopDownload())
|
||||
);
|
||||
}
|
||||
|
||||
public clearTokenCookie(): Promise<void>{
|
||||
public autoDownloadVersion(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
|
||||
|
||||
let failed = false;
|
||||
let downloadVersion: BSVersion
|
||||
|
||||
return from(this.getAuthToken()).pipe(
|
||||
map(token => {
|
||||
if(!token){
|
||||
throw new CustomError("No token has been found while try to auto download Beat Saber from Oculus", "OCULUS_TOKEN_NEEDED");
|
||||
}
|
||||
return token;
|
||||
}),
|
||||
switchMap(token => {
|
||||
if(!downloadInfo.isVerification){
|
||||
return this.createDownloadVersion(downloadInfo.bsVersion).then(({version, dest}) => ({token, version, dest}));
|
||||
}
|
||||
return this.versions.getVersionPath(downloadInfo.bsVersion).then(path => ({token, version: downloadInfo.bsVersion, dest: path}));
|
||||
}),
|
||||
switchMap(({token, version, dest}) => {
|
||||
downloadVersion = version;
|
||||
return this.oculusDownloader.downloadApp({ accessToken: token, binaryId: version.OculusBinaryId, destination: dest }).pipe(
|
||||
map(progress => ({...progress, data: version})),
|
||||
);
|
||||
}),
|
||||
catchError(err => {
|
||||
failed = true;
|
||||
return throwError(() => err);
|
||||
}),
|
||||
finalize(() => from(!failed && this.versions.setVersionMetadata(downloadVersion, "store", BsStore.OCULUS))),
|
||||
finalize(() => this.oculusDownloader.stopDownload()),
|
||||
);
|
||||
}
|
||||
|
||||
public clearAuthToken(): Promise<void>{
|
||||
return session.defaultSession.clearStorageData({ storages: ["cookies"], origin: ".oculus.com" })
|
||||
}
|
||||
|
||||
+48
-59
@@ -1,20 +1,21 @@
|
||||
import { BS_APP_ID, BS_DEPOT } from "../constants";
|
||||
import { BS_APP_ID, BS_DEPOT } from "../../constants";
|
||||
import path from "path";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { UtilsService } from "./utils.service";
|
||||
import { UtilsService } from "../utils.service";
|
||||
import { spawnSync } from "child_process";
|
||||
import log from "electron-log";
|
||||
import { InstallationLocationService } from "./installation-location.service";
|
||||
import { BSLocalVersionService } from "./bs-local-version.service";
|
||||
import { copy, ensureDir } from "fs-extra";
|
||||
import { pathExist } from "../helpers/fs.helpers";
|
||||
import { Observable, map } from "rxjs";
|
||||
import { DepotDownloaderArgsOptions, DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent } from "../../shared/models/depot-downloader.model";
|
||||
import { DepotDownloader } from "../models/depot-downloader.class";
|
||||
import { InstallationLocationService } from "../installation-location.service";
|
||||
import { BSLocalVersionService } from "../bs-local-version.service";
|
||||
import { ensureDir } from "fs-extra";
|
||||
import { ensurePathNotAlreadyExist } from "../../helpers/fs.helpers";
|
||||
import { Observable, catchError, finalize, map, throwError } from "rxjs";
|
||||
import { DepotDownloaderArgsOptions, DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent } from "../../../shared/models/depot-downloader.model";
|
||||
import { DepotDownloader } from "../../models/depot-downloader.class";
|
||||
import { app } from "electron";
|
||||
import { BsStore } from "../../../shared/models/bs-store.enum";
|
||||
|
||||
export class BSInstallerService {
|
||||
private static instance: BSInstallerService;
|
||||
export class BsSteamDownloaderService {
|
||||
private static instance: BsSteamDownloaderService;
|
||||
|
||||
private readonly utils: UtilsService;
|
||||
private readonly installLocationService: InstallationLocationService;
|
||||
@@ -33,10 +34,10 @@ export class BSInstallerService {
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
if (!BSInstallerService.instance) {
|
||||
BSInstallerService.instance = new BSInstallerService();
|
||||
if (!BsSteamDownloaderService.instance) {
|
||||
BsSteamDownloaderService.instance = new BsSteamDownloaderService();
|
||||
}
|
||||
return BSInstallerService.instance;
|
||||
return BsSteamDownloaderService.instance;
|
||||
}
|
||||
|
||||
private getDepotDownloaderExePath(): string {
|
||||
@@ -59,10 +60,10 @@ export class BSInstallerService {
|
||||
}
|
||||
}
|
||||
|
||||
private async buildDepotDownloaderInstance(downloadInfos: DownloadInfo, qr?: boolean): Promise<DepotDownloader> {
|
||||
private async buildDepotDownloaderInstance(downloadInfos: DownloadSteamInfo, qr?: boolean): Promise<{depotDownloader: DepotDownloader, depotDownloaderOptions: DepotDownloaderArgsOptions, version: BSVersion}> {
|
||||
|
||||
const versionPath = await this.localVersionService.getVersionPath(downloadInfos.bsVersion);
|
||||
const dest = downloadInfos.isVerification ? versionPath : await this.getPathNotAleardyExist(versionPath);
|
||||
const dest = downloadInfos.isVerification ? versionPath : await ensurePathNotAlreadyExist(versionPath);
|
||||
const downloadVersion: BSVersion = { ...downloadInfos.bsVersion, ...(path.basename(dest) !== downloadInfos.bsVersion.BSVersion && { name: path.basename(dest) }) };
|
||||
|
||||
const depotDownloaderOptions: DepotDownloaderArgsOptions = {
|
||||
@@ -83,20 +84,22 @@ export class BSInstallerService {
|
||||
const exePath = this.getDepotDownloaderExePath();
|
||||
const args = DepotDownloader.buildArgs(depotDownloaderOptions);
|
||||
|
||||
return new DepotDownloader({
|
||||
const depotDownloader = new DepotDownloader({
|
||||
command: isLinux ? 'dotnet' : exePath,
|
||||
args: isLinux ? [exePath, ...args] : args,
|
||||
options: { cwd: await this.installLocationService.versionsDirectory() },
|
||||
echoStartData: downloadVersion
|
||||
}, log);
|
||||
|
||||
return { depotDownloader, depotDownloaderOptions, version: downloadVersion }
|
||||
}
|
||||
|
||||
private buildDepotDownloaderObservable(downloadInfos: DownloadInfo, qr?: boolean): Observable<DepotDownloaderEvent> {
|
||||
private buildDepotDownloaderObservable(downloadInfos: DownloadSteamInfo, qr?: boolean): Observable<DepotDownloaderEvent> {
|
||||
return new Observable(sub => {
|
||||
|
||||
const depotDownloaderBuildPromise = this.buildDepotDownloaderInstance(downloadInfos, qr);
|
||||
|
||||
depotDownloaderBuildPromise.then(depotDownloader => {
|
||||
depotDownloaderBuildPromise.then(({ depotDownloader, version }) => {
|
||||
|
||||
if(this.depotDownloader?.running){
|
||||
this.depotDownloader.stop();
|
||||
@@ -104,12 +107,23 @@ export class BSInstallerService {
|
||||
|
||||
this.depotDownloader = depotDownloader;
|
||||
|
||||
depotDownloader.$events().pipe(map(event => {
|
||||
if(event.type === DepotDownloaderEventType.Error){
|
||||
throw event;
|
||||
}
|
||||
return event;
|
||||
})).subscribe(sub);
|
||||
let failed = false;
|
||||
|
||||
depotDownloader.$events().pipe(
|
||||
map(event => {
|
||||
if(event.type === DepotDownloaderEventType.Error){
|
||||
throw event;
|
||||
}
|
||||
return event;
|
||||
}),
|
||||
catchError(err => {
|
||||
failed = true;
|
||||
return throwError(() => err);
|
||||
}),
|
||||
finalize(() => {
|
||||
!failed && this.localVersionService.setVersionMetadata(version, "store", BsStore.STEAM);
|
||||
})
|
||||
).subscribe(sub);
|
||||
|
||||
}).catch(err => sub.error({
|
||||
type: DepotDownloaderEventType.Error,
|
||||
@@ -118,16 +132,16 @@ export class BSInstallerService {
|
||||
} as DepotDownloaderEvent));
|
||||
|
||||
return () => {
|
||||
depotDownloaderBuildPromise.then(depotDownloader => depotDownloader.stop());
|
||||
depotDownloaderBuildPromise.then(({ depotDownloader }) => depotDownloader.stop());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public downloadBsVersion(downloadInfos: DownloadInfo): Observable<DepotDownloaderEvent> {
|
||||
public downloadBsVersion(downloadInfos: DownloadSteamInfo): Observable<DepotDownloaderEvent> {
|
||||
return this.buildDepotDownloaderObservable(downloadInfos);
|
||||
}
|
||||
|
||||
public autoDownloadBsVersion(downloadInfos: DownloadInfo): Observable<DepotDownloaderEvent> {
|
||||
public autoDownloadBsVersion(downloadInfos: DownloadSteamInfo): Observable<DepotDownloaderEvent> {
|
||||
return this.buildDepotDownloaderObservable({...downloadInfos, password: null, stay: true}).pipe(map(event => {
|
||||
if(event.type === DepotDownloaderEventType.Info && event.subType === DepotDownloaderInfoEvent.Password){
|
||||
throw new Error("Ask for password while auto download");
|
||||
@@ -136,7 +150,7 @@ export class BSInstallerService {
|
||||
}));
|
||||
}
|
||||
|
||||
public downloadBsVersionWithQRCode(downloadInfos: DownloadInfo): Observable<DepotDownloaderEvent> {
|
||||
public downloadBsVersionWithQRCode(downloadInfos: DownloadSteamInfo): Observable<DepotDownloaderEvent> {
|
||||
return this.buildDepotDownloaderObservable(downloadInfos, true);
|
||||
}
|
||||
|
||||
@@ -147,42 +161,17 @@ export class BSInstallerService {
|
||||
public stopDownload(): void {
|
||||
this.depotDownloader?.stop();
|
||||
}
|
||||
|
||||
private async getPathNotAleardyExist(path: string): Promise<string> {
|
||||
let destPath = path;
|
||||
let folderExist = await pathExist(destPath);
|
||||
let i = 0;
|
||||
|
||||
while (folderExist) {
|
||||
i++;
|
||||
destPath = `${path} (${i})`;
|
||||
folderExist = await pathExist(destPath);
|
||||
}
|
||||
|
||||
return destPath;
|
||||
}
|
||||
|
||||
public async importVersion(path: string): Promise<BSVersion> {
|
||||
const version = await this.localVersionService.getVersionOfBSFolder(path);
|
||||
|
||||
if (!version) {
|
||||
throw new Error("NOT_BS_FOLDER");
|
||||
}
|
||||
|
||||
const destPath = await this.getPathNotAleardyExist(await this.localVersionService.getVersionPath(version));
|
||||
|
||||
await copy(path, destPath, { dereference: true });
|
||||
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
export interface DownloadInfo {
|
||||
bsVersion: BSVersion;
|
||||
isVerification?: boolean;
|
||||
stay?: boolean;
|
||||
}
|
||||
|
||||
export interface DownloadSteamInfo extends DownloadInfo {
|
||||
username?: string;
|
||||
password?: string;
|
||||
stay?: boolean;
|
||||
isVerification?: boolean;
|
||||
}
|
||||
|
||||
export interface DownloadEvent {
|
||||
+3
-1
@@ -34,6 +34,8 @@ export const ChooseStore: ModalComponent<BsStore> = ({ resolver }) => {
|
||||
}
|
||||
})();
|
||||
|
||||
// TODO : Translate
|
||||
|
||||
return (
|
||||
<form className="flex flex-col gap-3">
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("which platform ?")}</h1>
|
||||
@@ -41,7 +43,7 @@ export const ChooseStore: ModalComponent<BsStore> = ({ resolver }) => {
|
||||
<div className="flex flex-row w-full flex-grow gap-4">
|
||||
<div className="flex flex-col flex-grow basis-0 gap-2 text-center px-5 pt-3 pb-1 rounded-md border-main-color-3 border-2 cursor-pointer" onMouseEnter={() => setOculusHover(true)} onMouseLeave={() => setOculusHover(false)} onClick={() => chooseStore(BsStore.OCULUS)} style={{backgroundColor: oculusHover ? bg.dim : bg.bright}}>
|
||||
<OculusIcon className="flex-grow aspect-square text-black bg-white rounded-full p-5"/>
|
||||
<h2 className="font-bold">Oculus PC</h2>
|
||||
<h2 className="font-bold">Oculus Store (PC)</h2>
|
||||
</div>
|
||||
<div className="flex flex-col flex-grow basis-0 gap-2 text-center px-5 pt-3 pb-1 rounded-md border-main-color-3 border-2 cursor-pointer" onMouseEnter={() => setSteamHover(true)} onMouseLeave={() => setSteamHover(false)} onClick={() => chooseStore(BsStore.STEAM)} style={{backgroundColor: steamHover ? bg.dim : bg.bright}}>
|
||||
<SteamIcon className="flex-grow"/>
|
||||
|
||||
@@ -3,18 +3,34 @@ import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { ModalComponent, ModalExitCode } from "renderer/services/modale.service";
|
||||
import BeatImpatient from "../../../../../assets/images/apngs/beat-impatient.png";
|
||||
import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useState } from "react";
|
||||
import { BsStore } from "shared/models/bs-store.enum";
|
||||
|
||||
export const ImportVersionModal: ModalComponent<void> = ({ resolver }) => {
|
||||
export const ImportVersionModal: ModalComponent<BsStore> = ({ resolver }) => {
|
||||
|
||||
const t = useTranslation();
|
||||
const [isOculus, setIsOculus] = useState(false);
|
||||
|
||||
const submit = () => {
|
||||
resolver({ exitCode: ModalExitCode.COMPLETED, data: isOculus ? BsStore.OCULUS : BsStore.STEAM });
|
||||
}
|
||||
|
||||
return (
|
||||
<form>
|
||||
<h1 className="text-3xl uppercase tracking-wide w-full text-center text-gray-800 dark:text-gray-200">{t("modals.bs-import-version.title")}</h1>
|
||||
<BsmImage className="mx-auto h-20" image={BeatImpatient} />
|
||||
<p className="max-w-sm text-gray-800 dark:text-gray-200">{t("modals.bs-import-version.description")}</p>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4 mt-4">
|
||||
<Tippy content="Cocher si c'est une version Oculus" placement="right" arrow={false} className="!bg-neutral-900 font-bold">
|
||||
<div className="relative flex flex-row items-center gap-1.5 mb-4 mt-3 cursor-help w-fit">
|
||||
<BsmCheckbox className="relative h-5 w-5 z-[1]" checked={isOculus} onChange={checked => setIsOculus(checked)}/>
|
||||
<span className="font-bold italic">Version Oculus</span> {/* TODO Translate */}
|
||||
</div>
|
||||
</Tippy>
|
||||
<div className="grid grid-flow-col grid-cols-2 gap-4">
|
||||
<BsmButton typeColor="cancel" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.CANCELED })} withBar={false} text="misc.cancel" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={() => resolver({ exitCode: ModalExitCode.COMPLETED })} withBar={false} text="modals.bs-import-version.buttons.submit" />
|
||||
<BsmButton typeColor="primary" className="rounded-md text-center transition-all" onClick={submit} withBar={false} text="modals.bs-import-version.buttons.submit" />
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import { SteamDownloaderService } from "renderer/services/bs-downgrade/steam-downloader.service";
|
||||
import { useState } from "react";
|
||||
import { distinctUntilChanged, map, of, Subscription, switchMap } from "rxjs";
|
||||
import { BSLauncherService, LaunchMods } from "renderer/services/bs-launcher.service";
|
||||
@@ -15,13 +14,17 @@ import Tippy from "@tippyjs/react";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import equal from "fast-deep-equal";
|
||||
import { useOnUpdate } from "renderer/hooks/use-on-update.hook";
|
||||
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
|
||||
import { ProgressBarService } from "renderer/services/progress-bar.service";
|
||||
|
||||
export function BsVersionItem(props: { version: BSVersion }) {
|
||||
const downloaderService = useService(SteamDownloaderService);
|
||||
|
||||
const bsDownloader = useService(BsDownloaderService);
|
||||
const verionManagerService = useService(BSVersionManagerService);
|
||||
const launcherService = useService(BSLauncherService);
|
||||
const configService = useService(ConfigurationService);
|
||||
const bsUninstallerService = useService(BSUninstallerService);
|
||||
const progressBar = useService(ProgressBarService);
|
||||
|
||||
const { state } = useLocation() as { state: BSVersion };
|
||||
const { fontSize, ref } = useFitText();
|
||||
@@ -33,14 +36,14 @@ export function BsVersionItem(props: { version: BSVersion }) {
|
||||
useOnUpdate(() => {
|
||||
const subs: Subscription[] = []
|
||||
|
||||
subs.push(downloaderService.currentBsVersionDownload$.pipe(map(download => equal(download, props.version)), distinctUntilChanged()).subscribe(isDownloading => {
|
||||
subs.push(bsDownloader.downloadingVersion$.pipe(map(download => equal(download, props.version)), distinctUntilChanged()).subscribe(isDownloading => {
|
||||
setIsDownloading(() => isDownloading);
|
||||
}));
|
||||
|
||||
subs.push(downloaderService.currentBsVersionDownload$.pipe(
|
||||
subs.push(bsDownloader.downloadingVersion$.pipe(
|
||||
map(download => equal(download, props.version)),
|
||||
distinctUntilChanged(),
|
||||
switchMap(isDownloading => isDownloading ? downloaderService.downloadProgress$ : of(0)),
|
||||
switchMap(isDownloading => isDownloading ? progressBar.progress$ : of(0)),
|
||||
).subscribe(progress => {
|
||||
setDownloadProgress(() => progress);
|
||||
}));
|
||||
@@ -62,9 +65,9 @@ export function BsVersionItem(props: { version: BSVersion }) {
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
const versionDownload = downloaderService.currentBsVersionDownload$.value;
|
||||
const wasVerification = downloaderService.isVerification;
|
||||
downloaderService.stopDownload().then(() => {
|
||||
const versionDownload = bsDownloader.downloadingVersion;
|
||||
const wasVerification = bsDownloader.isVerifying;
|
||||
bsDownloader.stopDownload().then(() => {
|
||||
if(wasVerification){ return; }
|
||||
bsUninstallerService.uninstall(versionDownload).then(res => res && verionManagerService.askInstalledVersions());
|
||||
});
|
||||
|
||||
@@ -11,15 +11,15 @@ import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import Tippy from "@tippyjs/react";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { SteamDownloaderService } from "renderer/services/bs-downgrade/steam-downloader.service";
|
||||
import { distinctUntilChanged } from "rxjs";
|
||||
import equal from "fast-deep-equal";
|
||||
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
|
||||
|
||||
export function NavBar() {
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
const versionDownloader = useService(SteamDownloaderService);
|
||||
const versionDownloader = useService(BsDownloaderService);
|
||||
|
||||
const downloadingVersion = useObservable(versionDownloader.currentBsVersionDownload$.pipe(distinctUntilChanged(equal)));
|
||||
const downloadingVersion = useObservable(versionDownloader.downloadingVersion$.pipe(distinctUntilChanged(equal)));
|
||||
const installedVersions = useObservable(versionManager.installedVersions$);
|
||||
|
||||
const color = useThemeColor("first-color");
|
||||
|
||||
@@ -10,6 +10,7 @@ import { LaunchModToogle } from "./launch-mod-toogle.component";
|
||||
import BSLogo from "../../../../../../assets/images/apngs/bs-logo.png";
|
||||
import { BsmImage } from "renderer/components/shared/bsm-image.component";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { BsStore } from "shared/models/bs-store.enum";
|
||||
|
||||
type Props = { version: BSVersion };
|
||||
|
||||
@@ -73,7 +74,7 @@ export function LaunchSlide({ version }: Props) {
|
||||
<h1 className="relative text-4xl font-bold italic -top-3">{version.name ? `${version.BSVersion} - ${version.name}` : version.BSVersion}</h1>
|
||||
</div>
|
||||
<div className="grid grid-flow-col gap-6">
|
||||
{!version.oculus && <LaunchModToogle infoText="pages.version-viewer.launch-mods.oculus-description" icon="oculus" onClick={() => setMode(LaunchMods.OCULUS_MOD, !oculusMode)} active={oculusMode} text="pages.version-viewer.launch-mods.oculus" />}
|
||||
{!(version.oculus || version.store === BsStore.OCULUS) && <LaunchModToogle infoText="pages.version-viewer.launch-mods.oculus-description" icon="oculus" onClick={() => setMode(LaunchMods.OCULUS_MOD, !oculusMode)} active={oculusMode} text="pages.version-viewer.launch-mods.oculus" />}
|
||||
<LaunchModToogle infoText="pages.version-viewer.launch-mods.desktop-description" icon="desktop" onClick={() => setMode(LaunchMods.DESKTOP_MOD, !desktopMode)} active={desktopMode} text="pages.version-viewer.launch-mods.desktop" />
|
||||
<LaunchModToogle infoText="pages.version-viewer.launch-mods.debug-description" icon="terminal" onClick={() => setMode(LaunchMods.DEBUG_MOD, !debugMode)} active={debugMode} text="pages.version-viewer.launch-mods.debug" />
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { AvailableVersionsSlider } from "../components/available-versions/available-versions-slider.component";
|
||||
import { SteamDownloaderService } from "../services/bs-downgrade/steam-downloader.service";
|
||||
import { Slideshow } from "renderer/components/slideshow/slideshow.component";
|
||||
import { createContext, useMemo, useState } from "react";
|
||||
import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
@@ -7,80 +6,38 @@ import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useTranslation } from "renderer/hooks/use-translation.hook";
|
||||
import { BSVersionManagerService } from "renderer/services/bs-version-manager.service";
|
||||
import { BsmDropdownButton } from "renderer/components/shared/bsm-dropdown-button.component";
|
||||
import { ProgressBarService } from "renderer/services/progress-bar.service";
|
||||
import { ModalExitCode, ModalService } from "renderer/services/modale.service";
|
||||
import { ImportVersionModal } from "renderer/components/modal/modal-types/import-version-modal.component";
|
||||
import { IpcService } from "renderer/services/ipc.service";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { lastValueFrom, map } from "rxjs";
|
||||
import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { BsDownloaderService } from "renderer/services/bs-downgrade/bs-downloader.service";
|
||||
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
|
||||
|
||||
export const AvailableVersionsContext = createContext<{ selectedVersion: BSVersion; setSelectedVersion: (version: BSVersion) => void }>(null);
|
||||
|
||||
export function AvailableVersionsList() {
|
||||
|
||||
const steamDownloader = useService(SteamDownloaderService);
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
const progressBar = useService(ProgressBarService);
|
||||
const modal = useService(ModalService);
|
||||
const ipc = useService(IpcService);
|
||||
const notification = useService(NotificationService);
|
||||
const config = useService(ConfigurationService);
|
||||
const bsDownloader = useService(BsDownloaderService);
|
||||
const versionManager = useService(BSVersionManagerService);
|
||||
|
||||
const [selectedVersion, setSelectedVersion] = useState<BSVersion>(null);
|
||||
const contextValue = useMemo(() => ({ selectedVersion, setSelectedVersion }), [selectedVersion]);
|
||||
|
||||
const downloading = useObservable(steamDownloader.currentBsVersionDownload$.pipe(map(v => !!v)));
|
||||
const downloading = useObservable(bsDownloader.downloadingVersion$.pipe(map(v => !!v)));
|
||||
const t = useTranslation();
|
||||
|
||||
const startDownload = async () => {
|
||||
|
||||
const store = bsDownloader.getLastStoreDownloadedFrom() ?? await bsDownloader.chooseStoreToDownloadFrom();
|
||||
const store = bsDownloader.getLastStoreDownloadedFrom() ?? await bsDownloader.chooseStoreToDownloadFrom().catch(() => null);
|
||||
|
||||
if(!store){ return; }
|
||||
|
||||
return bsDownloader.downloadVersion(selectedVersion, store)
|
||||
.catch(console.log)
|
||||
.finally(() => setSelectedVersion(null));
|
||||
};
|
||||
|
||||
const importVersion = async () => {
|
||||
if (!progressBar.require()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const modalRes = await modal.openModal(ImportVersionModal);
|
||||
|
||||
if (modalRes.exitCode !== ModalExitCode.COMPLETED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const folderRes = await lastValueFrom(ipc.sendV2<{ canceled: boolean; filePaths: string[] }>("choose-folder"))
|
||||
|
||||
if (!folderRes || folderRes.canceled || !folderRes.filePaths?.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
notification.notifySuccess({ title: "notifications.bs-import-version.success.start-import.title", desc: "notifications.bs-import-version.success.start-import.desc", duration: 6_000 });
|
||||
|
||||
progressBar.showFake(0.008);
|
||||
|
||||
const toImport = folderRes.filePaths.at(0);
|
||||
|
||||
const imported = await steamDownloader.importVersion(toImport);
|
||||
|
||||
if (imported) {
|
||||
versionManager.askInstalledVersions();
|
||||
notification.notifySuccess({ title: "notifications.bs-import-version.success.imported.title", duration: 3_000 });
|
||||
progressBar.complete();
|
||||
} else {
|
||||
notification.notifyError({ title: "notifications.types.error", desc: "notifications.bs-import-version.errors.import-error.desc" });
|
||||
}
|
||||
|
||||
progressBar.hide(true);
|
||||
const importVersion = () => {
|
||||
return lastValueFrom(versionManager.importVersion()).catch(() => {});
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,7 +6,7 @@ import { BsmButton } from "renderer/components/shared/bsm-button.component";
|
||||
import { BsmIconType } from "renderer/components/svgs/bsm-icon.component";
|
||||
import { DefaultConfigKey, ThemeConfig } from "renderer/config/default-configuration.config";
|
||||
import { useThemeColor } from "renderer/hooks/use-theme-color.hook";
|
||||
import { SteamDownloaderService } from "renderer/services/bs-downgrade/steam-downloader.service";
|
||||
import { SteamDownloaderService } from "renderer/services/bs-version-download/steam-downloader.service";
|
||||
import { ConfigurationService } from "renderer/services/configuration.service";
|
||||
import { I18nService } from "renderer/services/i18n.service";
|
||||
import { IpcService } from "renderer/services/ipc.service";
|
||||
@@ -35,7 +35,7 @@ import { useService } from "renderer/hooks/use-service.hook";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { BsmException } from "shared/models/bsm-exception.model";
|
||||
import { useObservable } from "renderer/hooks/use-observable.hook";
|
||||
import { AuthUserService } from "renderer/services/auth-user.service";
|
||||
import { OculusDownloaderService } from "renderer/services/bs-version-download/oculus-downloader.service";
|
||||
|
||||
export function SettingsPage() {
|
||||
|
||||
@@ -43,7 +43,8 @@ export function SettingsPage() {
|
||||
const themeService = useService(ThemeService);
|
||||
const ipcService = useService(IpcService);
|
||||
const modalService = useService(ModalService);
|
||||
const downloaderService = useService(SteamDownloaderService);
|
||||
const steamDownloader = useService(SteamDownloaderService);
|
||||
const oculusDownloader = useService(OculusDownloaderService);
|
||||
const progressBarService = useService(ProgressBarService);
|
||||
const notificationService = useService(NotificationService);
|
||||
const i18nService = useService(I18nService);
|
||||
@@ -52,7 +53,6 @@ export function SettingsPage() {
|
||||
const playlistsManager = useService(PlaylistsManagerService);
|
||||
const modelsManager = useService(ModelsManagerService);
|
||||
const versionLinker = useService(VersionFolderLinkerService);
|
||||
const authService = useService(AuthUserService);
|
||||
|
||||
const { firstColor, secondColor } = useThemeColor();
|
||||
|
||||
@@ -79,11 +79,12 @@ export function SettingsPage() {
|
||||
const [mapDeepLinksEnabled, setMapDeepLinksEnabled] = useState(false);
|
||||
const [playlistsDeepLinkEnabled, setPlaylistsDeepLinkEnabled] = useState(false);
|
||||
const [modelsDeepLinkEnabled, setModelsDeepLinkEnabled] = useState(false);
|
||||
const [hasDownloaderSession, setHasDownloaderSession] = useState(false);
|
||||
const appVersion = useObservable(ipcService.sendV2<string>("current-version"));
|
||||
const steamSessionExist = useObservable(authService.sessionExist$);
|
||||
|
||||
useEffect(() => {
|
||||
loadInstallationFolder();
|
||||
loadDownloadersSession();
|
||||
mapsManager.isDeepLinksEnabled().then(enabled => setMapDeepLinksEnabled(() => enabled));
|
||||
playlistsManager.isDeepLinksEnabled().then(enabled => setPlaylistsDeepLinkEnabled(() => enabled));
|
||||
modelsManager.isDeepLinksEnabled().then(enabled => setModelsDeepLinkEnabled(() => enabled));
|
||||
@@ -97,9 +98,23 @@ export function SettingsPage() {
|
||||
};
|
||||
|
||||
const loadInstallationFolder = () => {
|
||||
downloaderService.getInstallationFolder().then(res => setInstallationFolder(res));
|
||||
steamDownloader.getInstallationFolder().then(res => setInstallationFolder(res));
|
||||
};
|
||||
|
||||
const loadDownloadersSession = () => {
|
||||
if(steamDownloader.sessionExist()){ return setHasDownloaderSession(true); }
|
||||
|
||||
oculusDownloader.hasAuthToken().then(hasToken => {
|
||||
setHasDownloaderSession(hasToken);
|
||||
});
|
||||
}
|
||||
|
||||
const clearDownloadersSession = () => {
|
||||
steamDownloader.deleteSteamSession();
|
||||
oculusDownloader.clearAuthToken();
|
||||
loadDownloadersSession();
|
||||
}
|
||||
|
||||
const setFirstColorSetting = (hex: string) => configService.set("first-color", hex);
|
||||
const setSecondColorSetting = (hex: string) => configService.set("second-color", hex);
|
||||
|
||||
@@ -131,7 +146,7 @@ export function SettingsPage() {
|
||||
|
||||
notificationService.notifySuccess({ title: "notifications.settings.move-folder.success.titles.transfer-started", desc: "notifications.settings.move-folder.success.descs.transfer-started" });
|
||||
|
||||
lastValueFrom(downloaderService.setInstallationFolder(fileChooserRes.filePaths[0])).then(res => {
|
||||
lastValueFrom(steamDownloader.setInstallationFolder(fileChooserRes.filePaths[0])).then(res => {
|
||||
|
||||
progressBarService.complete();
|
||||
progressBarService.hide(true);
|
||||
@@ -207,8 +222,8 @@ export function SettingsPage() {
|
||||
<BsmButton className="inline-block grow-0 bg-transparent sticky h-full w-full top-20 right-20 !m-0 rounded-full p-1" onClick={() => nav(-1)} icon="close" withBar={false} />
|
||||
</div>
|
||||
|
||||
<SettingContainer title="pages.settings.steam.title" description="pages.settings.steam.description">
|
||||
<BsmButton onClick={() => authService.deleteSteamSession()} className="w-fit px-3 py-[2px] text-white rounded-md" withBar={false} text="pages.settings.steam.logout" typeColor="error" disabled={!steamSessionExist}/>
|
||||
<SettingContainer title="pages.settings.steam-and-oculus.title" description="pages.settings.steam-and-oculus.description">
|
||||
<BsmButton onClick={clearDownloadersSession} className="w-fit px-3 py-[2px] text-white rounded-md" withBar={false} text="pages.settings.steam-and-oculus.logout" typeColor="error" disabled={!hasDownloaderSession}/>
|
||||
</SettingContainer>
|
||||
|
||||
<SettingContainer title="pages.settings.appearance.title" description="pages.settings.appearance.description">
|
||||
|
||||
@@ -8,7 +8,6 @@ import { BSUninstallerService } from "../services/bs-uninstaller.service";
|
||||
import { BSVersionManagerService } from "../services/bs-version-manager.service";
|
||||
import { ModalExitCode, ModalService } from "../services/modale.service";
|
||||
import DefautVersionImage from "../../../assets/images/default-version-img.jpg";
|
||||
import { SteamDownloaderService } from "renderer/services/bs-downgrade/steam-downloader.service";
|
||||
import { IpcService } from "renderer/services/ipc.service";
|
||||
import { LaunchSlide } from "renderer/components/version-viewer/slides/launch/launch-slide.component";
|
||||
import { ModsSlide } from "renderer/components/version-viewer/slides/mods/mods-slide.component";
|
||||
@@ -21,13 +20,14 @@ import { BSLauncherService } from "renderer/services/bs-launcher.service";
|
||||
import { CreateLaunchShortcutModal } from "renderer/components/modal/modal-types/create-launch-shortcut-modal.component";
|
||||
import { lastValueFrom } from "rxjs";
|
||||
import { NotificationService } from "renderer/services/notification.service";
|
||||
import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service";
|
||||
|
||||
export function VersionViewer() {
|
||||
|
||||
const bsUninstallerService = useService(BSUninstallerService);
|
||||
const bsVersionManagerService = useService(BSVersionManagerService);
|
||||
const modalService = useService(ModalService);
|
||||
const bsDownloaderService = useService(SteamDownloaderService);
|
||||
const bsDownloader = useService(BsDownloaderService);
|
||||
const ipcService = useService(IpcService);
|
||||
const bsLauncher = useService(BSLauncherService);
|
||||
const notification = useService(NotificationService);
|
||||
@@ -43,7 +43,7 @@ export function VersionViewer() {
|
||||
navigate(`/bs-version/${version.BSVersion}`, { state: version });
|
||||
};
|
||||
const openFolder = () => ipcService.sendLazy("bs-version.open-folder", { args: state });
|
||||
const verifyFiles = () => bsDownloaderService.verifyBsVersionFiles(state);
|
||||
const verifyFiles = () => bsDownloader.verifyBsVersion(state);
|
||||
|
||||
const uninstall = async () => {
|
||||
const modalCompleted = await modalService.openModal(UninstallModal, state);
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Observable } from "rxjs";
|
||||
import { map } from "rxjs/operators";
|
||||
import { ConfigurationService } from "./configuration.service";
|
||||
|
||||
// TODO : No need for a separated service, just use steam-downloader.service.ts
|
||||
|
||||
export class AuthUserService {
|
||||
private static instance: AuthUserService;
|
||||
|
||||
private readonly configService: ConfigurationService;
|
||||
|
||||
private readonly STEAM_USERNAME_KEY = "STEAM-USERNAME";
|
||||
private readonly STEAM_ID_KEY = "STEAM-ID";
|
||||
|
||||
public static getInstance(): AuthUserService {
|
||||
if (!AuthUserService.instance) {
|
||||
AuthUserService.instance = new AuthUserService();
|
||||
}
|
||||
return AuthUserService.instance;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
this.configService = ConfigurationService.getInstance();
|
||||
}
|
||||
|
||||
public sessionExist(): boolean {
|
||||
return !!this.configService.get(this.STEAM_USERNAME_KEY);
|
||||
}
|
||||
|
||||
public get sessionExist$(): Observable<boolean> {
|
||||
return this.configService.watch(this.STEAM_USERNAME_KEY).pipe(map(v => !!v));
|
||||
}
|
||||
|
||||
public setSteamSession(username: string, stay = true): void {
|
||||
this.configService.set(this.STEAM_USERNAME_KEY, username, stay);
|
||||
}
|
||||
|
||||
public setSteamID(steamID: string): void {
|
||||
this.configService.set(this.STEAM_ID_KEY, steamID);
|
||||
}
|
||||
|
||||
public getSteamUsername(): string {
|
||||
return this.configService.get(this.STEAM_USERNAME_KEY);
|
||||
}
|
||||
|
||||
public deleteSteamSession(): void {
|
||||
this.configService.delete(this.STEAM_USERNAME_KEY);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import { BsStore } from "shared/models/bs-store.enum";
|
||||
import { ConfigurationService } from "../configuration.service";
|
||||
import { BehaviorSubject } from "rxjs";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { ModalExitCode, ModalService } from "../modale.service";
|
||||
import { ChooseStore } from "renderer/components/modal/modal-types/bs-downgrade/choose-store-modal.component";
|
||||
import { SteamDownloaderService } from "./steam-downloader.service";
|
||||
import { OculusDownloaderService } from "./oculus-downloader.service";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
|
||||
export class BsDownloaderService {
|
||||
|
||||
private static instance: BsDownloaderService;
|
||||
|
||||
public static getInstance(): BsDownloaderService {
|
||||
if (!BsDownloaderService.instance) {
|
||||
BsDownloaderService.instance = new BsDownloaderService();
|
||||
}
|
||||
|
||||
return BsDownloaderService.instance;
|
||||
}
|
||||
|
||||
private readonly config: ConfigurationService;
|
||||
private readonly modals: ModalService;
|
||||
private readonly steamDownloader: SteamDownloaderService;
|
||||
private readonly oculusDownloader: OculusDownloaderService; // TODO : create oculus downloader
|
||||
|
||||
private readonly downloadingVersion$ = new BehaviorSubject<BSVersion>(null); // <= TODO : will replace obs in SteamDownloaderService
|
||||
|
||||
private constructor(){
|
||||
this.config = ConfigurationService.getInstance();
|
||||
this.modals = ModalService.getInstance();
|
||||
this.steamDownloader = SteamDownloaderService.getInstance();
|
||||
this.oculusDownloader = OculusDownloaderService.getInstance();
|
||||
}
|
||||
|
||||
public getLastStoreDownloadedFrom(): BsStore | undefined {
|
||||
const lastStore = this.config.get("lastStoreDownloadedFrom");
|
||||
|
||||
if(!lastStore){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return lastStore as BsStore;
|
||||
}
|
||||
|
||||
public async chooseStoreToDownloadFrom(): Promise<BsStore | undefined> {
|
||||
const res = await this.modals.openModal(ChooseStore);
|
||||
|
||||
if(res.exitCode !== ModalExitCode.COMPLETED){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return res.data;
|
||||
}
|
||||
|
||||
public async downloadVersion(version: BSVersion, from: BsStore): Promise<BSVersion | void> {
|
||||
|
||||
if(from === BsStore.STEAM || !version.OculusBinaryId){
|
||||
return this.steamDownloader.downloadBsVersion(version);
|
||||
}
|
||||
|
||||
if(from === BsStore.OCULUS){
|
||||
return this.oculusDownloader.downloadBsVersion(version);
|
||||
}
|
||||
}
|
||||
|
||||
public importVersion(): void {
|
||||
// TODO : open Modal
|
||||
// Will replace method in SteamDownloaderService
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import { Observable, catchError, lastValueFrom, map, of, tap, throwError } from "rxjs";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { IpcService } from "../ipc.service";
|
||||
import { Progression } from "main/helpers/fs.helpers";
|
||||
import { ProgressBarService } from "../progress-bar.service";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
import { NotificationService } from "../notification.service";
|
||||
import { OculusDownloadInfo } from "main/services/bs-oculus-downloader.service";
|
||||
import { ModalExitCode, ModalService } from "../modale.service";
|
||||
import { LoginToMetaModal } from "renderer/components/modal/modal-types/bs-downgrade/login-to-meta-modal.component";
|
||||
|
||||
export class OculusDownloaderService {
|
||||
|
||||
private static instance: OculusDownloaderService;
|
||||
|
||||
public static getInstance(): OculusDownloaderService {
|
||||
if (!OculusDownloaderService.instance) {
|
||||
OculusDownloaderService.instance = new OculusDownloaderService();
|
||||
}
|
||||
|
||||
return OculusDownloaderService.instance;
|
||||
}
|
||||
|
||||
private readonly ipc: IpcService;
|
||||
private readonly progressBar: ProgressBarService;
|
||||
private readonly notifications: NotificationService;
|
||||
private readonly modals: ModalService;
|
||||
|
||||
private constructor(){
|
||||
this.ipc = IpcService.getInstance();
|
||||
this.progressBar = ProgressBarService.getInstance();
|
||||
this.notifications = NotificationService.getInstance();
|
||||
this.modals = ModalService.getInstance();
|
||||
}
|
||||
|
||||
private handleDownloadErrors(err: CustomError): void{
|
||||
if(!err?.code){
|
||||
// handle unknown error
|
||||
}
|
||||
|
||||
this.notifications.notifyError({title: err.code});
|
||||
}
|
||||
|
||||
private handleDownload(download: Observable<Progression>): Observable<Progression> {
|
||||
const progress$ = download.pipe(map(progress => (progress.current / progress.total) * 100), catchError(() => of(0)));
|
||||
this.progressBar.show(progress$, true);
|
||||
|
||||
return download.pipe(tap({
|
||||
error: err => this.handleDownloadErrors(err),
|
||||
}));
|
||||
}
|
||||
|
||||
private tryAutoDownload(version: BSVersion): Observable<Progression>{
|
||||
return this.handleDownload(
|
||||
this.ipc.sendV2("bs-oculus-auto-download", { args: version })
|
||||
);
|
||||
}
|
||||
|
||||
private doDownloadBsVersion(downloadInfo: OculusDownloadInfo): Observable<Progression>{
|
||||
return this.handleDownload(
|
||||
this.ipc.sendV2("bs-oculus-download", { args: downloadInfo })
|
||||
);
|
||||
}
|
||||
|
||||
public async downloadBsVersion(version: BSVersion): Promise<BSVersion> {
|
||||
return (async () => {
|
||||
|
||||
const autoDownload = await lastValueFrom(this.tryAutoDownload(version)).then(() => true).catch(() => false);
|
||||
|
||||
if(autoDownload){
|
||||
return autoDownload;
|
||||
}
|
||||
|
||||
const [completed, stay] = await this.modals.openModal(LoginToMetaModal).then(res => [res.exitCode === ModalExitCode.COMPLETED, res.data]);
|
||||
|
||||
if(!completed){
|
||||
return completed;
|
||||
}
|
||||
|
||||
return lastValueFrom(this.doDownloadBsVersion({ version, stay })).then(() => true);
|
||||
|
||||
})().then(() => version)
|
||||
.finally(() => this.progressBar.hide(true));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { BehaviorSubject, Observable } from "rxjs";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
|
||||
export abstract class AbstractBsDownloaderService {
|
||||
private static readonly __downloadingVersion$ = new BehaviorSubject<BSVersion>(null);
|
||||
private static readonly __isVerifying$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
protected get _downloadingVersion$(){ return AbstractBsDownloaderService.__downloadingVersion$; }
|
||||
protected get _isVerifying$(){ return AbstractBsDownloaderService.__isVerifying$; }
|
||||
|
||||
public get downloadingVersion$(): Observable<BSVersion>{ return this._downloadingVersion$.asObservable(); }
|
||||
public get downloadingVersion(): BSVersion{ return this._downloadingVersion$.getValue(); }
|
||||
|
||||
public get isVerifying$(): Observable<boolean>{ return this._isVerifying$.asObservable(); }
|
||||
public get isVerifying(): boolean{ return this._isVerifying$.getValue(); }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { BsStore } from "shared/models/bs-store.enum";
|
||||
import { ConfigurationService } from "../configuration.service";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { ModalExitCode, ModalService } from "../modale.service";
|
||||
import { ChooseStore } from "renderer/components/modal/modal-types/bs-downgrade/choose-store-modal.component";
|
||||
import { SteamDownloaderService } from "./steam-downloader.service";
|
||||
import { OculusDownloaderService } from "./oculus-downloader.service";
|
||||
import { DownloaderServiceInterface } from "./bs-store-downloader.interface";
|
||||
import { AbstractBsDownloaderService } from "./abstract-bs-downloader.service";
|
||||
import { BSVersionManagerService } from "../bs-version-manager.service";
|
||||
|
||||
export class BsDownloaderService extends AbstractBsDownloaderService {
|
||||
|
||||
private static instance: BsDownloaderService;
|
||||
|
||||
public static getInstance(): BsDownloaderService {
|
||||
if (!BsDownloaderService.instance) {
|
||||
BsDownloaderService.instance = new BsDownloaderService();
|
||||
}
|
||||
|
||||
return BsDownloaderService.instance;
|
||||
}
|
||||
|
||||
private readonly config: ConfigurationService;
|
||||
private readonly modals: ModalService;
|
||||
private readonly steamDownloader: SteamDownloaderService;
|
||||
private readonly oculusDownloader: OculusDownloaderService;
|
||||
private readonly versionManager: BSVersionManagerService;
|
||||
|
||||
private constructor(){
|
||||
super();
|
||||
this.config = ConfigurationService.getInstance();
|
||||
this.modals = ModalService.getInstance();
|
||||
this.steamDownloader = SteamDownloaderService.getInstance();
|
||||
this.oculusDownloader = OculusDownloaderService.getInstance();
|
||||
this.versionManager = BSVersionManagerService.getInstance();
|
||||
}
|
||||
|
||||
private getStoreDownloader(bsStore: BsStore): DownloaderServiceInterface{
|
||||
switch(bsStore){
|
||||
case BsStore.OCULUS:
|
||||
return this.oculusDownloader;
|
||||
case BsStore.STEAM:
|
||||
return this.steamDownloader;
|
||||
default:
|
||||
throw new Error("Unknown store");
|
||||
}
|
||||
}
|
||||
|
||||
private resetDownloadState(){
|
||||
this._downloadingVersion$.next(null);
|
||||
this._isVerifying$.next(false);
|
||||
}
|
||||
|
||||
public getLastStoreDownloadedFrom(): BsStore | undefined {
|
||||
const lastStore = this.config.get("lastStoreDownloadedFrom");
|
||||
|
||||
if(!lastStore){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return lastStore as BsStore;
|
||||
}
|
||||
|
||||
public async chooseStoreToDownloadFrom(): Promise<BsStore | undefined> {
|
||||
const res = await this.modals.openModal(ChooseStore);
|
||||
|
||||
if(res.exitCode !== ModalExitCode.COMPLETED){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return res.data;
|
||||
}
|
||||
|
||||
public downloadVersion(version: BSVersion, from: BsStore): Promise<BSVersion> {
|
||||
return this.getStoreDownloader(from).downloadBsVersion(version).finally(() => {
|
||||
this.resetDownloadState();
|
||||
this.versionManager.askInstalledVersions();
|
||||
});
|
||||
}
|
||||
|
||||
public verifyBsVersion(version: BSVersion){
|
||||
this._isVerifying$.next(true);
|
||||
return this.getStoreDownloader(version.store).verifyBsVersion(version).finally(() => {
|
||||
this.resetDownloadState();
|
||||
this.versionManager.askInstalledVersions();
|
||||
});
|
||||
}
|
||||
|
||||
public async stopDownload(): Promise<void> {
|
||||
return Promise.all([
|
||||
this.steamDownloader.stopDownload(),
|
||||
this.oculusDownloader.stopDownload()
|
||||
]).then(() => {});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
|
||||
export interface DownloaderServiceInterface {
|
||||
downloadBsVersion(version: BSVersion): Promise<BSVersion>;
|
||||
verifyBsVersion(version: BSVersion): Promise<BSVersion>;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { Observable, Subscription, catchError, finalize, lastValueFrom, map, of, take, tap } from "rxjs";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { IpcService } from "../ipc.service";
|
||||
import { Progression } from "main/helpers/fs.helpers";
|
||||
import { ProgressBarService } from "../progress-bar.service";
|
||||
import { CustomError } from "shared/models/exceptions/custom-error.class";
|
||||
import { NotificationService } from "../notification.service";
|
||||
import { ModalExitCode, ModalService } from "../modale.service";
|
||||
import { LoginToMetaModal } from "renderer/components/modal/modal-types/bs-downgrade/login-to-meta-modal.component";
|
||||
import { DownloaderServiceInterface } from "./bs-store-downloader.interface";
|
||||
import { AbstractBsDownloaderService } from "./abstract-bs-downloader.service";
|
||||
import { DownloadInfo } from "main/services/bs-version-download/bs-steam-downloader.service";
|
||||
|
||||
export class OculusDownloaderService extends AbstractBsDownloaderService implements DownloaderServiceInterface{
|
||||
|
||||
private static instance: OculusDownloaderService;
|
||||
|
||||
public static getInstance(): OculusDownloaderService {
|
||||
if (!OculusDownloaderService.instance) {
|
||||
OculusDownloaderService.instance = new OculusDownloaderService();
|
||||
}
|
||||
|
||||
return OculusDownloaderService.instance;
|
||||
}
|
||||
|
||||
private readonly ipc: IpcService;
|
||||
private readonly progressBar: ProgressBarService;
|
||||
private readonly notifications: NotificationService;
|
||||
private readonly modals: ModalService;
|
||||
|
||||
private constructor(){
|
||||
super();
|
||||
this.ipc = IpcService.getInstance();
|
||||
this.progressBar = ProgressBarService.getInstance();
|
||||
this.notifications = NotificationService.getInstance();
|
||||
this.modals = ModalService.getInstance();
|
||||
}
|
||||
|
||||
private handleDownloadErrors(err: CustomError): void{
|
||||
if(!err?.code){
|
||||
// handle unknown error
|
||||
}
|
||||
|
||||
this.notifications.notifyError({title: err.code});
|
||||
}
|
||||
|
||||
private handleDownload(download: Observable<Progression<BSVersion>>): Observable<Progression<BSVersion>> {
|
||||
const progress$ = download.pipe(map(progress => (progress.current / progress.total) * 100), catchError(() => of(0)));
|
||||
this.progressBar.show(progress$, true);
|
||||
|
||||
const subs: Subscription[] = [];
|
||||
|
||||
subs.push(
|
||||
download.pipe(take(1), catchError(() => of(null))).subscribe(data => data && this._downloadingVersion$.next(data.data))
|
||||
)
|
||||
|
||||
return download.pipe(
|
||||
tap({
|
||||
error: err => this.handleDownloadErrors(err),
|
||||
}),
|
||||
finalize(() => subs.forEach(sub => sub.unsubscribe()))
|
||||
);
|
||||
}
|
||||
|
||||
private tryAutoDownload(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
|
||||
return this.handleDownload(
|
||||
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-auto-download", { args: downloadInfo })
|
||||
);
|
||||
}
|
||||
|
||||
private startDownloadBsVersion(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
|
||||
return this.handleDownload(
|
||||
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-download", { args: downloadInfo })
|
||||
);
|
||||
}
|
||||
|
||||
private async doDownloadBsVersion(bsVersion: BSVersion, isVerification: boolean): Promise<BSVersion> {
|
||||
return (async () => {
|
||||
|
||||
const autoDownload = await lastValueFrom(this.tryAutoDownload({ bsVersion, isVerification })).then(() => true).catch(() => false);
|
||||
|
||||
if(autoDownload){
|
||||
return autoDownload;
|
||||
}
|
||||
|
||||
const [completed, stay] = await this.modals.openModal(LoginToMetaModal).then(res => [res.exitCode === ModalExitCode.COMPLETED, res.data]);
|
||||
|
||||
if(!completed){
|
||||
return completed;
|
||||
}
|
||||
|
||||
return lastValueFrom(this.startDownloadBsVersion({ bsVersion, isVerification, stay })).then(() => true);
|
||||
|
||||
})().then(() => bsVersion)
|
||||
.finally(() => this.progressBar.hide(true));
|
||||
}
|
||||
|
||||
public downloadBsVersion(version: BSVersion): Promise<BSVersion> {
|
||||
return this.doDownloadBsVersion(version, false);
|
||||
}
|
||||
|
||||
public async verifyBsVersion(version: BSVersion): Promise<BSVersion> {
|
||||
return this.doDownloadBsVersion(version, true);
|
||||
}
|
||||
|
||||
public stopDownload(): Promise<void>{
|
||||
return lastValueFrom(this.ipc.sendV2<void>("bs-oculus-stop-download"));
|
||||
}
|
||||
|
||||
public hasAuthToken(): Promise<boolean>{
|
||||
return lastValueFrom(this.ipc.sendV2<boolean>("bs-oculus-has-auth-token"));
|
||||
}
|
||||
|
||||
public clearAuthToken(): Promise<void>{
|
||||
return lastValueFrom(this.ipc.sendV2<void>("bs-oculus-clear-auth-token"));
|
||||
}
|
||||
|
||||
}
|
||||
+31
-50
@@ -1,9 +1,7 @@
|
||||
import { DownloadInfo } from "main/services/bs-installer.service";
|
||||
import { DownloadSteamInfo } from "main/services/bs-version-download/bs-steam-downloader.service";
|
||||
import { BehaviorSubject, Observable, ReplaySubject, Subscription, lastValueFrom, throwError } from "rxjs";
|
||||
import { filter, map, share, take, tap, throttleTime } from "rxjs/operators";
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { AuthUserService } from "../auth-user.service";
|
||||
import { BSVersionManagerService } from "../bs-version-manager.service";
|
||||
import { IpcService } from "../ipc.service";
|
||||
import { ModalExitCode, ModalService } from "../modale.service";
|
||||
import { NotificationService } from "../notification.service";
|
||||
@@ -13,22 +11,13 @@ import { SteamGuardModal } from "renderer/components/modal/modal-types/bs-downgr
|
||||
import { LinkOpenerService } from "../link-opener.service";
|
||||
import { DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent, DepotDownloaderWarningEvent } from "../../../shared/models/depot-downloader.model";
|
||||
import { SteamMobileApproveModal } from "renderer/components/modal/modal-types/bs-downgrade/steam-mobile-approve-modal.component";
|
||||
import { DownloaderServiceInterface } from "./bs-store-downloader.interface";
|
||||
import { AbstractBsDownloaderService } from "./abstract-bs-downloader.service";
|
||||
|
||||
export class SteamDownloaderService {
|
||||
export class SteamDownloaderService extends AbstractBsDownloaderService implements DownloaderServiceInterface{
|
||||
|
||||
private static instance: SteamDownloaderService;
|
||||
|
||||
private readonly modalService: ModalService;
|
||||
private readonly ipcService: IpcService;
|
||||
private readonly bsVersionManager: BSVersionManagerService;
|
||||
private readonly authService: AuthUserService;
|
||||
private readonly progressBarService: ProgressBarService;
|
||||
private readonly notificationService: NotificationService;
|
||||
private readonly linkOpener: LinkOpenerService;
|
||||
|
||||
private readonly isVerification$ = new BehaviorSubject(false);
|
||||
public readonly currentBsVersionDownload$ = new BehaviorSubject<BSVersion>(null);
|
||||
public readonly downloadProgress$ = new BehaviorSubject(0);
|
||||
|
||||
public static getInstance(): SteamDownloaderService {
|
||||
if (!SteamDownloaderService.instance) {
|
||||
SteamDownloaderService.instance = new SteamDownloaderService();
|
||||
@@ -36,11 +25,20 @@ export class SteamDownloaderService {
|
||||
return SteamDownloaderService.instance;
|
||||
}
|
||||
|
||||
private readonly modalService: ModalService;
|
||||
private readonly ipcService: IpcService;
|
||||
private readonly progressBarService: ProgressBarService;
|
||||
private readonly notificationService: NotificationService;
|
||||
private readonly linkOpener: LinkOpenerService;
|
||||
|
||||
private readonly STEAM_SESSION_USERNAME_KEY = "STEAM-USERNAME";
|
||||
|
||||
public readonly downloadProgress$ = new BehaviorSubject(0);
|
||||
|
||||
private constructor() {
|
||||
super();
|
||||
this.ipcService = IpcService.getInstance();
|
||||
this.modalService = ModalService.getInstance();
|
||||
this.bsVersionManager = BSVersionManagerService.getInstance();
|
||||
this.authService = AuthUserService.getInstance();
|
||||
this.progressBarService = ProgressBarService.getInstance();
|
||||
this.notificationService = NotificationService.getInstance();
|
||||
this.linkOpener = LinkOpenerService.getInstance();
|
||||
@@ -50,6 +48,11 @@ export class SteamDownloaderService {
|
||||
return lastValueFrom(this.ipcService.sendV2<boolean>("is-dotnet-6-installed"));
|
||||
}
|
||||
|
||||
private setSteamSession(username: string): void { localStorage.setItem(this.STEAM_SESSION_USERNAME_KEY, username); }
|
||||
private getSteamUsername(): string { return localStorage.getItem(this.STEAM_SESSION_USERNAME_KEY); }
|
||||
public deleteSteamSession(): void { localStorage.removeItem(this.STEAM_SESSION_USERNAME_KEY); }
|
||||
public sessionExist(): boolean { return !!localStorage.getItem(this.STEAM_SESSION_USERNAME_KEY); }
|
||||
|
||||
private async showDotNetNotInstalledError(): Promise<void> {
|
||||
const choice = await this.notificationService.notifyError({
|
||||
duration: 11_000,
|
||||
@@ -63,28 +66,14 @@ export class SteamDownloaderService {
|
||||
}
|
||||
}
|
||||
|
||||
public get isDownloading(): boolean {
|
||||
return !!this.currentBsVersionDownload$.value;
|
||||
}
|
||||
|
||||
public async getInstallationFolder(): Promise<string> {
|
||||
return lastValueFrom(this.ipcService.sendV2<string>("bs-download.installation-folder"));
|
||||
}
|
||||
|
||||
public get isVerification(): boolean {
|
||||
return this.isVerification$.value;
|
||||
}
|
||||
|
||||
public setInstallationFolder(path: string): Observable<string> {
|
||||
return this.ipcService.sendV2<string>("bs-download.set-installation-folder", { args: path });
|
||||
}
|
||||
|
||||
public async importVersion(pathToImport: string): Promise<boolean> {
|
||||
return lastValueFrom(this.ipcService.sendV2<void>("bs-download.import-version", { args: pathToImport }))
|
||||
.then(() => true)
|
||||
.catch(() => false);
|
||||
}
|
||||
|
||||
// ### Downloading
|
||||
|
||||
private handleInfoEvents(events$: Observable<DepotDownloaderEvent>): Subscription[] {
|
||||
@@ -97,7 +86,7 @@ export class SteamDownloaderService {
|
||||
).subscribe(startData => {
|
||||
const downloadVersion = JSON.parse(startData) as BSVersion;
|
||||
if(typeof downloadVersion === "object" && downloadVersion?.BSVersion){
|
||||
this.currentBsVersionDownload$.next(downloadVersion);
|
||||
this._downloadingVersion$.next(downloadVersion);
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -135,7 +124,7 @@ export class SteamDownloaderService {
|
||||
filter(event => event.subType === DepotDownloaderInfoEvent.Finished),
|
||||
take(1),
|
||||
).subscribe(() => {
|
||||
if(this.isVerification){
|
||||
if(this.isVerifying){
|
||||
return this.notificationService.notifySuccess({title: "notifications.bs-download.success.titles.verification-finished"});
|
||||
}
|
||||
return this.notificationService.notifySuccess({title: "notifications.bs-download.success.titles.download-success"});
|
||||
@@ -187,7 +176,7 @@ export class SteamDownloaderService {
|
||||
}).pipe(
|
||||
tap({
|
||||
error: (e) => {
|
||||
this.authService.deleteSteamSession();
|
||||
this.deleteSteamSession();
|
||||
!silent && this.hanndleErrorEvent(e)
|
||||
}
|
||||
}),
|
||||
@@ -196,26 +185,26 @@ export class SteamDownloaderService {
|
||||
|
||||
}
|
||||
|
||||
private tryAutoDownloadBsVersion(downloadInfo: DownloadInfo){
|
||||
private tryAutoDownloadBsVersion(downloadInfo: DownloadSteamInfo){
|
||||
|
||||
if(!this.authService.sessionExist()){
|
||||
if(!this.sessionExist()){
|
||||
return throwError(() => new Error("No session"));
|
||||
}
|
||||
|
||||
const infos: DownloadInfo = {...downloadInfo, username: this.authService.getSteamUsername()}
|
||||
const infos: DownloadSteamInfo = {...downloadInfo, username: this.getSteamUsername()}
|
||||
return this.wrapDownload(
|
||||
this.ipcService.sendV2<DepotDownloaderEvent>("auto-download-bs-version", { args: infos }),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
private startDownload(downloadInfo: DownloadInfo){
|
||||
private startDownload(downloadInfo: DownloadSteamInfo){
|
||||
return this.wrapDownload(
|
||||
this.ipcService.sendV2<DepotDownloaderEvent>("download-bs-version", { args: downloadInfo })
|
||||
);
|
||||
}
|
||||
|
||||
private startQrCodeDownload(downloadInfo: DownloadInfo){
|
||||
private startQrCodeDownload(downloadInfo: DownloadSteamInfo){
|
||||
return this.wrapDownload(
|
||||
this.ipcService.sendV2<DepotDownloaderEvent>("download-bs-version-qr", { args: downloadInfo })
|
||||
);
|
||||
@@ -228,7 +217,6 @@ export class SteamDownloaderService {
|
||||
}
|
||||
|
||||
this.progressBarService.show(this.downloadProgress$, true);
|
||||
this.isVerification$.next(isVerification);
|
||||
|
||||
const downloadPromise = (async () => {
|
||||
|
||||
@@ -238,7 +226,7 @@ export class SteamDownloaderService {
|
||||
return Promise.reject(new Error("DotNet not installed"));
|
||||
}
|
||||
|
||||
const downloadInfo: DownloadInfo = {bsVersion, isVerification}
|
||||
const downloadInfo: DownloadSteamInfo = {bsVersion, isVerification}
|
||||
|
||||
const autoDownload = await lastValueFrom(this.tryAutoDownloadBsVersion(downloadInfo)).then(() => true).catch(() => false);
|
||||
|
||||
@@ -255,7 +243,7 @@ export class SteamDownloaderService {
|
||||
}
|
||||
|
||||
if(loginRes.data.stay){
|
||||
this.authService.setSteamSession(loginRes.data.username);
|
||||
this.setSteamSession(loginRes.data.username);
|
||||
}
|
||||
|
||||
const download$ = loginRes.data.method === "qr" ? qrCodeDownload$ : this.startDownload({...downloadInfo, username: loginRes.data.username, password: loginRes.data.password, stay: loginRes.data.stay});
|
||||
@@ -268,10 +256,7 @@ export class SteamDownloaderService {
|
||||
|
||||
return downloadPromise.then(() => {}).finally(() => {
|
||||
this.downloadProgress$.next(0);
|
||||
this.currentBsVersionDownload$.next(null);
|
||||
this.progressBarService.hide(true);
|
||||
this.isVerification$.next(false);
|
||||
this.bsVersionManager.askInstalledVersions();
|
||||
});
|
||||
|
||||
}
|
||||
@@ -284,10 +269,6 @@ export class SteamDownloaderService {
|
||||
return this.doDownloadBsVersion(version).then(() => version);
|
||||
}
|
||||
|
||||
public verifyBsVersionFiles(version: BSVersion): Promise<BSVersion> {
|
||||
return this.doDownloadBsVersion(version, true).then(() => version);
|
||||
}
|
||||
|
||||
public verifyBsVersion(version: BSVersion): Promise<BSVersion> {
|
||||
return this.doDownloadBsVersion(version, true).then(() => version);
|
||||
}
|
||||
@@ -1,19 +1,23 @@
|
||||
import { BSVersion } from "shared/bs-version.interface";
|
||||
import { BehaviorSubject, Observable } from "rxjs";
|
||||
import { BehaviorSubject, Observable, Subscription, lastValueFrom, map, shareReplay, throwError } from "rxjs";
|
||||
import { IpcService } from "./ipc.service";
|
||||
import { ModalExitCode, ModalService } from "./modale.service";
|
||||
import { NotificationService } from "./notification.service";
|
||||
import { ProgressBarService } from "./progress-bar.service";
|
||||
import { EditVersionModal } from "renderer/components/modal/modal-types/edit-version-modal.component";
|
||||
import { popElement } from "shared/helpers/array.helpers";
|
||||
import { ImportVersionModal } from "renderer/components/modal/modal-types/import-version-modal.component";
|
||||
import { Progression } from "main/helpers/fs.helpers";
|
||||
import { ImportVersionOptions } from "main/services/bs-local-version.service";
|
||||
|
||||
export class BSVersionManagerService {
|
||||
private static instance: BSVersionManagerService;
|
||||
|
||||
private readonly ipcService: IpcService;
|
||||
private readonly modalService: ModalService;
|
||||
private readonly notificationService: NotificationService;
|
||||
private readonly progressBarService: ProgressBarService;
|
||||
private readonly notification: NotificationService;
|
||||
private readonly progressBar: ProgressBarService;
|
||||
private readonly modals: ModalService;
|
||||
|
||||
public readonly installedVersions$: BehaviorSubject<BSVersion[]> = new BehaviorSubject([]);
|
||||
public readonly availableVersions$: BehaviorSubject<BSVersion[]> = new BehaviorSubject([]);
|
||||
@@ -21,8 +25,9 @@ export class BSVersionManagerService {
|
||||
private constructor() {
|
||||
this.ipcService = IpcService.getInstance();
|
||||
this.modalService = ModalService.getInstance();
|
||||
this.notificationService = NotificationService.getInstance();
|
||||
this.progressBarService = ProgressBarService.getInstance();
|
||||
this.notification = NotificationService.getInstance();
|
||||
this.progressBar = ProgressBarService.getInstance();
|
||||
this.modals = ModalService.getInstance();
|
||||
this.askAvailableVersions().then(() => this.askInstalledVersions());
|
||||
}
|
||||
|
||||
@@ -70,7 +75,7 @@ export class BSVersionManagerService {
|
||||
}
|
||||
return this.ipcService.send<BSVersion>("bs-version.edit", { args: { version, name: modalRes.data.name, color: modalRes.data.color } }).then(res => {
|
||||
if (!res.success) {
|
||||
this.notificationService.notifyError({
|
||||
this.notification.notifyError({
|
||||
title: `notifications.custom-version.errors.titles.${res.error.title}`,
|
||||
...(res.error.message && { desc: `notifications.custom-version.errors.msg.${res.error.message}` }),
|
||||
});
|
||||
@@ -82,7 +87,7 @@ export class BSVersionManagerService {
|
||||
}
|
||||
|
||||
public async cloneVersion(version: BSVersion): Promise<BSVersion> {
|
||||
if (!this.progressBarService.require()) {
|
||||
if (!this.progressBar.require()) {
|
||||
return null;
|
||||
}
|
||||
const modalRes = await this.modalService.openModal(EditVersionModal, { version, clone: true });
|
||||
@@ -92,22 +97,76 @@ export class BSVersionManagerService {
|
||||
if (modalRes.data.name?.length < 2) {
|
||||
return null;
|
||||
}
|
||||
this.progressBarService.showFake(0.01);
|
||||
this.progressBar.showFake(0.01);
|
||||
return this.ipcService.send<BSVersion>("bs-version.clone", { args: { version, name: modalRes.data.name, color: modalRes.data.color } }).then(res => {
|
||||
this.progressBarService.hide(true);
|
||||
this.progressBar.hide(true);
|
||||
if (!res.success) {
|
||||
this.notificationService.notifyError({
|
||||
this.notification.notifyError({
|
||||
title: `notifications.custom-version.errors.titles.${res.error.title}`,
|
||||
...(res.error.message && { desc: `notifications.custom-version.errors.msg.${res.error.message}` }),
|
||||
});
|
||||
return null;
|
||||
}
|
||||
this.notificationService.notifySuccess({ title: "notifications.custom-version.success.titles.CloningFinished" });
|
||||
this.notification.notifySuccess({ title: "notifications.custom-version.success.titles.CloningFinished" });
|
||||
this.askInstalledVersions();
|
||||
return res.data;
|
||||
});
|
||||
}
|
||||
|
||||
public importVersion(): Observable<Progression<BSVersion>> {
|
||||
if(!this.progressBar.require()){
|
||||
return throwError(() => new Error("Action already in progress"));
|
||||
}
|
||||
|
||||
const obs$ = new Observable<Progression<BSVersion>>(obs => {
|
||||
|
||||
const subs: Subscription[] = [];
|
||||
|
||||
(async () => {
|
||||
|
||||
const resModal = await this.modals.openModal(ImportVersionModal);
|
||||
if(resModal.exitCode !== ModalExitCode.COMPLETED){
|
||||
return;
|
||||
}
|
||||
|
||||
const store = resModal.data;
|
||||
|
||||
const folderRes = await lastValueFrom(this.ipcService.sendV2<{ canceled: boolean; filePaths: string[] }>("choose-folder"));
|
||||
if(!folderRes || folderRes.canceled || !folderRes.filePaths?.length){
|
||||
return;
|
||||
}
|
||||
|
||||
const import$ = this.ipcService.sendV2<Progression<BSVersion>, ImportVersionOptions>("import-version", { args: {fromPath: folderRes.filePaths.at(0), store} });
|
||||
|
||||
subs.push(import$.subscribe(obs));
|
||||
|
||||
await lastValueFrom(import$);
|
||||
|
||||
this.notification.notifySuccess({ title: "notifications.bs-import-version.success.imported.title", duration: 3_000 });
|
||||
|
||||
})().then(() => {
|
||||
obs.complete()
|
||||
}).catch(err => {
|
||||
this.notification.notifyError({ title: "notifications.types.error", desc: "notifications.bs-import-version.errors.import-error.desc" });
|
||||
obs.error(err)
|
||||
}).finally(() => {
|
||||
this.askInstalledVersions();
|
||||
this.progressBar.hide(true);
|
||||
});
|
||||
|
||||
return () => {
|
||||
subs.forEach(sub => sub.unsubscribe());
|
||||
}
|
||||
}).pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: true })
|
||||
);
|
||||
|
||||
// TODO : progression wrong
|
||||
this.progressBar.show(obs$.pipe(map(progress => (progress.current / progress.total) * 100)), true);
|
||||
|
||||
return obs$;
|
||||
}
|
||||
|
||||
public getVersionPath(version: BSVersion): Observable<string> {
|
||||
return this.ipcService.sendV2("get-version-full-path", { args: version });
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface BSVersion extends PartialBSVersion {
|
||||
steam?: boolean;
|
||||
oculus?: boolean;
|
||||
color?: string;
|
||||
downloadedFrom?: BsStore;
|
||||
store?: BsStore;
|
||||
OculusBinaryId?: string;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user