From 2ae0aa9a929ee34e154ee4ad4c357ec1a82637b5 Mon Sep 17 00:00:00 2001 From: webadderall <131426131+webadderall@users.noreply.github.com> Date: Fri, 17 Apr 2026 20:35:08 +1000 Subject: [PATCH] fix: address CodeRabbit review feedback - Remove dead helperExists local in recording/windows.ts - Hoist fs/promises import out of close handler in recording/ffmpeg.ts - Guard fs.readdir with mkdir in recording/prune.ts (ENOENT resilience) - Derive companion audio suffixes from COMPANION_AUDIO_LAYOUTS in prune.ts - Guard mousemove hook registration to Linux only in cursor/interaction.ts - Replace dynamic require('electron') with static import in cursor/monitor.ts - Wrap nodeRequire in try/catch in ffmpeg/binary.ts for fallback safety - Fix hardcoded timeOffsetMs: 0 in project/session.ts (use normalizer) - Fix isPathInsideDirectory to normalize candidatePath in project/manager.ts - Fix isAllowedLocalReadPath security: require path to be in allowlist (AND not OR) - Derive extension regex from constants in project/manager.ts - Consolidate duplicate Duration parsers in recording/diagnostics.ts - Refactor ensureReadableFile to use options object instead of description string - Make swiftc compilation async (execFile) in paths/binaries.ts - Add socket timeout to httpsGet in captions/whisper.ts --- electron/ipc/captions/generate.ts | 10 ++++----- electron/ipc/captions/whisper.ts | 5 ++++- electron/ipc/cursor/interaction.ts | 12 ++++++++--- electron/ipc/cursor/monitor.ts | 2 +- electron/ipc/ffmpeg/binary.ts | 16 ++++++++------ electron/ipc/paths/binaries.ts | 20 +++++++++++------- electron/ipc/project/manager.ts | 16 ++++++++------ electron/ipc/project/session.ts | 2 +- electron/ipc/recording/diagnostics.ts | 10 +++------ electron/ipc/recording/ffmpeg.ts | 2 +- electron/ipc/recording/prune.ts | 16 +++++++------- electron/ipc/recording/windows.ts | 9 +++----- .../recordly-native-cursor-monitor | Bin 95840 -> 95840 bytes .../recordly-screencapturekit-helper | Bin 189936 -> 189936 bytes .../bin/darwin-arm64/recordly-system-cursors | Bin 99752 -> 99752 bytes .../bin/darwin-arm64/recordly-window-list | Bin 116024 -> 116024 bytes .../darwin-x64/recordly-native-cursor-monitor | Bin 70568 -> 70568 bytes .../recordly-screencapturekit-helper | Bin 180456 -> 180456 bytes .../bin/darwin-x64/recordly-system-cursors | Bin 70360 -> 70360 bytes .../bin/darwin-x64/recordly-window-list | Bin 82424 -> 82424 bytes 20 files changed, 67 insertions(+), 53 deletions(-) diff --git a/electron/ipc/captions/generate.ts b/electron/ipc/captions/generate.ts index f74abf15..33c34584 100644 --- a/electron/ipc/captions/generate.ts +++ b/electron/ipc/captions/generate.ts @@ -12,9 +12,9 @@ import { resolveRecordingSession } from "../project/session"; const execFileAsync = promisify(execFile); -export async function ensureReadableFile(filePath: string, description: string) { +export async function ensureReadableFile(filePath: string, options?: { executable?: boolean }) { await fs.access(filePath, fsConstants.R_OK); - if (description === "whisper executable") { + if (options?.executable) { try { await fs.access(filePath, fsConstants.X_OK); } catch { @@ -113,7 +113,7 @@ export async function extractCaptionAudioSource(options: { for (const candidate of candidates) { try { - await ensureReadableFile(candidate.path, "video file"); + await ensureReadableFile(candidate.path); await execFileAsync( options.ffmpegPath, [ @@ -169,8 +169,8 @@ export async function generateAutoCaptionsFromVideo(options: { const whisperExecutablePath = await resolveWhisperExecutablePath(options.whisperExecutablePath); const whisperModelPath = path.resolve(options.whisperModelPath); - await ensureReadableFile(whisperExecutablePath, "whisper executable"); - await ensureReadableFile(whisperModelPath, "whisper model"); + await ensureReadableFile(whisperExecutablePath, { executable: true }); + await ensureReadableFile(whisperModelPath); const tempBase = path.join( app.getPath("temp"), diff --git a/electron/ipc/captions/whisper.ts b/electron/ipc/captions/whisper.ts index 70a97ede..c8e774c6 100644 --- a/electron/ipc/captions/whisper.ts +++ b/electron/ipc/captions/whisper.ts @@ -41,7 +41,7 @@ export function downloadFileWithProgress( ): Promise { const request = (currentUrl: string, redirectCount = 0): Promise => { return new Promise((resolve, reject) => { - const req = httpsGet(currentUrl, (response) => { + const req = httpsGet(currentUrl, { timeout: 30_000 }, (response) => { const statusCode = response.statusCode ?? 0; const location = response.headers.location; @@ -97,6 +97,9 @@ export function downloadFileWithProgress( }); req.on("error", reject); + req.on("timeout", () => { + req.destroy(new Error("Whisper model download timed out.")); + }); }); }; diff --git a/electron/ipc/cursor/interaction.ts b/electron/ipc/cursor/interaction.ts index 81b8bbdb..f5ae15ca 100644 --- a/electron/ipc/cursor/interaction.ts +++ b/electron/ipc/cursor/interaction.ts @@ -183,7 +183,9 @@ export async function startInteractionCapture() { hook.on("mousedown", onMouseDown); hook.on("mouseup", onMouseUp); - hook.on("mousemove", onMouseMove); + if (process.platform === "linux") { + hook.on("mousemove", onMouseMove); + } hook.start(); @@ -192,11 +194,15 @@ export async function startInteractionCapture() { if (typeof hook.off === "function") { hook.off("mousedown", onMouseDown); hook.off("mouseup", onMouseUp); - hook.off("mousemove", onMouseMove); + if (process.platform === "linux") { + hook.off("mousemove", onMouseMove); + } } else if (typeof hook.removeListener === "function") { hook.removeListener("mousedown", onMouseDown); hook.removeListener("mouseup", onMouseUp); - hook.removeListener("mousemove", onMouseMove); + if (process.platform === "linux") { + hook.removeListener("mousemove", onMouseMove); + } } } catch { // ignore listener cleanup errors diff --git a/electron/ipc/cursor/monitor.ts b/electron/ipc/cursor/monitor.ts index 4943e523..0c39909a 100644 --- a/electron/ipc/cursor/monitor.ts +++ b/electron/ipc/cursor/monitor.ts @@ -1,6 +1,7 @@ import { spawn } from "node:child_process"; import { constants as fsConstants } from "node:fs"; import fs from "node:fs/promises"; +import { BrowserWindow } from "electron"; import type { CursorVisualType } from "../types"; import { currentCursorVisualType, @@ -13,7 +14,6 @@ import { import { getCursorMonitorExePath, ensureNativeCursorMonitorBinary } from "../paths/binaries"; export function emitCursorStateChanged(cursorType: CursorVisualType) { - const { BrowserWindow } = require("electron") as typeof import("electron"); BrowserWindow.getAllWindows().forEach((window) => { if (!window.isDestroyed()) { window.webContents.send("cursor-state-changed", { cursorType }); diff --git a/electron/ipc/ffmpeg/binary.ts b/electron/ipc/ffmpeg/binary.ts index 095db5a8..c76acd40 100644 --- a/electron/ipc/ffmpeg/binary.ts +++ b/electron/ipc/ffmpeg/binary.ts @@ -6,13 +6,17 @@ import { app } from "electron"; const nodeRequire = createRequire(import.meta.url); export function loadFfmpegStatic(): string | null { - const moduleExports = nodeRequire("ffmpeg-static"); - if (typeof moduleExports === "string") { - return moduleExports; - } + try { + const moduleExports = nodeRequire("ffmpeg-static"); + if (typeof moduleExports === "string") { + return moduleExports; + } - if (typeof moduleExports?.default === "string") { - return moduleExports.default as string; + if (typeof moduleExports?.default === "string") { + return moduleExports.default as string; + } + } catch { + // ffmpeg-static not available; fall through to system FFmpeg } return null; diff --git a/electron/ipc/paths/binaries.ts b/electron/ipc/paths/binaries.ts index c43cb97c..45ec80da 100644 --- a/electron/ipc/paths/binaries.ts +++ b/electron/ipc/paths/binaries.ts @@ -1,13 +1,16 @@ -import { spawnSync } from "node:child_process"; +import { execFile } from "node:child_process"; import { existsSync, constants as fsConstants } from "node:fs"; import fs from "node:fs/promises"; import path from "node:path"; +import { promisify } from "node:util"; import { app } from "electron"; import { nativeHelperMigrationPromise, setNativeHelperMigrationPromise, } from "../state"; +const execFileAsync = promisify(execFile); + /** * Resolve a path within the app bundle, handling asar unpacking in production. * Files listed in asarUnpack are extracted to app.asar.unpacked/ and must be @@ -205,13 +208,14 @@ export async function ensureSwiftHelperBinary( return binaryPath; } - const result = spawnSync("swiftc", ["-O", sourcePath, "-o", binaryPath], { - encoding: "utf8", - timeout: 120000, - }); - - if (result.status !== 0) { - const details = [result.stderr, result.stdout].filter(Boolean).join("\n").trim(); + try { + await execFileAsync("swiftc", ["-O", sourcePath, "-o", binaryPath], { + encoding: "utf8", + timeout: 120000, + }); + } catch (error) { + const err = error as NodeJS.ErrnoException & { stdout?: string; stderr?: string }; + const details = [err.stderr, err.stdout].filter(Boolean).join("\n").trim(); throw new Error(details || `Failed to compile ${label}`); } diff --git a/electron/ipc/project/manager.ts b/electron/ipc/project/manager.ts index b3d09607..2db9e774 100644 --- a/electron/ipc/project/manager.ts +++ b/electron/ipc/project/manager.ts @@ -41,10 +41,11 @@ export function getAssetRootPath() { } export function isPathInsideDirectory(candidatePath: string, directoryPath: string) { + const normalizedCandidatePath = normalizePath(candidatePath); const normalizedDirectoryPath = normalizePath(directoryPath); return ( - candidatePath === normalizedDirectoryPath || - candidatePath.startsWith(`${normalizedDirectoryPath}${path.sep}`) + normalizedCandidatePath === normalizedDirectoryPath || + normalizedCandidatePath.startsWith(`${normalizedDirectoryPath}${path.sep}`) ); } @@ -52,9 +53,9 @@ export function isAllowedLocalReadPath(candidatePath: string) { const allowedPrefixes = [RECORDINGS_DIR, USER_DATA_PATH, getAssetRootPath(), app.getPath("temp")]; return ( - existsSync(candidatePath) || - allowedPrefixes.some((prefix) => isPathInsideDirectory(candidatePath, prefix)) || - approvedLocalReadPaths.has(candidatePath) + existsSync(candidatePath) && + (allowedPrefixes.some((prefix) => isPathInsideDirectory(candidatePath, prefix)) || + approvedLocalReadPaths.has(candidatePath)) ); } @@ -238,7 +239,10 @@ export async function buildProjectLibraryEntry( return { path: normalizedPath, - name: path.basename(normalizedPath).replace(/\.(recordly|openscreen)$/i, ""), + name: path.basename(normalizedPath).replace( + new RegExp(`\\.(${[PROJECT_FILE_EXTENSION, ...LEGACY_PROJECT_FILE_EXTENSIONS].join("|")})$`, "i"), + "", + ), updatedAt: stats.mtimeMs, thumbnailPath: thumbnailExists ? thumbnailPath : null, isCurrent: Boolean( diff --git a/electron/ipc/project/session.ts b/electron/ipc/project/session.ts index 429e7607..995f7193 100644 --- a/electron/ipc/project/session.ts +++ b/electron/ipc/project/session.ts @@ -65,7 +65,7 @@ export async function resolveRecordingSessionManifest( return { videoPath: normalizedVideoPath, webcamPath: null, - timeOffsetMs: 0, + timeOffsetMs: normalizeRecordingTimeOffsetMs(parsed.timeOffsetMs), }; } diff --git a/electron/ipc/recording/diagnostics.ts b/electron/ipc/recording/diagnostics.ts index 07bdfe13..9ce89a89 100644 --- a/electron/ipc/recording/diagnostics.ts +++ b/electron/ipc/recording/diagnostics.ts @@ -55,13 +55,9 @@ export async function probeMediaDurationSeconds(filePath: string): Promise Boolean(value)) @@ -74,14 +76,12 @@ export async function pruneAutoRecordings(exemptPaths: string[] = []) { await fs.rm(getTelemetryPathForVideo(entry.filePath), { force: true }); // Clean up companion audio files left from recording (macOS .m4a, Windows .wav) const base = entry.filePath.replace(/\.(mp4|mov|webm)$/i, ""); - for (const suffix of [ - ".system.m4a", - ".mic.m4a", - ".system.wav", - ".mic.wav", - ".mic.webm", - ".system.webm", - ]) { + const companionSuffixes = Array.from( + new Set( + COMPANION_AUDIO_LAYOUTS.flatMap((layout) => [layout.systemSuffix, layout.micSuffix]), + ), + ); + for (const suffix of companionSuffixes) { await fs.rm(base + suffix, { force: true }).catch(() => undefined); } } catch (error) { diff --git a/electron/ipc/recording/windows.ts b/electron/ipc/recording/windows.ts index 76355b66..32c39cb4 100644 --- a/electron/ipc/recording/windows.ts +++ b/electron/ipc/recording/windows.ts @@ -32,21 +32,18 @@ const execFileAsync = promisify(execFile); export async function isNativeWindowsCaptureAvailable(): Promise { if (process.platform !== "win32") return false; - const helperPath = getWindowsCaptureExePath(); const os = await import("node:os"); const [major, , build] = os.release().split(".").map(Number); const supported = major >= 10 && build >= 19041; - let helperExists = false; + if (!supported) return false; try { - await fs.access(helperPath, fsConstants.X_OK); - helperExists = true; + await fs.access(getWindowsCaptureExePath(), fsConstants.X_OK); } catch { return false; } - void helperExists; - return supported; + return true; } export function waitForWindowsCaptureStart(proc: ChildProcessWithoutNullStreams) { diff --git a/electron/native/bin/darwin-arm64/recordly-native-cursor-monitor b/electron/native/bin/darwin-arm64/recordly-native-cursor-monitor index fdd0bd08f3aea74efde13723ae5e8a7c063990be..e0e478f11b0d8ac0ed5bcfbcd5fc4ea5dfac4e55 100755 GIT binary patch delta 147 zcmaFxhV{W4)(ty21;m1)p3bl~pRnNUiL@fO8>gC2aBe@r$;h9n*v-JezzD=3AoA-T z4-*3eb2$SO3j+fa5Hc{7PybQG=we@#nw(#hl2fUhmspZnma3avT2!20q??Nl#c!~HQafi-+%FoNJ;e%$D%!4WuPtVL%mg$K06=^;bZ|eiE7O!IJckRWaQ6O>}6nJU<6_i5czeF zhlzoKxsrj2g@J(y2pJeEr~fEobaBitNX;uwE=oa)@mqbJTcjW#yXx3e-qO3qYTK)e86PtN0Ha4Wr2qf` diff --git a/electron/native/bin/darwin-arm64/recordly-screencapturekit-helper b/electron/native/bin/darwin-arm64/recordly-screencapturekit-helper index ff4b8aa5a49ba34960122c50f9d514b61c35ba44..91b54570d68b316b5fd176d8496a340e8c8dfe79 100755 GIT binary patch delta 6049 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 delta 6067 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 delta 136 zcmdnd#lEA9eFF!Vz{9*|*9mtm9<30lP(2{v;o2<0wOxXX@&7hOWd;TYMj!?Okze3vX_!*d37#NsBntoXp}92L6Y)o<4bK mwA<)~+5WT%Dt9XqjIMqE%y5I-{;=MBqc7syHy&hcWd;CLn=f(z diff --git a/electron/native/bin/darwin-x64/recordly-native-cursor-monitor b/electron/native/bin/darwin-x64/recordly-native-cursor-monitor index 58bdf488fcec54b0ce0fff90796124b14b7d881e..d577b1a6fe148c5f17883a13c42d75441b112b2b 100755 GIT binary patch delta 35 rcmZ3noMpvwmJJ!40(b5o^D~}g-9GE1cxK1;!iUWjoZBln84b7qCl?Pv delta 35 rcmZ3noMpvwmJJ!40=E5|?k=)0GFaypb2MEo-=(>Nb9)6RqX8EH2Hp)j diff --git a/electron/native/bin/darwin-x64/recordly-screencapturekit-helper b/electron/native/bin/darwin-x64/recordly-screencapturekit-helper index 831b869056ecb3705c6db369a80f1f3cc6c682d3..3696e45dfb7f9cccc0cb63bf9d5b5a4c1c01cb1c 100755 GIT binary patch delta 5423 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 Wn-KmdeHRq-Eq0DK<~REK=>Gr${>-lc delta 5437 zcmZ{o4OCTC7RS%N@DLE~y)Y3)ec%V8@|D<-X?&;wij63KU|5=vVv2xN;$ZKgVvwdd zx7^rR*=jywTGNo%S&zt7u+&jXCmbDOniwIq#9_@)`JKa^ecst`VQ-CZt^5A>_domW zv(Gv2-FM%;Tru!+#lZTL0M&X6q}e7Wg8(5!l}2y$ny80sNw!0&%B}EI?jgOH+=L9} zHYigh=*{Hzuzo2P$AZY{eL*sax5EJ?N|cB9yB()W_v&F*5Z zw_EQbHlAzVSM zcxEaD56ZnOtRBCUiR13Fw$r)%s$82a#A=Ls2quO04CyZ=nVKzxE2-JU^$&p&yP+t* zaO0|y{QSZgC975zl|EOJ|7<~N%<~0#`5RPk&$}z>XYjU9V1F;XJPpRXtdGE83#`S_ z$uK!;>R2Jl;Ao%_X+kVqS*ERvl&9M9@r0{|mLIM(8cUNQE-rz>pDIcd3s38T|B7&?b`DltQd>8V20pEFIm zN;wZuVQmWc+(>#nb=E4YzJe`Hh<>?;x=@vSLD%bwG z@R5IfX$J+ymm7J1DC;gh{O10)H(rkq|{@Dlq#%~(u8NE zG~g{MH5mQ`$-9^##fimI8gRc9J2p#6$8JToT%wcsu#`AlDJ2oBr8MCOQkEhthtXJ{ z1HP7BKD4JU;%l1AZv}kskLd+446Ox_2(vJw08T&=+SXDpJ3hP?UeLrGJhT?t-~gtq zgAhFUEEK^hw5@|!=#kc2bXDq-V#kX05Q(kp!5{0_gB{l38EM!H!4Lb^Lj)Gdv+;!x zK|^E|(hvu+m;{>&!Jp;`g)kb9VY6&Ghuwv+0g^Fi12|v>{(S?D=X3=WL8O&Nig&^i z*oW0l>hT8FNvXpYDNWcdr4s`xV6-Nti6D@IA_#SAG{ERZq*tED7hos?v3k+K52r8qHQ8_DY!C8Z8Cq#VWqDNR@{BSZ)R_vA%hXLD3=3|r;S{ErbSRjS^N$JG8?GQUaB*|avBKl@Up?@V{Eh$_=KYYql!rmtFaby!@;*<^RUZuj2{)!RXD{EM2mZp#zrux{_wsN3Ki@$E_i-ZcnN8#wT=7cu=sjlk3Ej`i zoxbK3f1dMuuf%N1L*rXTpI3(%x~-MIAKmQT{gD~UGw_c0;dt*YNSk%{7ISNEB!Bj$Tss5ALE&f zU~c9;|B|(xXPnOc7qj2Zx`uTOw{PTp1+1x@*D;#EpS9c}kpu2$P2$3Ca(f8x^(UO5 zjQxM{K$p3&T5ey;eiV=M3~Q!Rhj^6-`kp(E;{oiP;A3uohX)$W2|V8p`7cg>h61_J zmw3Q(?ia`Y>&zRu|3WthTg6}=FoFYrh2cBltt`)VbN(YLf9Ek8hQ{$+FgLNk;<)lX zJGF!TxHjeA=K6M{ZI}8n-35F6!{)fNT3q4F>GyU`#39qXVai*!pT4G%fAuau(JSsj z;#<`2QTP8iN->fAAIOg*z0FEDM(S>?W;eva9c6|ke1$BuLf@;y+)nCB)vo0&c3Hq+5RK>z8|P!q{?`IQdvCxP^CksYyV!jvB2T^ zD<1oz5Th(kk&vwZI%YG^XD(-6%v{a9jM;~-9J2o!=Gn|8%sZJkGyhSsLu_T^0teJE zUuCXkp5#;m9ATcve2RGsa~tzJ%wI7dV*Z->FK%`?#T7O>IG~rghxt0QPlYPLH$|;b zJaZuP0_F(jV&+8V*O(VDzc1Ngaf+weXyJf0%-=8~YW5f=*XYaGo!2ySvuQN9?hip>?G%;^sKFMs^uG(9ftC-uF&oY0*EGp^u zP8RSz8~0VJ1UH#unf>THD`%X<9K>AAJd}AG^C;#InI|)!Wqy$Pn@awWO=kmkr~+m& z$1u-lewX=4=2qrB<{QjwnFqh5@@--s$^0VoY6lxT*!UasZsx!$m0%xpoa6wf@I2|7 z?q*sVx_-$OTEZ-^XOcHFXEP&n6|J@ZGz zqXs%fB0t&tjCqrrY5%*}c$EYCm`^ef-$@CAoWk>Ha3b?fZckq0?WDp diff --git a/electron/native/bin/darwin-x64/recordly-system-cursors b/electron/native/bin/darwin-x64/recordly-system-cursors index 2c53fd6830cd5954465399d110ee2da5014ee7ca..545613624b4254e34bceb3c2de9606dba61fad60 100755 GIT binary patch delta 35 rcmcbyl;y@!mJJ!40&n!)n3dd(>Sx<6_I`Hp-K6FU&g~VPj0v0o4c-n` delta 35 rcmcbyl;y@!mJJ!40!dm2j~r67kmz%Nz@!Ct-kh;CJ+Se(vRWd?wa{h>uI&