From 43bf822aee0e8bde2e611761598072eae13f329d Mon Sep 17 00:00:00 2001 From: MathieuG-P <40181755+Zagrios@users.noreply.github.com> Date: Fri, 27 Oct 2023 20:11:16 +0200 Subject: [PATCH] [feature-274] handle irrelevant oculus download errors Ignore errors such as "no token" when auto download or "window closed by user" in the ui --- package-lock.json | 85 ++++++++++++++----- package.json | 1 + src/main/models/depot-downloader.class.ts | 2 +- .../bs-oculus-downloader.service.ts | 10 +-- .../bs-steam-downloader.service.ts | 2 +- src/main/services/ipc.service.ts | 3 +- .../available-versions-list.components.tsx | 6 +- .../oculus-downloader.service.ts | 18 ++-- .../steam-downloader.service.ts | 2 +- src/renderer/services/ipc.service.ts | 3 +- .../depot-downloader.model.ts | 0 .../oculus-download.model.ts | 16 ++++ .../models/exceptions/custom-error.class.ts | 15 +++- 13 files changed, 120 insertions(+), 43 deletions(-) rename src/shared/models/{ => bs-version-download}/depot-downloader.model.ts (100%) create mode 100644 src/shared/models/bs-version-download/oculus-download.model.ts diff --git a/package-lock.json b/package-lock.json index b15db640..20111c59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "rxjs": "^7.8.0", "sanitize-filename": "^1.6.3", "semver": "^7.5.4", + "serialize-error": "^8.1.0", "striptags": "^4.0.0-alpha.4", "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-scoped-groups": "^2.0.0", @@ -9427,6 +9428,35 @@ "node": ">=10.0" } }, + "node_modules/global-agent/node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-agent/node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { "version": "13.18.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", @@ -16423,13 +16453,11 @@ "dev": true }, "node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "optional": true, + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", "dependencies": { - "type-fest": "^0.13.1" + "type-fest": "^0.20.2" }, "engines": { "node": ">=10" @@ -16439,11 +16467,9 @@ } }, "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "optional": true, + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "engines": { "node": ">=10" }, @@ -26365,6 +26391,25 @@ "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" + }, + "dependencies": { + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "optional": true, + "requires": { + "type-fest": "^0.13.1" + } + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "optional": true + } } }, "globals": { @@ -31649,21 +31694,17 @@ } }, "serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "dev": true, - "optional": true, + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", + "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", "requires": { - "type-fest": "^0.13.1" + "type-fest": "^0.20.2" }, "dependencies": { "type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "dev": true, - "optional": true + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" } } }, diff --git a/package.json b/package.json index 520ef7cc..1bba2430 100644 --- a/package.json +++ b/package.json @@ -266,6 +266,7 @@ "rxjs": "^7.8.0", "sanitize-filename": "^1.6.3", "semver": "^7.5.4", + "serialize-error": "^8.1.0", "striptags": "^4.0.0-alpha.4", "tailwind-scrollbar-hide": "^1.1.7", "tailwindcss-scoped-groups": "^2.0.0", diff --git a/src/main/models/depot-downloader.class.ts b/src/main/models/depot-downloader.class.ts index 6ab49a14..dcaa101a 100644 --- a/src/main/models/depot-downloader.class.ts +++ b/src/main/models/depot-downloader.class.ts @@ -1,6 +1,6 @@ import { ChildProcessWithoutNullStreams, SpawnOptionsWithoutStdio, spawn } from "child_process"; import { Observable, ReplaySubject, Subscriber, filter, map, share } from "rxjs"; -import { DepotDownloaderArgsOptions, DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderEventTypes, DepotDownloaderInfoEvent, DepotDownloaderWarningEvent } from "../../shared/models/depot-downloader.model"; +import { DepotDownloaderArgsOptions, DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderEventTypes, DepotDownloaderInfoEvent, DepotDownloaderWarningEvent } from "../../shared/models/bs-version-download/depot-downloader.model"; export class DepotDownloader { diff --git a/src/main/services/bs-version-download/bs-oculus-downloader.service.ts b/src/main/services/bs-version-download/bs-oculus-downloader.service.ts index 02ee5fb0..3d1a6ec8 100644 --- a/src/main/services/bs-version-download/bs-oculus-downloader.service.ts +++ b/src/main/services/bs-version-download/bs-oculus-downloader.service.ts @@ -123,7 +123,7 @@ export class BsOculusDownloaderService { this.clearAuthToken(); } - if(window.isClosable() && !window.isDestroyed()){ + if(!window.isDestroyed() && window.isClosable()){ window.close(); } }); @@ -146,12 +146,6 @@ export class BsOculusDownloaderService { public downloadVersion(downloadInfo: DownloadInfo): Observable>{ 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"); - } - return token; - }), switchMap(token => { if(!downloadInfo.isVerification){ return this.createDownloadVersion(downloadInfo.bsVersion).then(({version, dest}) => ({token, version, dest})) @@ -175,7 +169,7 @@ export class BsOculusDownloaderService { 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"); + throw new CustomError("No Meta auth token was found in cookies for auto download", "NO_META_AUTH_TOKEN"); } return token; }), diff --git a/src/main/services/bs-version-download/bs-steam-downloader.service.ts b/src/main/services/bs-version-download/bs-steam-downloader.service.ts index ea34e5fa..7eac396d 100644 --- a/src/main/services/bs-version-download/bs-steam-downloader.service.ts +++ b/src/main/services/bs-version-download/bs-steam-downloader.service.ts @@ -9,7 +9,7 @@ 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 { DepotDownloaderArgsOptions, DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent } from "../../../shared/models/bs-version-download/depot-downloader.model"; import { DepotDownloader } from "../../models/depot-downloader.class"; import { app } from "electron"; import { BsStore } from "../../../shared/models/bs-store.enum"; diff --git a/src/main/services/ipc.service.ts b/src/main/services/ipc.service.ts index f33f94cb..b3fdd159 100644 --- a/src/main/services/ipc.service.ts +++ b/src/main/services/ipc.service.ts @@ -3,6 +3,7 @@ import { BrowserWindow, ipcMain } from "electron"; import { Observable } from "rxjs"; import { IpcCompleteChannel, IpcErrorChannel, IpcTearDownChannel } from "shared/models/ipc/ipc-response.interface"; import { IpcReplier } from "shared/models/ipc/ipc-request.interface"; +import { serializeError } from 'serialize-error'; import log from "electron-log"; export class IpcService { @@ -47,7 +48,7 @@ export class IpcService { next: data => this.send(channel, window, data), error: error => { log.error(error); - this.send(this.getErrorChannel(channel), window, error); + this.send(this.getErrorChannel(channel), window, serializeError(error)); }, complete: () => this.send(this.getCompleteChannel(channel), window) }) diff --git a/src/renderer/pages/available-versions-list.components.tsx b/src/renderer/pages/available-versions-list.components.tsx index f8884e69..da2833e5 100644 --- a/src/renderer/pages/available-versions-list.components.tsx +++ b/src/renderer/pages/available-versions-list.components.tsx @@ -11,6 +11,7 @@ import { useService } from "renderer/hooks/use-service.hook"; import { BSVersion } from "shared/bs-version.interface"; import { useObservable } from "renderer/hooks/use-observable.hook"; import { BsDownloaderService } from "renderer/services/bs-version-download/bs-downloader.service"; +import { BsStore } from "shared/models/bs-store.enum"; export const AvailableVersionsContext = createContext<{ selectedVersion: BSVersion; setSelectedVersion: (version: BSVersion) => void }>(null); @@ -27,7 +28,10 @@ export function AvailableVersionsList() { const startDownload = async () => { - const store = bsDownloader.getLastStoreDownloadedFrom() ?? await bsDownloader.chooseStoreToDownloadFrom().catch(() => null); + const store = await (async () => { + if(!selectedVersion.OculusBinaryId) { return BsStore.STEAM; } + return bsDownloader.getLastStoreDownloadedFrom() ?? await bsDownloader.chooseStoreToDownloadFrom().catch(() => null); + })(); if(!store){ return; } diff --git a/src/renderer/services/bs-version-download/oculus-downloader.service.ts b/src/renderer/services/bs-version-download/oculus-downloader.service.ts index 5add2117..15fba494 100644 --- a/src/renderer/services/bs-version-download/oculus-downloader.service.ts +++ b/src/renderer/services/bs-version-download/oculus-downloader.service.ts @@ -10,6 +10,7 @@ import { LoginToMetaModal } from "renderer/components/modal/modal-types/bs-downg 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"; +import { MetaAuthErrorCodes } from "shared/models/bs-version-download/oculus-download.model"; export class OculusDownloaderService extends AbstractBsDownloaderService implements DownloaderServiceInterface{ @@ -44,33 +45,40 @@ export class OculusDownloaderService extends AbstractBsDownloaderService impleme this.notifications.notifyError({title: err.code}); } - private handleDownload(download: Observable>): Observable> { + private handleDownload(download: Observable>, ingoreErrorCodes?: string[]): Observable> { 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)) + download.pipe(take(1)).subscribe({ next: data => data && this._downloadingVersion$.next(data.data), error: () => {} }) ) return download.pipe( tap({ - error: err => this.handleDownloadErrors(err), + error: (err: CustomError) => { + if(ingoreErrorCodes?.includes(err.code)){ return; } + this.handleDownloadErrors(err); + }, }), finalize(() => subs.forEach(sub => sub.unsubscribe())) ); } private tryAutoDownload(downloadInfo: DownloadInfo): Observable>{ + const ignoreCode = [MetaAuthErrorCodes.NO_META_AUTH_TOKEN]; return this.handleDownload( - this.ipc.sendV2>("bs-oculus-auto-download", { args: downloadInfo }) + this.ipc.sendV2>("bs-oculus-auto-download", { args: downloadInfo }), + ignoreCode ); } private startDownloadBsVersion(downloadInfo: DownloadInfo): Observable>{ + const ignoreCode = [MetaAuthErrorCodes.OCULUS_LOGIN_WINDOW_CLOSED_BY_USER]; return this.handleDownload( - this.ipc.sendV2>("bs-oculus-download", { args: downloadInfo }) + this.ipc.sendV2>("bs-oculus-download", { args: downloadInfo }), + ignoreCode ); } diff --git a/src/renderer/services/bs-version-download/steam-downloader.service.ts b/src/renderer/services/bs-version-download/steam-downloader.service.ts index cc540d7b..92f18c5b 100644 --- a/src/renderer/services/bs-version-download/steam-downloader.service.ts +++ b/src/renderer/services/bs-version-download/steam-downloader.service.ts @@ -9,7 +9,7 @@ import { ProgressBarService } from "../progress-bar.service"; import { LoginToSteamModal } from "renderer/components/modal/modal-types/bs-downgrade/login-to-steam-modal.component"; import { SteamGuardModal } from "renderer/components/modal/modal-types/bs-downgrade/steam-guard-modal.component"; import { LinkOpenerService } from "../link-opener.service"; -import { DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent, DepotDownloaderWarningEvent } from "../../../shared/models/depot-downloader.model"; +import { DepotDownloaderErrorEvent, DepotDownloaderEvent, DepotDownloaderEventType, DepotDownloaderInfoEvent, DepotDownloaderWarningEvent } from "../../../shared/models/bs-version-download/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"; diff --git a/src/renderer/services/ipc.service.ts b/src/renderer/services/ipc.service.ts index 5938123c..996de6f5 100644 --- a/src/renderer/services/ipc.service.ts +++ b/src/renderer/services/ipc.service.ts @@ -1,6 +1,7 @@ import { defaultIfEmpty, share } from "rxjs/operators"; import { Observable, ReplaySubject, identity } from "rxjs"; import { IpcRequest, IpcResponse } from "shared/models/ipc"; +import { deserializeError } from 'serialize-error'; import { IpcCompleteChannel, IpcErrorChannel, IpcTearDownChannel } from "shared/models/ipc/ipc-response.interface"; export class IpcService { @@ -77,7 +78,7 @@ export class IpcService { const obs = new Observable(observer => { window.electron.ipcRenderer.on(request.responceChannel, (res: T) => observer.next(res)); - window.electron.ipcRenderer.on(errorChannel, (err: Error) => observer.error(err)); + window.electron.ipcRenderer.on(errorChannel, (err) => observer.error(deserializeError(err))); window.electron.ipcRenderer.on(completeChannel, () => observer.complete()); window.electron.ipcRenderer.sendMessage(channel, request); diff --git a/src/shared/models/depot-downloader.model.ts b/src/shared/models/bs-version-download/depot-downloader.model.ts similarity index 100% rename from src/shared/models/depot-downloader.model.ts rename to src/shared/models/bs-version-download/depot-downloader.model.ts diff --git a/src/shared/models/bs-version-download/oculus-download.model.ts b/src/shared/models/bs-version-download/oculus-download.model.ts new file mode 100644 index 00000000..4dca030a --- /dev/null +++ b/src/shared/models/bs-version-download/oculus-download.model.ts @@ -0,0 +1,16 @@ + +export enum OculusDownloaderErrorCodes { + DOWNLOAD_MANIFEST_ZIP_FAILED = "DOWNLOAD_MANIFEST_ZIP_FAILED", + MANIFEST_FILE_NOT_FOUND = "MANIFEST_FILE_NOT_FOUND", + PARSE_MANIFEST_FILE_FAILED = "PARSE_MANIFEST_FILE_FAILED", + ALREADY_DOWNLOADING = "ALREADY_DOWNLOADING", + UNABLE_TO_GET_MANIFEST = "UNABLE_TO_GET_MANIFEST", + VERIFY_INTEGRITY_FAILED = "VERIFY_INTEGRITY_FAILED", + SOME_FILES_FAILED_TO_DOWNLOAD = "SOME_FILES_FAILED_TO_DOWNLOAD", +} + +export enum MetaAuthErrorCodes { + OCULUS_LOGIN_TIMED_OUT = "OCULUS_LOGIN_TIMED_OUT", + OCULUS_LOGIN_WINDOW_CLOSED_BY_USER = "OCULUS_LOGIN_WINDOW_CLOSED_BY_USER", + NO_META_AUTH_TOKEN = "NO_META_AUTH_TOKEN", +} \ No newline at end of file diff --git a/src/shared/models/exceptions/custom-error.class.ts b/src/shared/models/exceptions/custom-error.class.ts index bdc18c8a..7146da23 100644 --- a/src/shared/models/exceptions/custom-error.class.ts +++ b/src/shared/models/exceptions/custom-error.class.ts @@ -12,14 +12,25 @@ export class CustomError extends Error { public get code(): string { return this._code; } public get data(): unknown { return this._data; } - public static fromError(error: Error, code: string, data?: unknown): CustomError{ + public static fromError(error: Error, code?: string, data?: unknown): CustomError{ + + if(error instanceof CustomError){ + return error; + } + + code ||= (error as unknown as {code: string}).code || CustomErrorCodes.UNKNOWN_ERROR; + const customError = new CustomError(error.message ?? error.toString(), code, data); customError.stack = error.stack; return customError; } - public static throw(error: Error, code: string, data?: unknown): never{ + public static throw(error: Error, code?: string, data?: unknown): never{ throw CustomError.fromError(error, code, data); } +} + +export enum CustomErrorCodes { + UNKNOWN_ERROR = "UNKNOWN_ERROR", } \ No newline at end of file