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