Live video on the AT Protocol
at next 107 lines 2.6 kB view raw
1import { spawn } from "child_process"; 2import { app } from "electron"; 3import { access, constants } from "fs/promises"; 4import os from "os"; 5import { resolve } from "path"; 6import getEnv from "./env"; 7 8const findExe = async (): Promise<string> => { 9 const { isDev } = getEnv(); 10 let fname = "streamplace"; 11 let exe: string; 12 let platform = os.platform() as string; 13 let architecture = os.arch() as string; 14 if (platform === "win32") { 15 platform = "windows"; 16 fname += ".exe"; 17 } 18 if (architecture === "x64") { 19 architecture = "amd64"; 20 } 21 let binfolder = `build-${platform}-${architecture}`; 22 if (isDev) { 23 // theoretically cwd is streamplace/js/desktop: 24 exe = resolve(process.cwd(), "..", "..", binfolder, fname); 25 } else { 26 exe = resolve(process.resourcesPath, fname); 27 } 28 try { 29 await access(exe, constants.F_OK); 30 } catch (e) { 31 throw new Error( 32 `could not find streamplace node binary at ${exe}: ${e.message}`, 33 ); 34 } 35 return exe; 36}; 37 38export default async function makeNode(opts: { 39 env: { [k: string]: string }; 40 autoQuit: boolean; 41}) { 42 const exe = await findExe(); 43 const addr = opts.env.SP_HTTP_ADDR ?? "127.0.0.1:38082"; 44 const internalAddr = opts.env.SP_HTTP_INTERNAL_ADDR ?? "127.0.0.1:39092"; 45 const proc = spawn(exe, [], { 46 stdio: "inherit", 47 env: { 48 ...process.env, 49 SP_HTTP_ADDR: addr, 50 SP_HTTP_INTERNAL_ADDR: internalAddr, 51 ...opts.env, 52 }, 53 windowsHide: true, 54 }); 55 await checkService(`http://${addr}/api/healthz`); 56 57 if (opts.autoQuit) { 58 app.on("before-quit", () => { 59 console.log("before-quit"); 60 proc.kill("SIGTERM"); 61 }); 62 } 63 proc.on("exit", () => { 64 console.log("node exited"); 65 if (opts.autoQuit) { 66 console.log("exiting app"); 67 app.quit(); 68 } 69 }); 70 71 return { 72 proc, 73 addr: `http://${addr}`, 74 internalAddr: `http://${internalAddr}`, 75 }; 76} 77 78const checkService = ( 79 url: string, 80 interval = 300, 81 timeout = 10000, 82): Promise<void> => { 83 let attempts = 0; 84 const maxAttempts = timeout / interval; 85 86 return new Promise((resolve, reject) => { 87 const intervalId = setInterval(async () => { 88 attempts++; 89 90 try { 91 const response = await fetch(url); 92 if (response.ok) { 93 // Response status in the range 200-299 94 clearInterval(intervalId); 95 resolve(); 96 } 97 } catch (error) { 98 // Fetch failed, continue trying 99 } 100 101 if (attempts >= maxAttempts) { 102 clearInterval(intervalId); 103 reject(new Error("streamplace did not boot up in time")); 104 } 105 }, interval); 106 }); 107};