fix: hotfix export regressions

This commit is contained in:
webadderall
2026-04-15 17:19:49 +10:00
parent 0026698b17
commit 60d364a4ed
5 changed files with 68 additions and 16 deletions
+56 -9
View File
@@ -157,6 +157,7 @@ let currentProjectPath: string | null = null
let nativeScreenRecordingActive = false
let currentVideoPath: string | null = null
let currentRecordingSession: RecordingSessionData | null = null
const approvedLocalReadPaths = new Set<string>()
let nativeCaptureProcess: ChildProcessWithoutNullStreams | null = null
let nativeCaptureOutputBuffer = ''
let nativeCaptureTargetPath: string | null = null
@@ -623,6 +624,44 @@ function normalizeVideoSourcePath(videoPath?: string | null): string | null {
return trimmed
}
function isPathInsideDirectory(candidatePath: string, directoryPath: string) {
const normalizedDirectoryPath = normalizePath(directoryPath)
return candidatePath === normalizedDirectoryPath || candidatePath.startsWith(`${normalizedDirectoryPath}${path.sep}`)
}
function isAllowedLocalReadPath(candidatePath: string) {
const allowedPrefixes = [
RECORDINGS_DIR,
USER_DATA_PATH,
getAssetRootPath(),
app.getPath('temp'),
]
return allowedPrefixes.some((prefix) => isPathInsideDirectory(candidatePath, prefix))
|| approvedLocalReadPaths.has(candidatePath)
}
async function rememberApprovedLocalReadPath(filePath?: string | null) {
const normalizedPath = normalizeVideoSourcePath(filePath)
if (!normalizedPath) {
return
}
const resolvedPath = normalizePath(normalizedPath)
approvedLocalReadPaths.add(resolvedPath)
try {
approvedLocalReadPaths.add(await fs.realpath(resolvedPath))
} catch {
// Ignore missing files; the eventual read will surface the real error.
}
}
async function replaceApprovedSessionLocalReadPaths(filePaths: Array<string | null | undefined>) {
approvedLocalReadPaths.clear()
await Promise.all(filePaths.map((filePath) => rememberApprovedLocalReadPath(filePath)))
}
async function resolveProjectMediaSources(project: unknown): Promise<
| {
success: true
@@ -4887,7 +4926,12 @@ body{background:transparent;overflow:hidden;width:100vw;height:100vh}
}
try {
return { success: true, paths: await getCompanionAudioFallbackPaths(videoPath) }
const paths = await getCompanionAudioFallbackPaths(videoPath)
await Promise.all([
rememberApprovedLocalReadPath(videoPath),
...paths.map((fallbackPath) => rememberApprovedLocalReadPath(fallbackPath)),
])
return { success: true, paths }
} catch (error) {
console.error('Failed to resolve companion audio fallback paths:', error)
return { success: false, paths: [], error: String(error) }
@@ -5349,14 +5393,9 @@ body{background:transparent;overflow:hidden;width:100vw;height:100vh}
try {
// Security: only allow reads from known safe directories to prevent
// malicious code from reading arbitrary files (SSH keys, credentials, etc.)
const resolved = path.resolve(filePath)
const allowedPrefixes = [
RECORDINGS_DIR,
USER_DATA_PATH,
getAssetRootPath(),
app.getPath('temp'),
]
if (!allowedPrefixes.some((prefix) => resolved.startsWith(path.resolve(prefix)))) {
const resolved = normalizePath(filePath)
const realResolved = await fs.realpath(resolved).catch(() => resolved)
if (!isAllowedLocalReadPath(resolved) && !isAllowedLocalReadPath(realResolved)) {
console.warn(`[read-local-file] Blocked read outside allowed directories: ${resolved}`)
return { success: false, error: 'Access denied: path outside allowed directories' }
}
@@ -6151,6 +6190,10 @@ body{background:transparent;overflow:hidden;width:100vw;height:100vh}
}
currentRecordingSession = resolvedSession
await replaceApprovedSessionLocalReadPaths([
resolvedSession.videoPath,
resolvedSession.webcamPath,
])
if (resolvedSession.webcamPath) {
await persistRecordingSessionManifest(resolvedSession)
@@ -6168,6 +6211,10 @@ body{background:transparent;overflow:hidden;width:100vw;height:100vh}
webcamPath: normalizeVideoSourcePath(session.webcamPath ?? null),
timeOffsetMs: normalizeRecordingTimeOffsetMs(session.timeOffsetMs),
}
await replaceApprovedSessionLocalReadPaths([
currentRecordingSession.videoPath,
currentRecordingSession.webcamPath,
])
currentProjectPath = null
await persistRecordingSessionManifest(currentRecordingSession)
return { success: true }
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "recordly",
"version": "1.1.22",
"version": "1.1.23",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "recordly",
"version": "1.1.22",
"version": "1.1.23",
"hasInstallScript": true,
"dependencies": {
"capturekit": "^1.0.13",
+1 -1
View File
@@ -12,7 +12,7 @@
"url": "https://github.com/webadderall/Recordly/issues"
},
"private": true,
"version": "1.1.22",
"version": "1.1.23",
"type": "module",
"scripts": {
"dev": "vite",
+4 -2
View File
@@ -12,7 +12,7 @@ import type {
ZoomRegion,
ZoomTransitionEasing,
} from "@/components/video-editor/types";
import { AudioProcessor } from "./audioEncoder";
import { AudioProcessor, isAacAudioEncodingSupported } from "./audioEncoder";
import {
normalizeLightningRuntimePlatform,
shouldPreferNativeAutoBackend,
@@ -293,7 +293,9 @@ export class ModernVideoExporter {
this.metadataLoadTimeMs = this.getNowMs() - stageStartedAt;
const nativeAudioPlan = this.buildNativeAudioPlan(videoInfo);
const shouldUseFfmpegAudioFallback =
!useNativeEncoder && nativeAudioPlan.audioMode !== "none";
!useNativeEncoder
&& nativeAudioPlan.audioMode !== "none"
&& !(await isAacAudioEncodingSupported());
const effectiveDuration = this.streamingDecoder.getEffectiveDuration(
this.config.trimRegions,
this.config.speedRegions,
+5 -2
View File
@@ -12,7 +12,7 @@ import type {
ZoomTransitionEasing,
ZoomRegion,
} from "@/components/video-editor/types";
import { AudioProcessor } from "./audioEncoder";
import { AudioProcessor, isAacAudioEncodingSupported } from "./audioEncoder";
import { FrameRenderer } from "./frameRenderer";
import type { SupportedMp4EncoderPath } from "./mp4Support";
import { captureCanvasFrameForNativeExport } from "./nativeFrameCapture";
@@ -129,7 +129,10 @@ export class VideoExporter {
let useNativeEncoder = shouldUseExperimentalNativeExport
? await this.tryStartNativeVideoExport()
: false;
const shouldUseFfmpegAudioFallback = !useNativeEncoder && audioPlan.audioMode !== "none";
const shouldUseFfmpegAudioFallback =
!useNativeEncoder
&& audioPlan.audioMode !== "none"
&& !(await isAacAudioEncodingSupported());
if (!useNativeEncoder) {
await this.initializeEncoder();