gameflow-deck/src/bun/api/jobs/self-update-job.ts
Simeon Radivoev 38cb752552
feat: Implemented public plugin system accessible from the store.
feat: Implemented external ryujinx integration plugin
refactor: moved sdk types and schemas to own workspace package
fix: Fixed emulator launch with no game
2026-05-10 02:21:01 +03:00

121 lines
No EOL
5.2 KiB
TypeScript

import z from "zod";
import { IJob, JobContext } from "@simeonradivoev/gameflow-sdk";
import { events } from "../app";
import { Downloader } from "@/bun/utils/downloader";
import path from 'node:path';
import os from "node:os";
import winUpdateScript from '@/bun/utils/update-gameflow-windows.bat' with { type: "text" };
import linuxUpdateScript from '@/bun/utils/update-gameflow-linux.sh' with { type: "text" };
import mustache from "mustache";
import pkg from '~/package.json';
import { sleep } from "bun";
export default class SelfUpdateJob implements IJob<never, string>
{
static id = "self-update-job" as const;
static dataSchema = z.never();
group = "self-update";
async downloadUpdate (url: URL, dest: string | undefined, filename: string, ctx: JobContext<IJob<never, string>, never, string>)
{
const downloader = new Downloader('update',
[{
url: url,
file_path: "",
file_name: filename
}],
dest,
{
onProgress (stats)
{
ctx.setProgress(stats.progress, "Downloading Update");
},
});
return downloader.start();
}
async start (context: JobContext<IJob<never, string>, never, string>)
{
context.setProgress(0, "Downloading Update");
await sleep(1000);
const latest = await fetch('https://api.github.com/repos/simeonradivoev/gameflow-deck/releases/latest');
if (latest.ok)
{
const data = await latest.json();
let validAsset: any | undefined;
switch (process.platform)
{
case "win32":
validAsset = data.assets.find((e: any) => new Bun.Glob(`Gameflow-${process.platform}-${process.arch}.zip`).match(e.name));
if (!validAsset)
{
validAsset = data.assets.find((e: any) => new Bun.Glob(`Gameflow-*.zip`).match(e.name));
}
break;
case "linux":
validAsset = data.assets.find((e: any) => new Bun.Glob(`Gameflow-${process.platform}-${process.arch}.AppImage`).match(e.name));
if (!validAsset)
{
validAsset = data.assets.find((e: any) => new Bun.Glob(`*.AppImage`).match(e.name));
}
break;
default:
events.emit('notification', { message: "Unsupported Platfrom", title: 'Failed Update', type: "error" });
return;
}
if (!validAsset)
{
events.emit('notification', { message: "Could not find download", title: 'Failed Update', type: "error" });
return;
}
console.log("Found Download", validAsset.browser_download_url);
console.log("Starting Download");
switch (process.platform)
{
case "linux":
const appimage = process.env.APPIMAGE;
if (!appimage)
{
events.emit('notification', {
message: "Only AppImage supported",
title: 'Failed Update',
type: 'error'
});
return;
}
const linuxDownloads = await this.downloadUpdate(new URL(validAsset.browser_download_url), undefined, path.basename(appimage), context);
if (!linuxDownloads) return;
const shPath = path.join(os.tmpdir(), "update-gameflow.sh");
await Bun.write(shPath, mustache.render(linuxUpdateScript, {
tempFile: linuxDownloads[0],
appImagePath: appimage
}));
context.setProgress(0, "Restarting App To Update");
events.emit('exitapp');
Bun.spawn(["bash", shPath], { detached: true });
process.exit(0);
case "win32":
const winDownloads = await this.downloadUpdate(new URL(validAsset.browser_download_url), undefined, "Gameflow-update.zip", context);
if (!winDownloads) return;
const batPath = path.join(os.tmpdir(), "update-gameflow.bat");
await Bun.write(batPath, mustache.render(winUpdateScript, {
tempFile: winDownloads[0],
installDir: path.dirname(process.execPath),
extractDir: path.join(os.tmpdir(), 'gameflow-update-extract'),
exePath: `${pkg.bin}.exe`
}));
context.setProgress(0, "Restarting App To Update");
events.emit('exitapp');
Bun.spawn(["cmd", "/c", "start", "cmd", "/c", batPath], { detached: true });
process.exit(0);
}
} else
{
events.emit('notification', { message: latest.statusText, title: 'Failed Update', type: "error" });
}
}
}