From 12ac980f2991e43648bf82fb27dfab4d726a4b50 Mon Sep 17 00:00:00 2001 From: Nika Siradze Date: Fri, 19 Jun 2026 14:46:25 +0400 Subject: [PATCH] Fix CLI-backed Studio actions in bundled runtime (honor PODCLI_BACKEND) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runCli and the DaVinci export resolved the Python backend at projectRoot/backend, which in a hermetic install points outside the bundle (the launcher ships it under runtime/backend and sets PODCLI_BACKEND). That broke every CLI-backed Studio action there — including the new thumbnail-config import/export/reset, which failed with "can't open .../backend/cli.py: No such file or directory". Resolve the backend dir once (PODCLI_BACKEND, else projectRoot/backend) and use it for runCli, pythonBackend, and the DaVinci CLI, so the same code works in dev and in the bundled runtime on every platform. --- src/config/paths.ts | 11 ++++++++--- src/ui/web-server.ts | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/config/paths.ts b/src/config/paths.ts index 25fac3e..c9477d7 100644 --- a/src/config/paths.ts +++ b/src/config/paths.ts @@ -28,6 +28,12 @@ function resolveHome(): string { const home = resolveHome(); +// Hermetic installs run from a bundled runtime where the backend lives outside +// projectRoot; the launcher points PODCLI_BACKEND at it. +const backendDir = process.env.PODCLI_BACKEND + ? resolve(process.env.PODCLI_BACKEND) + : join(projectRoot, "backend"); + function detectPython(): string { if (process.env.PYTHON_PATH) return process.env.PYTHON_PATH; const isWindows = process.platform === "win32"; @@ -44,6 +50,7 @@ function detectPython(): string { export const paths = { home, projectRoot, + backendDir, homeMarker, dataDir, cache: join(dataDir, "cache"), @@ -61,9 +68,7 @@ export const paths = { corrections: join(home, "corrections.json"), thumbnailConfig: join(home, "thumbnail-config.json"), integrations: join(home, "integrations.json"), - pythonBackend: process.env.PODCLI_BACKEND - ? join(resolve(process.env.PODCLI_BACKEND), "main.py") - : join(projectRoot, "backend", "main.py"), + pythonBackend: join(backendDir, "main.py"), pythonPath: detectPython(), ffmpegPath: process.env.FFMPEG_PATH || "ffmpeg", ffprobePath: process.env.FFPROBE_PATH || "ffprobe", diff --git a/src/ui/web-server.ts b/src/ui/web-server.ts index 6d46ac6..33ba40d 100644 --- a/src/ui/web-server.ts +++ b/src/ui/web-server.ts @@ -1327,7 +1327,7 @@ function runPy(scriptAndArgs: string[]): Promise<{ code: number; stdout: string; } const runCli = (args: string[]) => - runPy([join(paths.projectRoot, "backend", "cli.py"), "--no-banner", ...args]); + runPy([join(paths.backendDir, "cli.py"), "--no-banner", ...args]); // Composite a thumbnail PNG onto the start of a clip. stripStart > 0 removes a // prior card first (avoids stacking on re-bake). Returns the bake's success. @@ -1720,7 +1720,7 @@ app.post("/api/clips/:id/davinci", async (req, res) => { res.status(400).json({ error: "rendered file missing" }); return; } - const cli = join(paths.projectRoot, "backend", "services", "integrations", "davinci_resolve", "cli.py"); + const cli = join(paths.backendDir, "services", "integrations", "davinci_resolve", "cli.py"); const r = await runPy([cli, "--source", clip.output_path, "--title", clip.title]); if (r.code !== 0) { res.status(400).json({ error: stripAnsi(r.stderr || r.stdout) || "export failed" });