this repo has no description

Merge pull request #190 from moonlight-mod/notnite/linux-updater

authored by Cynthia Foxwell and committed by GitHub 4034e908 ddc9f585

Changed files
+136 -1
packages
core-extensions
src
nativeFixes
+128
packages/core-extensions/src/nativeFixes/host.ts
··· 1 import { app, nativeTheme } from "electron"; 2 3 const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(","); 4 5 moonlightHost.events.on("window-created", function (browserWindow) { ··· 44 } 45 46 app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].join(","));
··· 1 import { app, nativeTheme } from "electron"; 2 + import * as path from "node:path"; 3 + import * as fs from "node:fs/promises"; 4 + import * as fsSync from "node:fs"; 5 + import { parseTarGzip } from "nanotar"; 6 7 + const logger = moonlightHost.getLogger("nativeFixes/host"); 8 const enabledFeatures = app.commandLine.getSwitchValue("enable-features").split(","); 9 10 moonlightHost.events.on("window-created", function (browserWindow) { ··· 49 } 50 51 app.commandLine.appendSwitch("enable-features", [...new Set(enabledFeatures)].join(",")); 52 + 53 + if (process.platform === "linux" && moonlightHost.getConfigOption<boolean>("nativeFixes", "linuxUpdater")) { 54 + const exePath = app.getPath("exe"); 55 + const appName = path.basename(exePath); 56 + const targetDir = path.dirname(exePath); 57 + const { releaseChannel }: { releaseChannel: string } = JSON.parse( 58 + fsSync.readFileSync(path.join(targetDir, "resources", "build_info.json"), "utf8") 59 + ); 60 + 61 + const updaterModule = require(path.join(moonlightHost.asarPath, "app_bootstrap", "hostUpdater.js")); 62 + const updater = updaterModule.constructor; 63 + 64 + async function doUpdate(cb: (percent: number) => void) { 65 + logger.debug("Extracting to", targetDir); 66 + 67 + const exists = (path: string) => 68 + fs 69 + .stat(path) 70 + .then(() => true) 71 + .catch(() => false); 72 + 73 + const url = `https://discord.com/api/download/${releaseChannel}?platform=linux&format=tar.gz`; 74 + const resp = await fetch(url, { 75 + cache: "no-store" 76 + }); 77 + 78 + const reader = resp.body!.getReader(); 79 + const contentLength = parseInt(resp.headers.get("Content-Length") ?? "0"); 80 + logger.info(`Expecting ${contentLength} bytes for the update`); 81 + const bytes = new Uint8Array(contentLength); 82 + let pos = 0; 83 + let lastPercent = 0; 84 + 85 + while (true) { 86 + const { done, value } = await reader.read(); 87 + if (done) { 88 + break; 89 + } else { 90 + bytes.set(value, pos); 91 + pos += value.length; 92 + 93 + const newPercent = Math.floor((pos / contentLength) * 100); 94 + if (lastPercent !== newPercent) { 95 + lastPercent = newPercent; 96 + cb(newPercent); 97 + } 98 + } 99 + } 100 + 101 + const files = await parseTarGzip(bytes); 102 + 103 + for (const file of files) { 104 + if (!file.data) continue; 105 + // @ts-expect-error What do you mean their own types are wrong 106 + if (file.type !== "file") continue; 107 + 108 + // Discord update files are inside of a main "Discord(PTB|Canary)" folder 109 + const filePath = file.name.replace(`${appName}/`, ""); 110 + logger.info("Extracting", filePath); 111 + 112 + let targetFilePath = path.join(targetDir, filePath); 113 + if (filePath === "resources/app.asar") { 114 + // You tried 115 + targetFilePath = path.join(targetDir, "resources", "_app.asar"); 116 + } else if (filePath === appName) { 117 + // Can't write over the executable? Just move it! 4head 118 + if (await exists(targetFilePath)) { 119 + await fs.rename(targetFilePath, targetFilePath + ".bak"); 120 + await fs.unlink(targetFilePath + ".bak"); 121 + } 122 + } 123 + const targetFileDir = path.dirname(targetFilePath); 124 + 125 + if (!(await exists(targetFileDir))) await fs.mkdir(targetFileDir, { recursive: true }); 126 + await fs.writeFile(targetFilePath, file.data); 127 + 128 + const mode = file.attrs?.mode; 129 + if (mode != null) { 130 + // Not sure why this slice is needed 131 + await fs.chmod(targetFilePath, mode.slice(-3)); 132 + } 133 + } 134 + 135 + logger.debug("Done updating"); 136 + } 137 + 138 + const realEmit = updater.prototype.emit; 139 + updater.prototype.emit = function (event: string, ...args: any[]) { 140 + // Arrow functions don't bind `this` :D 141 + const call = (event: string, ...args: any[]) => realEmit.call(this, event, ...args); 142 + 143 + if (event === "update-manually") { 144 + const latestVerStr: string = args[0]; 145 + logger.debug("update-manually called, intercepting", latestVerStr); 146 + call("update-available"); 147 + 148 + (async () => { 149 + try { 150 + await doUpdate((progress) => { 151 + call("update-progress", progress); 152 + }); 153 + // Copied from the win32 updater 154 + this.updateVersion = latestVerStr; 155 + call( 156 + "update-downloaded", 157 + {}, 158 + releaseChannel, 159 + latestVerStr, 160 + new Date(), 161 + this.updateUrl, 162 + this.quitAndInstall.bind(this) 163 + ); 164 + } catch (e) { 165 + logger.error("Error updating", e); 166 + } 167 + })(); 168 + 169 + return this; 170 + } else { 171 + return realEmit.call(this, event, ...args); 172 + } 173 + }; 174 + }
+8 -1
packages/core-extensions/src/nativeFixes/manifest.json
··· 4 "meta": { 5 "name": "Native Fixes", 6 "tagline": "Various configurable fixes for Discord and Electron", 7 - "authors": ["Cynosphere", "adryd"], 8 "tags": ["fixes"] 9 }, 10 "environment": "desktop", ··· 43 "description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems", 44 "type": "boolean", 45 "default": true 46 } 47 }, 48 "apiLevel": 2
··· 4 "meta": { 5 "name": "Native Fixes", 6 "tagline": "Various configurable fixes for Discord and Electron", 7 + "authors": ["Cynosphere", "adryd", "NotNite"], 8 "tags": ["fixes"] 9 }, 10 "environment": "desktop", ··· 43 "description": "Provides hardware accelerated video encode and decode. Has no effect on other operating systems", 44 "type": "boolean", 45 "default": true 46 + }, 47 + "linuxUpdater": { 48 + "advice": "restart", 49 + "displayName": "Linux Updater", 50 + "description": "Actually implements updating Discord on Linux. Has no effect on other operating systems", 51 + "type": "boolean", 52 + "default": false 53 } 54 }, 55 "apiLevel": 2