diff --git a/src/main/helpers/fs.helpers.ts b/src/main/helpers/fs.helpers.ts index abb0324b..ad655be2 100644 --- a/src/main/helpers/fs.helpers.ts +++ b/src/main/helpers/fs.helpers.ts @@ -167,5 +167,6 @@ export function hashFile(filePath: string, algorithm = "sha256"): Promise { total: number; current: number; + diff?: number; data?: T; } diff --git a/src/main/models/oculus-downloader.class.ts b/src/main/models/oculus-downloader.class.ts index dc9f6df5..2fc40e2e 100644 --- a/src/main/models/oculus-downloader.class.ts +++ b/src/main/models/oculus-downloader.class.ts @@ -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 } from "fs-extra"; +import { mkdirs, createWriteStream, pathExists, writeFile, WriteStream } from "fs-extra"; import path from "path"; import { inflate } from "pako" -import { Observable, ReplaySubject, lastValueFrom, share, tap } from "rxjs"; +import { EMPTY, Observable, ReplaySubject, catchError, filter, from, lastValueFrom, map, mergeMap, scan, share, tap } from "rxjs"; import { Progression, hashFile } from "../helpers/fs.helpers"; export class OculusDownloader { @@ -38,25 +38,49 @@ export class OculusDownloader { return manifestFile.async("text").then(JSON.parse).catch(err => CustomError.throw(err, "PARSE_MANIFEST_FILE_FAILED")); } - private async downloadManifestFile(file: OculusManifestFile, destination: string): Promise { + private downloadManifestFile(file: OculusManifestFile, destination: string): Observable> { const downloadSegment = async (segment: OculusManifestFileSegment): Promise => { const segmentUrl = this.getDownloadSegmentUrl(this.options.accessToken, this.options.binaryId, segment[1]); const response = await fetch(segmentUrl); return response.arrayBuffer(); } - - await mkdirs(path.dirname(destination)); - const writeStream = createWriteStream(destination); - - for (const segment of file.segments) { - const arrBuffer = await downloadSegment(segment); - await writeStream.write(inflate(arrBuffer)); - } - - writeStream.close(); - return file; + const totalSegmentSize = file.segments.reduce((acc, segment) => acc + segment[2], 0); + const progress: Progression = { current: 0, total: totalSegmentSize, diff: 0, data: file }; + + return new Observable>(sub => { + + let canceled = false; + let writeStream: WriteStream; + + (async () => { + await mkdirs(path.dirname(destination)); + writeStream = createWriteStream(destination); + + for (const segment of file.segments) { + if(canceled || !writeStream.writable){ return; } + + const arrBuffer = await downloadSegment(segment); + const inflated = inflate(arrBuffer); + await writeStream.write(inflated); + + progress.current += inflated.byteLength; + progress.diff = inflated.byteLength; + + sub.next(progress); + } + + })().catch(err => sub.error(err)).finally(() => { + sub.complete(); + writeStream?.end(); + }); + + return () => { + canceled = true; + writeStream?.end(); + } + }); } private isFileIntegrityValid(file: OculusFileWithName, folder: string): Promise { @@ -114,7 +138,6 @@ export class OculusDownloader { this.isDownloading = true; const progress: Progression = { current: 0, total: 0 }; - const intallPath = path.join("C:", "test", "test"); (async () => { @@ -124,22 +147,34 @@ export class OculusDownloader { progress.total = files.reduce((acc, file) => { return acc + file[1].size }, 0) subscriber.next(progress); - for(const [filename, file] of files){ + const filesDownloadObservable = from(files).pipe( + filter(() => this.isDownloading), + mergeMap(([filename, file]) => ( + from(this.isFileIntegrityValid([filename, file], options.destination)).pipe( map(isValid => ({ filename, file, isValid }))) + )), + filter(({ isValid }) => !isValid), + mergeMap(({ filename, file }) => { + const target = path.join(options.destination, filename); + return this.downloadManifestFile(file, target).pipe( + catchError(err => { + this.options.logger?.error(err); + return EMPTY; + }) + ); + }, 10), + scan((acc, curr) => acc + curr.diff, 0), + ); - if(this.isDownloading === false){ return; } + await lastValueFrom(filesDownloadObservable.pipe(tap({ + next: download => { + progress.current = download; + subscriber.next(progress); + }, + }))); - 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")); - } + await writeFile(path.join(options.destination, "type.info"), "oculus"); - progress.current += file.size; - subscriber.next(progress); - } - - await writeFile(path.join(intallPath, "type.info"), "oculus"); - - const integrity = await lastValueFrom(this.verifyIntegrity(manifest, intallPath)).catch(err => CustomError.throw(err, "VERIFY_INTEGRITY_FAILED")); + const integrity = await lastValueFrom(this.verifyIntegrity(manifest, options.destination)).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,7 +183,7 @@ export class OculusDownloader { })().then(() => subscriber.complete()).catch(err => subscriber.error(err)); return () => { - console.log("C FINI MAIN") + console.log("C FINI"); this.isDownloading = false; } }).pipe(share({connector: () => new ReplaySubject(1)})); diff --git a/src/main/services/bs-oculus-downloader.service.ts b/src/main/services/bs-oculus-downloader.service.ts index d69ef8c1..20164a76 100644 --- a/src/main/services/bs-oculus-downloader.service.ts +++ b/src/main/services/bs-oculus-downloader.service.ts @@ -83,7 +83,7 @@ export class BsOculusDownloaderService { return undefined; } - private async getUserTokenFromMetaAuth(saveToken: boolean): Promise{ + private async getUserTokenFromMetaAuth(keepToken: boolean): Promise{ const redirectUrl = "https://developer.oculus.com/manage/"; const loginUrl = `https://auth.oculus.com/login/?redirect_uri=${encodeURIComponent(redirectUrl)}`; @@ -115,8 +115,8 @@ export class BsOculusDownloaderService { }).finally(() => { clearTimeout(timout); - - if(!saveToken){ + + if(!keepToken){ this.clearTokenCookie(); } @@ -157,7 +157,7 @@ export class BsOculusDownloaderService { 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: "1387243574708751", destination: dest }).subscribe(obs); + sub = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: downloadInfo.version.OculusBinaryId, destination: dest }).subscribe(obs); })().catch(err => obs.error(err)); return () => { @@ -183,7 +183,7 @@ export class BsOculusDownloaderService { const dest = await this.getPathNotAleardyExist(await this.versions.getVersionPath(version)); - sub = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: "1387243574708751", destination: dest }).subscribe(obs); + sub = this.oculusDownloader.downloadApp({ accessToken: token, binaryId: version.OculusBinaryId, destination: dest }).subscribe(obs); })().catch(err => obs.error(err)); return () => { diff --git a/src/renderer/services/bs-downgrade/bs-downloader.service.ts b/src/renderer/services/bs-downgrade/bs-downloader.service.ts index a1cc3f05..ee1a969e 100644 --- a/src/renderer/services/bs-downgrade/bs-downloader.service.ts +++ b/src/renderer/services/bs-downgrade/bs-downloader.service.ts @@ -56,7 +56,7 @@ export class BsDownloaderService { public async downloadVersion(version: BSVersion, from: BsStore): Promise { - if(from === BsStore.STEAM){ + if(from === BsStore.STEAM || !version.OculusBinaryId){ return this.steamDownloader.downloadBsVersion(version); } diff --git a/src/renderer/services/bs-downgrade/oculus-downloader.service.ts b/src/renderer/services/bs-downgrade/oculus-downloader.service.ts index db47c53e..8bda9607 100644 --- a/src/renderer/services/bs-downgrade/oculus-downloader.service.ts +++ b/src/renderer/services/bs-downgrade/oculus-downloader.service.ts @@ -66,17 +66,15 @@ export class OculusDownloaderService { return (async () => { const autoDownload = await lastValueFrom(this.tryAutoDownload(version)).then(() => true).catch(() => false); - - console.log("LAAAA"); if(autoDownload){ return autoDownload; } - const [confirm, stay] = await this.modals.openModal(LoginToMetaModal).then(res => [res.exitCode === ModalExitCode.COMPLETED, res.data]); + const [completed, stay] = await this.modals.openModal(LoginToMetaModal).then(res => [res.exitCode === ModalExitCode.COMPLETED, res.data]); - if(!confirm){ - return confirm; + if(!completed){ + return completed; } return lastValueFrom(this.doDownloadBsVersion({ version, stay })).then(() => true);