Live video on the AT Protocol
at natb/update-docs-rtmp 105 lines 2.6 kB view raw
1import os from "os"; 2import { resolve } from "path"; 3import { access, constants } from "fs/promises"; 4import { spawn } from "child_process"; 5import getEnv from "./env"; 6import { app } from "electron"; 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 proc.kill("SIGTERM"); 60 }); 61 } 62 proc.on("exit", () => { 63 console.log("node exited"); 64 if (opts.autoQuit) { 65 app.quit(); 66 } 67 }); 68 69 return { 70 proc, 71 addr: `http://${addr}`, 72 internalAddr: `http://${internalAddr}`, 73 }; 74} 75 76const checkService = ( 77 url: string, 78 interval = 300, 79 timeout = 10000, 80): Promise<void> => { 81 let attempts = 0; 82 const maxAttempts = timeout / interval; 83 84 return new Promise((resolve, reject) => { 85 const intervalId = setInterval(async () => { 86 attempts++; 87 88 try { 89 const response = await fetch(url); 90 if (response.ok) { 91 // Response status in the range 200-299 92 clearInterval(intervalId); 93 resolve(); 94 } 95 } catch (error) { 96 // Fetch failed, continue trying 97 } 98 99 if (attempts >= maxAttempts) { 100 clearInterval(intervalId); 101 reject(new Error("streamplace did not boot up in time")); 102 } 103 }, interval); 104 }); 105};