this repo has no description
at v1.3.0 6.4 kB view raw
1import { MoonlightBranch } from "@moonlight-mod/types"; 2import type { MoonbaseNatives, RepositoryManifest } from "./types"; 3import extractAsar from "@moonlight-mod/core/asar"; 4import { distDir, repoUrlFile, installedVersionFile } from "@moonlight-mod/types/constants"; 5import { parseTarGzip } from "nanotar"; 6 7const moonlightGlobal = globalThis.moonlightHost ?? globalThis.moonlightNode; 8 9const githubRepo = "moonlight-mod/moonlight"; 10const githubApiUrl = `https://api.github.com/repos/${githubRepo}/releases/latest`; 11const artifactName = "dist.tar.gz"; 12 13const nightlyRefUrl = "https://moonlight-mod.github.io/moonlight/ref"; 14const nightlyZipUrl = "https://moonlight-mod.github.io/moonlight/dist.tar.gz"; 15 16export const userAgent = `moonlight/${moonlightGlobal.version} (https://github.com/moonlight-mod/moonlight)`; 17 18async function getStableRelease(): Promise<{ 19 name: string; 20 assets: { 21 name: string; 22 browser_download_url: string; 23 }[]; 24}> { 25 const req = await fetch(githubApiUrl, { 26 cache: "no-store", 27 headers: { 28 "User-Agent": userAgent 29 } 30 }); 31 return await req.json(); 32} 33 34export default function getNatives(): MoonbaseNatives { 35 const logger = moonlightGlobal.getLogger("moonbase/natives"); 36 37 return { 38 async checkForMoonlightUpdate() { 39 try { 40 if (moonlightGlobal.branch === MoonlightBranch.STABLE) { 41 const json = await getStableRelease(); 42 return json.name !== moonlightGlobal.version ? json.name : null; 43 } else if (moonlightGlobal.branch === MoonlightBranch.NIGHTLY) { 44 const req = await fetch(nightlyRefUrl, { 45 cache: "no-store", 46 headers: { 47 "User-Agent": userAgent 48 } 49 }); 50 const ref = (await req.text()).split("\n")[0]; 51 return ref !== moonlightGlobal.version ? ref : null; 52 } 53 54 return null; 55 } catch (e) { 56 logger.error("Error checking for moonlight update", e); 57 return null; 58 } 59 }, 60 61 async updateMoonlight() { 62 // Note: this won't do anything on browser, we should probably disable it 63 // entirely when running in browser. 64 async function downloadStable(): Promise<[ArrayBuffer, string]> { 65 const json = await getStableRelease(); 66 const asset = json.assets.find((a) => a.name === artifactName); 67 if (!asset) throw new Error("Artifact not found"); 68 69 logger.debug(`Downloading ${asset.browser_download_url}`); 70 const req = await fetch(asset.browser_download_url, { 71 cache: "no-store", 72 headers: { 73 "User-Agent": userAgent 74 } 75 }); 76 77 return [await req.arrayBuffer(), json.name]; 78 } 79 80 async function downloadNightly(): Promise<[ArrayBuffer, string]> { 81 logger.debug(`Downloading ${nightlyZipUrl}`); 82 const zipReq = await fetch(nightlyZipUrl, { 83 cache: "no-store", 84 headers: { 85 "User-Agent": userAgent 86 } 87 }); 88 89 const refReq = await fetch(nightlyRefUrl, { 90 cache: "no-store", 91 headers: { 92 "User-Agent": userAgent 93 } 94 }); 95 const ref = (await refReq.text()).split("\n")[0]; 96 97 return [await zipReq.arrayBuffer(), ref]; 98 } 99 100 const [tar, ref] = 101 moonlightGlobal.branch === MoonlightBranch.STABLE 102 ? await downloadStable() 103 : moonlightGlobal.branch === MoonlightBranch.NIGHTLY 104 ? await downloadNightly() 105 : [null, null]; 106 107 if (!tar || !ref) return; 108 109 const dist = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), distDir); 110 if (await moonlightNodeSandboxed.fs.exists(dist)) await moonlightNodeSandboxed.fs.rmdir(dist); 111 await moonlightNodeSandboxed.fs.mkdir(dist); 112 113 logger.debug("Extracting update"); 114 const files = await parseTarGzip(tar); 115 for (const file of files) { 116 if (!file.data) continue; 117 // @ts-expect-error What do you mean their own types are wrong 118 if (file.type !== "file") continue; 119 120 const fullFile = moonlightNodeSandboxed.fs.join(dist, file.name); 121 const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile); 122 if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir); 123 await moonlightNodeSandboxed.fs.writeFile(fullFile, file.data); 124 } 125 126 logger.debug("Writing version file:", ref); 127 const versionFile = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), installedVersionFile); 128 await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim()); 129 130 logger.debug("Update extracted"); 131 }, 132 133 async fetchRepositories(repos) { 134 const ret: Record<string, RepositoryManifest[]> = {}; 135 136 for (const repo of repos) { 137 try { 138 const req = await fetch(repo, { 139 cache: "no-store", 140 headers: { 141 "User-Agent": userAgent 142 } 143 }); 144 const json = await req.json(); 145 ret[repo] = json; 146 } catch (e) { 147 logger.error(`Error fetching repository ${repo}`, e); 148 } 149 } 150 151 return ret; 152 }, 153 154 async installExtension(manifest, url, repo) { 155 const req = await fetch(url, { 156 cache: "no-store", 157 headers: { 158 "User-Agent": userAgent 159 } 160 }); 161 162 const dir = moonlightGlobal.getExtensionDir(manifest.id); 163 // remake it in case of updates 164 if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir); 165 await moonlightNodeSandboxed.fs.mkdir(dir); 166 167 const buffer = await req.arrayBuffer(); 168 const files = extractAsar(buffer); 169 for (const [file, buf] of Object.entries(files)) { 170 const fullFile = moonlightNodeSandboxed.fs.join(dir, file); 171 const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile); 172 173 if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir); 174 await moonlightNodeSandboxed.fs.writeFile(moonlightNodeSandboxed.fs.join(dir, file), buf); 175 } 176 177 await moonlightNodeSandboxed.fs.writeFileString(moonlightNodeSandboxed.fs.join(dir, repoUrlFile), repo); 178 }, 179 180 async deleteExtension(id) { 181 const dir = moonlightGlobal.getExtensionDir(id); 182 await moonlightNodeSandboxed.fs.rmdir(dir); 183 } 184 }; 185}