feat: Implemented emulator installation
feat: Updated romm API version feat: Updated es-de rules feat: Added tabs to game details refactor: returned to global query definitions to help with typescript performance
This commit is contained in:
parent
cf6fff6fac
commit
3750e9ed8f
103 changed files with 4888 additions and 1632 deletions
105
src/bun/api/jobs/emulator-download-job.ts
Normal file
105
src/bun/api/jobs/emulator-download-job.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { EmulatorPackageType } from "@/shared/constants";
|
||||
import { getStoreEmulatorPackage } from "../store/services/gamesService";
|
||||
import { IJob, JobContext } from "../task-queue";
|
||||
import z from "zod";
|
||||
import { Glob } from "bun";
|
||||
import { config } from "../app";
|
||||
import path from 'node:path';
|
||||
import { getOrCachedGithubRelease } from "../cache";
|
||||
import _7z from '7zip-min';
|
||||
import fs from "node:fs/promises";
|
||||
import { Downloader } from "@/bun/utils/downloader";
|
||||
import { move } from "fs-extra";
|
||||
|
||||
type EmulatorDownloadStates = "download" | "extract";
|
||||
|
||||
export class EmulatorDownloadJob implements IJob<z.infer<typeof EmulatorDownloadJob.dataSchema>, EmulatorDownloadStates>
|
||||
{
|
||||
static id = "download-emulator" as const;
|
||||
static dataSchema = z.object({ emulator: z.string() });
|
||||
emulator: string;
|
||||
downloadSource: string;
|
||||
emulatorPackage?: EmulatorPackageType;
|
||||
|
||||
constructor(emulator: string, downloadSource: string)
|
||||
{
|
||||
this.emulator = emulator;
|
||||
this.downloadSource = downloadSource;
|
||||
}
|
||||
|
||||
async start (context: JobContext<EmulatorDownloadJob, z.infer<typeof EmulatorDownloadJob.dataSchema>, EmulatorDownloadStates>)
|
||||
{
|
||||
this.emulatorPackage = await getStoreEmulatorPackage(this.emulator);
|
||||
if (!this.emulatorPackage) throw new Error("Emulator not found");
|
||||
if (!this.emulatorPackage.downloads) throw new Error("Emulator has no downloads");
|
||||
|
||||
const validDownloads = this.emulatorPackage.downloads[`${process.platform}:${process.arch}`];
|
||||
if (!validDownloads) throw new Error(`Now downloads in ${this.emulatorPackage.name} for platform ${process.platform}:${process.arch}`);
|
||||
|
||||
const validDownload = validDownloads.find(d => d.type === this.downloadSource);
|
||||
if (!validDownload || !validDownload.path) throw new Error(`Download type ${this.downloadSource} not found`);
|
||||
|
||||
console.log("Trying To Download from ", `https://api.github.com/repos/${validDownload.path}/releases/latest`);
|
||||
const latestRelease = await getOrCachedGithubRelease(validDownload.path);
|
||||
const glob = new Glob(validDownload.pattern);
|
||||
const validAsset = latestRelease.assets.find(a => glob.match(a.name));
|
||||
if (!validAsset) throw new Error("Could Not Find Valid Asset");
|
||||
const downloadUrl = validAsset.browser_download_url;
|
||||
const emulatorsFolder = path.join(config.get('downloadPath'), "emulators", this.emulator);
|
||||
|
||||
const isArchive = validAsset.content_type === 'application/x-7z-compressed' || validAsset.name.endsWith('.7z') || validAsset.content_type === 'application/zip' || validAsset.name.endsWith('.zip');
|
||||
|
||||
const isAppImage = validAsset.name.endsWith(".AppImage");
|
||||
|
||||
if (!isArchive && !isAppImage)
|
||||
{
|
||||
throw new Error("Invalid Download Type");
|
||||
}
|
||||
|
||||
const tmpFolder = path.join(config.get("downloadPath"), ".tmp");
|
||||
const downloader = new Downloader(this.emulator,
|
||||
[{ url: new URL(downloadUrl), file_name: path.basename(downloadUrl), file_path: this.emulator }],
|
||||
tmpFolder,
|
||||
{
|
||||
onProgress (stats)
|
||||
{
|
||||
context.setProgress(stats.progress, 'download');
|
||||
},
|
||||
});
|
||||
|
||||
const destinationPaths = await downloader.start();
|
||||
if (destinationPaths)
|
||||
{
|
||||
if (isArchive)
|
||||
{
|
||||
if (await downloader.start() && destinationPaths[0])
|
||||
{
|
||||
let destinationPath = destinationPaths[0];
|
||||
await _7z.unpack(destinationPath, emulatorsFolder);
|
||||
await fs.rm(destinationPath, { recursive: true });
|
||||
|
||||
// check if 1 root folder we need to get rid of
|
||||
const contents = await fs.readdir(emulatorsFolder);
|
||||
if (contents.length === 1)
|
||||
{
|
||||
const stat = await fs.stat(path.join(emulatorsFolder, contents[0]));
|
||||
if (stat.isDirectory())
|
||||
{
|
||||
console.log("Found 1 root folder, using that instead");
|
||||
const tmpEmulatorsFolder = `${emulatorsFolder} (1)`;
|
||||
await move(path.join(emulatorsFolder, contents[0]), tmpEmulatorsFolder, { overwrite: true });
|
||||
await move(tmpEmulatorsFolder, emulatorsFolder, { overwrite: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exposeData ()
|
||||
{
|
||||
return { emulator: this.emulator };
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue