this repo has no description
at v1.3.8 6.5 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(overrideBranch?: MoonlightBranch) { 62 const branch = overrideBranch ?? moonlightGlobal.branch; 63 64 // Note: this won't do anything on browser, we should probably disable it 65 // entirely when running in browser. 66 async function downloadStable(): Promise<[ArrayBuffer, string]> { 67 const json = await getStableRelease(); 68 const asset = json.assets.find((a) => a.name === artifactName); 69 if (!asset) throw new Error("Artifact not found"); 70 71 logger.debug(`Downloading ${asset.browser_download_url}`); 72 const req = await fetch(asset.browser_download_url, { 73 cache: "no-store", 74 headers: { 75 "User-Agent": userAgent 76 } 77 }); 78 79 return [await req.arrayBuffer(), json.name]; 80 } 81 82 async function downloadNightly(): Promise<[ArrayBuffer, string]> { 83 logger.debug(`Downloading ${nightlyZipUrl}`); 84 const zipReq = await fetch(nightlyZipUrl, { 85 cache: "no-store", 86 headers: { 87 "User-Agent": userAgent 88 } 89 }); 90 91 const refReq = await fetch(nightlyRefUrl, { 92 cache: "no-store", 93 headers: { 94 "User-Agent": userAgent 95 } 96 }); 97 const ref = (await refReq.text()).split("\n")[0]; 98 99 return [await zipReq.arrayBuffer(), ref]; 100 } 101 102 const [tar, ref] = 103 branch === MoonlightBranch.STABLE 104 ? await downloadStable() 105 : branch === MoonlightBranch.NIGHTLY 106 ? await downloadNightly() 107 : [null, null]; 108 109 if (!tar || !ref) return; 110 111 const dist = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), distDir); 112 if (await moonlightNodeSandboxed.fs.exists(dist)) await moonlightNodeSandboxed.fs.rmdir(dist); 113 await moonlightNodeSandboxed.fs.mkdir(dist); 114 115 logger.debug("Extracting update"); 116 const files = await parseTarGzip(tar); 117 for (const file of files) { 118 if (!file.data) continue; 119 // @ts-expect-error What do you mean their own types are wrong 120 if (file.type !== "file") continue; 121 122 const fullFile = moonlightNodeSandboxed.fs.join(dist, file.name); 123 const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile); 124 if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir); 125 await moonlightNodeSandboxed.fs.writeFile(fullFile, file.data); 126 } 127 128 logger.debug("Writing version file:", ref); 129 const versionFile = moonlightNodeSandboxed.fs.join(moonlightGlobal.getMoonlightDir(), installedVersionFile); 130 await moonlightNodeSandboxed.fs.writeFileString(versionFile, ref.trim()); 131 132 logger.debug("Update extracted"); 133 }, 134 135 async fetchRepositories(repos) { 136 const ret: Record<string, RepositoryManifest[]> = {}; 137 138 for (const repo of repos) { 139 try { 140 const req = await fetch(repo, { 141 cache: "no-store", 142 headers: { 143 "User-Agent": userAgent 144 } 145 }); 146 const json = await req.json(); 147 ret[repo] = json; 148 } catch (e) { 149 logger.error(`Error fetching repository ${repo}`, e); 150 } 151 } 152 153 return ret; 154 }, 155 156 async installExtension(manifest, url, repo) { 157 const req = await fetch(url, { 158 cache: "no-store", 159 headers: { 160 "User-Agent": userAgent 161 } 162 }); 163 164 const dir = moonlightGlobal.getExtensionDir(manifest.id); 165 // remake it in case of updates 166 if (await moonlightNodeSandboxed.fs.exists(dir)) await moonlightNodeSandboxed.fs.rmdir(dir); 167 await moonlightNodeSandboxed.fs.mkdir(dir); 168 169 const buffer = await req.arrayBuffer(); 170 const files = extractAsar(buffer); 171 for (const [file, buf] of Object.entries(files)) { 172 const fullFile = moonlightNodeSandboxed.fs.join(dir, file); 173 const fullDir = moonlightNodeSandboxed.fs.dirname(fullFile); 174 175 if (!(await moonlightNodeSandboxed.fs.exists(fullDir))) await moonlightNodeSandboxed.fs.mkdir(fullDir); 176 await moonlightNodeSandboxed.fs.writeFile(moonlightNodeSandboxed.fs.join(dir, file), buf); 177 } 178 179 await moonlightNodeSandboxed.fs.writeFileString(moonlightNodeSandboxed.fs.join(dir, repoUrlFile), repo); 180 }, 181 182 async deleteExtension(id) { 183 const dir = moonlightGlobal.getExtensionDir(id); 184 await moonlightNodeSandboxed.fs.rmdir(dir); 185 } 186 }; 187}