[feature-274] handle irrelevant oculus download errors

Ignore errors such as "no token" when auto download or "window closed by user" in the ui
This commit is contained in:
MathieuG-P
2023-10-27 20:11:16 +02:00
parent 0a1e1efcd9
commit 43bf822aee
13 changed files with 120 additions and 43 deletions
+63 -22
View File
@@ -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=="
}
}
},
+1
View File
@@ -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",
+1 -1
View File
@@ -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 {
@@ -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<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");
}
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;
}),
@@ -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";
+2 -1
View File
@@ -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)
})
@@ -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; }
@@ -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<Progression<BSVersion>>): Observable<Progression<BSVersion>> {
private handleDownload(download: Observable<Progression<BSVersion>>, ingoreErrorCodes?: string[]): 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))
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<Progression<BSVersion>>{
const ignoreCode = [MetaAuthErrorCodes.NO_META_AUTH_TOKEN];
return this.handleDownload(
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-auto-download", { args: downloadInfo })
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-auto-download", { args: downloadInfo }),
ignoreCode
);
}
private startDownloadBsVersion(downloadInfo: DownloadInfo): Observable<Progression<BSVersion>>{
const ignoreCode = [MetaAuthErrorCodes.OCULUS_LOGIN_WINDOW_CLOSED_BY_USER];
return this.handleDownload(
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-download", { args: downloadInfo })
this.ipc.sendV2<Progression<BSVersion>>("bs-oculus-download", { args: downloadInfo }),
ignoreCode
);
}
@@ -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";
+2 -1
View File
@@ -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<T>(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);
@@ -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",
}
@@ -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",
}