[feature-274] Improve Oculus downloading + add Modal to choose from where to download Beat Saber

This commit is contained in:
MathieuG-P
2023-10-16 23:38:57 +02:00
parent f8b0874f7d
commit 08d1add47b
19 changed files with 342 additions and 76 deletions
+5 -3
View File
@@ -11,7 +11,6 @@
"@electron/fuses": "^1.6.2",
"@node-steam/vdf": "^2.2.0",
"@tippyjs/react": "^4.2.6",
"@types/pako": "^2.0.1",
"archiver": "^6.0.1",
"color": "^4.2.3",
"dateformat": "^5.0.3",
@@ -65,6 +64,7 @@
"@types/jest": "^27.5.2",
"@types/node": "17.0.23",
"@types/node-fetch": "^2.6.3",
"@types/pako": "^2.0.1",
"@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11",
"@types/react-outside-click-handler": "^1.3.1",
@@ -2749,7 +2749,8 @@
"node_modules/@types/pako": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.1.tgz",
"integrity": "sha512-fXhui1fHdLrUR0KEyQsBzqdi3Z+MitnRcpI2eeFJyzaRdqO2miX/BDz2Hh0VdkBbrWprgcQ+ItFmbdKYdbMjvg=="
"integrity": "sha512-fXhui1fHdLrUR0KEyQsBzqdi3Z+MitnRcpI2eeFJyzaRdqO2miX/BDz2Hh0VdkBbrWprgcQ+ItFmbdKYdbMjvg==",
"dev": true
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@@ -21326,7 +21327,8 @@
"@types/pako": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.1.tgz",
"integrity": "sha512-fXhui1fHdLrUR0KEyQsBzqdi3Z+MitnRcpI2eeFJyzaRdqO2miX/BDz2Hh0VdkBbrWprgcQ+ItFmbdKYdbMjvg=="
"integrity": "sha512-fXhui1fHdLrUR0KEyQsBzqdi3Z+MitnRcpI2eeFJyzaRdqO2miX/BDz2Hh0VdkBbrWprgcQ+ItFmbdKYdbMjvg==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
+1 -1
View File
@@ -156,6 +156,7 @@
"@types/jest": "^27.5.2",
"@types/node": "17.0.23",
"@types/node-fetch": "^2.6.3",
"@types/pako": "^2.0.1",
"@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11",
"@types/react-outside-click-handler": "^1.3.1",
@@ -232,7 +233,6 @@
"@electron/fuses": "^1.6.2",
"@node-steam/vdf": "^2.2.0",
"@tippyjs/react": "^4.2.6",
"@types/pako": "^2.0.1",
"archiver": "^6.0.1",
"color": "^4.2.3",
"dateformat": "^5.0.3",
-4
View File
@@ -60,10 +60,6 @@ const createWindow = async (window: AppWindow = "launcher.html") => {
await installExtensions();
}
WindowManagerService.getInstance().openWindow(window);
setTimeout(() => { // TODO : to remove (test)
BsOculusDownloaderService.getInstance().downloadVersion({} as BSVersion).then(console.log).catch(console.error);
}, 3000);
};
const initServicesMustBeInitialized = () => {
+32 -29
View File
@@ -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 } from "fs-extra";
import { mkdirs, createWriteStream, pathExists, writeFile } from "fs-extra";
import path from "path";
import { inflate } from "pako"
import { Observable, lastValueFrom } from "rxjs";
import { Observable, lastValueFrom, tap } from "rxjs";
import { Progression, hashFile } from "../helpers/fs.helpers";
export class OculusDownloader {
@@ -59,33 +59,32 @@ export class OculusDownloader {
return file;
}
private verifyIntegrity(manifest: OculusManifest, folder: string): Observable<Progression<OculusManifestFile[]>> {
return new Observable<Progression<OculusManifestFile[]>>(sub => {
private isFileIntegrityValid(file: OculusFileWithName, folder: string): Promise<boolean> {
const [fileName, fileData] = file;
const destination = path.join(folder, fileName);
return pathExists(destination).then(exists => {
if(!exists){ return false; }
return hashFile(destination, "sha256").then(hash => hash === fileData.sha256);
});
}
private verifyIntegrity(manifest: OculusManifest, folder: string): Observable<Progression<OculusFileWithName[]>> {
return new Observable<Progression<OculusFileWithName[]>>(sub => {
const files = Object.entries(manifest.files);
const progress: Progression<OculusManifestFile[]> = { current: 0, total: files.length, data: [] };
const wrongFiles: OculusManifestFile[] = [];
const progress: Progression<OculusFileWithName[]> = { current: 0, total: files.length, data: [] };
const wrongFiles: OculusFileWithName[] = [];
let canceled = false;
(async () => {
for(const [fileName, file] of files){
for(const oculusFile of files){
if(canceled){ return; }
const destination = path.join(folder, fileName);
if(!pathExists(destination)){
wrongFiles.push(file);
progress.current++;
sub.next(progress);
continue;
}
const fileSha256 = await hashFile(destination, "sha256");
if(fileSha256 !== file.sha256){
wrongFiles.push(file);
if(!(await this.isFileIntegrityValid(oculusFile, folder))){
wrongFiles.push(oculusFile);
}
progress.current++;
@@ -115,6 +114,7 @@ export class OculusDownloader {
this.isDownloading = true;
const progress: Progression = { current: 0, total: 0 };
const intallPath = path.join("C:", "test", "test");
(async () => {
@@ -124,17 +124,22 @@ export class OculusDownloader {
progress.total = files.reduce((acc, file) => { return acc + file[1].size }, 0)
subscriber.next(progress);
for(const file of files){
for(const [filename, file] of files){
if(this.isDownloading === false){ return; }
const destination = path.join("C:", "test", "test", file[0]);
await this.downloadManifestFile(file[1], destination).catch(err => CustomError.throw(err, "DOWNLOAD_FILE_FAILED"));
progress.current += file[1].size;
if(!(await this.isFileIntegrityValid([filename, file], intallPath))){
const target = path.join(intallPath, filename);
await this.downloadManifestFile(file, target).catch(err => CustomError.throw(err, "DOWNLOAD_FILE_FAILED"));
}
progress.current += file.size;
subscriber.next(progress);
}
const integrity = await lastValueFrom(this.verifyIntegrity(manifest, path.join("C:", "test", "test"))).catch(err => CustomError.throw(err, "VERIFY_INTEGRITY_FAILED"));
await writeFile(path.join(intallPath, "type.info"), "oculus");
const integrity = await lastValueFrom(this.verifyIntegrity(manifest, intallPath)).catch(err => CustomError.throw(err, "VERIFY_INTEGRITY_FAILED"));
if(integrity.data.length > 0){
throw new CustomError("Some files failed to download", "SOME_FILES_FAILED_TO_DOWNLOAD", integrity.data);
@@ -148,10 +153,6 @@ export class OculusDownloader {
});
}
public async verifyApp(options: OculusDownloaderOptions){
throw new CustomError("Not implemented", "NOT_IMPLEMENTED");
}
public stopDownload(){
this.isDownloading = false;
}
@@ -192,6 +193,8 @@ interface OculusManifestFile {
type OculusManifestFileSegment = [number, string, number];
type OculusFileWithName = [string, OculusManifestFile];
interface Logger {
info: (...args: unknown[]) => void;
warn: (...args: unknown[]) => void;
@@ -13,9 +13,10 @@ import { DownloadLinkType } from "shared/models/mods";
import sanitize from "sanitize-filename";
import { copyDirectoryWithJunctions, deleteFolder, getFoldersInFolder, pathExist } from "../helpers/fs.helpers";
import { FolderLinkerService } from "./folder-linker.service";
import { ReadStream, createReadStream } from "fs-extra";
import { ReadStream, createReadStream, readFile } from "fs-extra";
import readline from "readline";
import { Observable, Subject } from "rxjs";
import { BsStore } from "../../shared/models/bs-store.enum";
export class BSLocalVersionService {
private static instance: BSLocalVersionService;
@@ -109,6 +110,13 @@ 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 customVersion = this.getCustomVersions().find(customVersion => {
return customVersion.BSVersion === folderVersion.BSVersion && customVersion.name === folderVersion.name;
});
@@ -1,9 +1,12 @@
import { BSVersion } from "../../shared/bs-version.interface";
import { WindowManagerService } from "./window-manager.service";
import { minToMs } from "../../shared/helpers/time.helpers";
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 { Cookie, session } from "electron";
import { pathExist } from "../helpers/fs.helpers";
import { BSLocalVersionService } from "./bs-local-version.service";
export class BsOculusDownloaderService {
@@ -17,12 +20,14 @@ export class BsOculusDownloaderService {
return BsOculusDownloaderService.instance;
}
private readonly windows: WindowManagerService;
private readonly oculusDownloader: OculusDownloader;
private readonly windows: WindowManagerService;
private readonly versions: BSLocalVersionService;
private constructor() {
this.windows = WindowManagerService.getInstance();
this.versions = BSLocalVersionService.getInstance();
this.oculusDownloader = new OculusDownloader();
}
@@ -58,7 +63,27 @@ export class BsOculusDownloaderService {
}
public async getUserToken(): Promise<string>{
private isCookieValid(cookie: Cookie): boolean {
if(!cookie){
return false;
}
return cookie.expirationDate > msToS(Date.now());
}
private async getTokenFromCookie(): 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;
}
return undefined;
}
public async getUserFromMetaAuth(saveToken: boolean): Promise<string>{
const redirectUrl = "https://developer.oculus.com/manage/";
const loginUrl = `https://auth.oculus.com/login/?redirect_uri=${encodeURIComponent(redirectUrl)}`;
const window = await this.windows.openWindow(loginUrl, { frame: true });
@@ -70,7 +95,7 @@ export class BsOculusDownloaderService {
reject(new CustomError("Trying to get Oculus user token timed out", "OCULUS_LOGIN_TIMED_OUT"));
window.close();
}, minToMs(5));
window.webContents.on("did-navigate", async (_, url) => {
if(!url.startsWith(redirectUrl)){ return; }
@@ -87,23 +112,70 @@ export class BsOculusDownloaderService {
reject(new CustomError("Oculus login window closed by user", "OCULUS_LOGIN_WINDOW_CLOSED_BY_USER"));
});
}).finally(() => {
if(!saveToken){
window.webContents.session.clearStorageData();
}
if(!window.isDestroyed() && window.isClosable()){
window.close();
}
clearTimeout(timout);
});
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;
}
public async downloadVersion(version: BSVersion){
const token = await this.getUserToken();
console.log(token);
const s = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: "5074476459318759", destination: "" }).subscribe({
// await this.clearTokenCookie();
const token = await this.getUserFromMetaAuth(true);
const dest = await this.getPathNotAleardyExist(await this.versions.getVersionPath(version));
const s = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: "1387243574708751", destination: dest }).subscribe({
next: a => console.log(a),
error: a => console.error(a),
complete: () => console.log("complete")
});
}
public async autoDownloadVersion(version: BSVersion){
const token = await this.getTokenFromCookie();
if(!token){
throw new CustomError("No token has been found while try to auto download Beat Saber from Oculus", "TOKEN_NEEDED");
}
const dest = await this.getPathNotAleardyExist(await this.versions.getVersionPath(version));
const s = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: "1387243574708751", destination: dest }).subscribe({
next: a => console.log(a),
error: a => console.error(a),
complete: () => console.log("complete")
});
}
public clearTokenCookie(): Promise<void>{
return session.defaultSession.clearStorageData({ storages: ["cookies"], origin: ".oculus.com" })
}
}
@@ -0,0 +1,53 @@
import { ModalComponent, ModalExitCode } from "../../../../services/modale.service";
import { OculusIcon } from "renderer/components/svgs/icons/oculus-icon.component";
import { SteamIcon } from "renderer/components/svgs/icons/steam-icon.component";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { BsStore } from "shared/models/bs-store.enum";
import { useState } from "react";
import tailwindConfig from "../../../../../../tailwind.config";
import Color from "color";
export const ChooseStore: ModalComponent<BsStore> = ({ resolver }) => {
const t = useTranslation();
const [oculusHover, setOculusHover] = useState(false);
const [steamHover, setSteamHover] = useState(false);
const chooseStore = (store: BsStore) => {
resolver({ exitCode: ModalExitCode.COMPLETED, data: store });
}
const isDarkMode = document.documentElement.classList.contains("dark");
const bg = (() => {
if(isDarkMode){
return {
bright: new Color(tailwindConfig.theme.colors["main-color"][1], "hex").hex(),
dim: new Color(tailwindConfig.theme.colors["main-color"][1], "hex").darken(.2).hex()
}
}
return {
bright: new Color(tailwindConfig.theme.colors["main-color"][1], "hex").hex(),
dim: new Color(tailwindConfig.theme.colors["main-color"][1], "hex").darken(.2).hex()
}
})();
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>
<p className="max-w-sm text-gray-800 dark:text-gray-200 text-center">Select the platfrom where you own Beat Saber or from which you want to download it.</p>
<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>
</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"/>
<h2 className="font-bold">Steam</h2>
</div>
</div>
</form>
);
};
@@ -1,6 +1,6 @@
import { BSVersion } from "shared/bs-version.interface";
import { Link, useLocation } from "react-router-dom";
import { BsDownloaderService } from "renderer/services/bs-downloader.service";
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";
@@ -17,7 +17,7 @@ import equal from "fast-deep-equal";
import { useOnUpdate } from "renderer/hooks/use-on-update.hook";
export function BsVersionItem(props: { version: BSVersion }) {
const downloaderService = useService(BsDownloaderService);
const downloaderService = useService(SteamDownloaderService);
const verionManagerService = useService(BSVersionManagerService);
const launcherService = useService(BSLauncherService);
const configService = useService(ConfigurationService);
@@ -11,13 +11,13 @@ 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 { BsDownloaderService } from "renderer/services/bs-downloader.service";
import { SteamDownloaderService } from "renderer/services/bs-downgrade/steam-downloader.service";
import { distinctUntilChanged } from "rxjs";
import equal from "fast-deep-equal";
export function NavBar() {
const versionManager = useService(BSVersionManagerService);
const versionDownloader = useService(BsDownloaderService);
const versionDownloader = useService(SteamDownloaderService);
const downloadingVersion = useObservable(versionDownloader.currentBsVersionDownload$.pipe(distinctUntilChanged(equal)));
const installedVersions = useObservable(versionManager.installedVersions$);
@@ -2,7 +2,7 @@ import { CSSProperties } from "react";
export function OculusIcon(props: { className?: string; style?: CSSProperties }) {
return (
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" height="19" width="32">
<svg className={props.className} style={props.style} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path
fill="currentColor"
d="M24.182 18.599c-0.427 0.297-0.901 0.474-1.411 0.552-0.51 0.083-1.016 0.068-1.521 0.068h-10.5c-0.51 0-1.016 0.021-1.526-0.068-0.51-0.083-0.979-0.255-1.411-0.552-0.854-0.604-1.365-1.563-1.365-2.604 0-1.057 0.516-2.016 1.37-2.599 0.417-0.297 0.896-0.479 1.396-0.557 0.5-0.083 1-0.083 1.526-0.083h10.5c0.5 0 1.016-0.016 1.516 0.063s0.984 0.26 1.401 0.542c0.865 0.578 1.365 1.557 1.365 2.599 0 1.036-0.526 2-1.38 2.599zM28.411 8.526c-1.125-0.906-2.417-1.531-3.818-1.865-0.802-0.193-1.604-0.281-2.432-0.307-0.599-0.021-1.198-0.010-1.818-0.010h-8.661c-0.609 0-1.224-0.010-1.833 0.010-0.823 0.026-1.63 0.109-2.432 0.307-1.401 0.339-2.698 0.964-3.818 1.865-2.281 1.823-3.599 4.568-3.599 7.474 0 2.911 1.318 5.656 3.583 7.474 1.13 0.906 2.422 1.531 3.823 1.87 0.802 0.193 1.609 0.281 2.432 0.302 0.599 0.021 1.198 0.016 1.818 0.016h8.661c0.599 0 1.219 0.005 1.818-0.016 0.823-0.021 1.62-0.109 2.417-0.302 1.401-0.344 2.682-0.969 3.823-1.87 2.307-1.823 3.625-4.568 3.625-7.474s-1.318-5.656-3.589-7.474z"
@@ -1,5 +1,5 @@
import { AvailableVersionsSlider } from "../components/available-versions/available-versions-slider.component";
import { BsDownloaderService } from "../services/bs-downloader.service";
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";
@@ -16,28 +16,52 @@ 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";
export const AvailableVersionsContext = createContext<{ selectedVersion: BSVersion; setSelectedVersion: (version: BSVersion) => void }>(null);
export function AvailableVersionsList() {
const bsDownloader = useService(BsDownloaderService);
const steamDownloader = useService(SteamDownloaderService);
const versionManager = useService(BSVersionManagerService);
const progressBar = useService(ProgressBarService);
const modal = useService(ModalService);
const ipc = useService(IpcService);
const installer = useService(BsDownloaderService);
const notification = useService(NotificationService);
const config = useService(ConfigurationService);
const bsDownloader = useService(BsDownloaderService);
const [selectedVersion, setSelectedVersion] = useState<BSVersion>(null);
const contextValue = useMemo(() => ({ selectedVersion, setSelectedVersion }), [selectedVersion]);
const downloading = useObservable(bsDownloader.currentBsVersionDownload$.pipe(map(v => !!v)));
const downloading = useObservable(steamDownloader.currentBsVersionDownload$.pipe(map(v => !!v)));
const t = useTranslation();
const startDownload = () => {
bsDownloader.downloadBsVersion(selectedVersion)
const downloadFromSteam = (version: BSVersion) => {
return steamDownloader.downloadBsVersion(version)
.catch(() => {})
.finally(() => setSelectedVersion(null));
}
const downloadFromOculus = (version: BSVersion) => {
return undefined; // TODO
}
const startDownload = () => {
return bsDownloader.downloadVersion(selectedVersion);
if(config.get("last-downloaded-from") === "steam"){
return downloadFromSteam(selectedVersion);
}
if(config.get("last-downloaded-from") === "oculus"){
return downloadFromOculus(selectedVersion);
}
};
const importVersion = async () => {
@@ -63,7 +87,7 @@ export function AvailableVersionsList() {
const toImport = folderRes.filePaths.at(0);
const imported = await installer.importVersion(toImport);
const imported = await steamDownloader.importVersion(toImport);
if (imported) {
versionManager.askInstalledVersions();
@@ -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 { BsDownloaderService } from "renderer/services/bs-downloader.service";
import { SteamDownloaderService } from "renderer/services/bs-downgrade/steam-downloader.service";
import { ConfigurationService } from "renderer/services/configuration.service";
import { I18nService } from "renderer/services/i18n.service";
import { IpcService } from "renderer/services/ipc.service";
@@ -43,7 +43,7 @@ export function SettingsPage() {
const themeService = useService(ThemeService);
const ipcService = useService(IpcService);
const modalService = useService(ModalService);
const downloaderService = useService(BsDownloaderService);
const downloaderService = useService(SteamDownloaderService);
const progressBarService = useService(ProgressBarService);
const notificationService = useService(NotificationService);
const i18nService = useService(I18nService);
@@ -8,7 +8,7 @@ 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 { BsDownloaderService } from "renderer/services/bs-downloader.service";
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";
@@ -27,7 +27,7 @@ export function VersionViewer() {
const bsUninstallerService = useService(BSUninstallerService);
const bsVersionManagerService = useService(BSVersionManagerService);
const modalService = useService(ModalService);
const bsDownloaderService = useService(BsDownloaderService);
const bsDownloaderService = useService(SteamDownloaderService);
const ipcService = useService(IpcService);
const bsLauncher = useService(BSLauncherService);
const notification = useService(NotificationService);
@@ -0,0 +1,74 @@
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();
}
public getLastStoreDownloadedFrom(): BsStore | undefined {
const lastStore = this.config.get("lastStoreDownloadedFrom");
if(!lastStore){
return undefined;
}
return lastStore as BsStore;
}
private 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): Promise<BSVersion | undefined> {
const store = this.getLastStoreDownloadedFrom() ?? await this.chooseStoreToDownloadFrom();
if(store === BsStore.STEAM){
return this.steamDownloader.downloadBsVersion(version);
}
if(store === BsStore.OCULUS){
return this.oculusDownloader.downloadBsVersion(version);
}
return undefined
}
public importVersion(): void {
// TODO : open Modal
// Will replace method in SteamDownloaderService
}
}
@@ -0,0 +1,21 @@
import { BSVersion } from "shared/bs-version.interface";
export class OculusDownloaderService {
private static instance: OculusDownloaderService;
public static getInstance(): OculusDownloaderService {
if (!OculusDownloaderService.instance) {
OculusDownloaderService.instance = new OculusDownloaderService();
}
return OculusDownloaderService.instance;
}
private constructor(){}
public async downloadBsVersion(version: BSVersion): Promise<BSVersion> {
return version;
}
}
@@ -2,20 +2,20 @@ import { DownloadInfo } from "main/services/bs-installer.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";
import { ProgressBarService } from "./progress-bar.service";
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";
import { ProgressBarService } from "../progress-bar.service";
import { LoginModal } from "renderer/components/modal/modal-types/login-modal.component";
import { GuardModal } from "renderer/components/modal/modal-types/guard-modal.component";
import { LinkOpenerService } from "./link-opener.service";
import { DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent, DepotDownloaderWarningEvent } from "../../shared/models/depot-downloader.model";
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/steam-mobile-approve-modal.component";
export class BsDownloaderService {
private static instance: BsDownloaderService;
export class SteamDownloaderService {
private static instance: SteamDownloaderService;
private readonly modalService: ModalService;
private readonly ipcService: IpcService;
@@ -29,11 +29,11 @@ export class BsDownloaderService {
public readonly currentBsVersionDownload$ = new BehaviorSubject<BSVersion>(null);
public readonly downloadProgress$ = new BehaviorSubject(0);
public static getInstance(): BsDownloaderService {
if (!BsDownloaderService.instance) {
BsDownloaderService.instance = new BsDownloaderService();
public static getInstance(): SteamDownloaderService {
if (!SteamDownloaderService.instance) {
SteamDownloaderService.instance = new SteamDownloaderService();
}
return BsDownloaderService.instance;
return SteamDownloaderService.instance;
}
private constructor() {
+5
View File
@@ -1,3 +1,5 @@
import { BsStore } from "./models/bs-store.enum";
export interface PartialBSVersion {
BSVersion: string,
name?: string
@@ -13,4 +15,7 @@ export interface BSVersion extends PartialBSVersion {
steam?: boolean;
oculus?: boolean;
color?: string;
downloadedFrom?: BsStore;
}
+4
View File
@@ -16,3 +16,7 @@ export function hourToMin(hours: number): number {
export function hourToS(hours: number): number {
return hours * minToS(MINUTES_IN_HOUR);
}
export function msToS(milliseconds: number): number {
return milliseconds / 1000;
}
+4
View File
@@ -0,0 +1,4 @@
export enum BsStore {
STEAM = "steam",
OCULUS = "oculus",
}