fix(cli): use 'where' instead of 'which' on Windows for FFmpeg and br… (#336)

* fix(cli): use 'where' instead of 'which' on Windows for FFmpeg and browser detection

- findFFmpeg() now uses 'where ffmpeg' on Windows, 'which ffmpeg' on Unix
- whichBinary() now uses 'where' on Windows, 'which' on Unix

Fixes FFmpeg detection failure on Windows where 'which' command doesn't exist.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(cli): handle multi-line output from Windows 'where' command

Windows 'where' can return multiple paths (one per line) when there
are multiple matches on PATH. Take only the first non-empty line.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(cli): extend Windows 'where' fix to whisper, tts, and clipboard modules

- whisper/manager.ts: whichBinary() now uses 'where' on Windows
- tts/synthesize.ts: findPython() now uses 'where' on Windows
- utils/clipboard.ts: detectProvider() now uses 'where' on Windows

All functions handle multi-line output from 'where' command.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Wang
2026-04-19 20:05:24 +02:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 38b7cb1c66
commit d1f992570a
5 changed files with 37 additions and 15 deletions
+8 -3
View File
@@ -2,12 +2,17 @@ import { execSync } from "node:child_process";
export function findFFmpeg(): string | undefined {
try {
const result = execSync("which ffmpeg", {
const cmd = process.platform === "win32" ? "where ffmpeg" : "which ffmpeg";
const output = execSync(cmd, {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
timeout: 5000,
}).trim();
return result || undefined;
});
const first = output
.split(/\r?\n/)
.map((s) => s.trim())
.find(Boolean);
return first || undefined;
} catch {
return undefined;
}
+8 -3
View File
@@ -38,12 +38,17 @@ const SYSTEM_CHROME_PATHS: ReadonlyArray<string> =
function whichBinary(name: string): string | undefined {
try {
const result = execSync(`which ${name}`, {
const cmd = process.platform === "win32" ? `where ${name}` : `which ${name}`;
const output = execSync(cmd, {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
timeout: 5000,
}).trim();
return result || undefined;
});
const first = output
.split(/\r?\n/)
.map((s) => s.trim())
.find(Boolean);
return first || undefined;
} catch {
return undefined;
}
+10 -4
View File
@@ -11,20 +11,26 @@ import { ensureModel, ensureVoices, DEFAULT_VOICE } from "./manager.js";
function findPython(): string | undefined {
for (const name of ["python3", "python"]) {
try {
const result = execFileSync("which", [name], {
const cmd = process.platform === "win32" ? "where" : "which";
const output = execFileSync(cmd, [name], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
timeout: 5000,
}).trim();
});
const first = output
.split(/\r?\n/)
.map((s) => s.trim())
.find(Boolean);
if (!first) continue;
// Verify it's Python 3
const version = execFileSync(result, ["--version"], {
const version = execFileSync(first, ["--version"], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
timeout: 5000,
}).trim();
if (version.includes("Python 3")) return result;
if (version.includes("Python 3")) return first;
} catch {
// not found or not Python 3
}
+3 -2
View File
@@ -31,9 +31,10 @@ function detectProvider(): ClipboardProvider | undefined {
{ cmd: "xclip", args: ["-selection", "clipboard"] },
{ cmd: "xsel", args: ["--clipboard", "--input"] },
];
const cmd = process.platform === "win32" ? "where" : "which";
for (const p of candidates) {
const which = spawnSync("which", [p.cmd], { stdio: "ignore" });
if (which.status === 0) return p;
const result = spawnSync(cmd, [p.cmd], { stdio: "ignore" });
if (result.status === 0) return p;
}
return undefined;
}
+8 -3
View File
@@ -22,12 +22,17 @@ function getModelUrl(model: string): string {
function whichBinary(name: string): string | undefined {
try {
const result = execFileSync("which", [name], {
const cmd = process.platform === "win32" ? "where" : "which";
const output = execFileSync(cmd, [name], {
encoding: "utf-8",
stdio: ["pipe", "pipe", "pipe"],
timeout: 5000,
}).trim();
return result || undefined;
});
const first = output
.split(/\r?\n/)
.map((s) => s.trim())
.find(Boolean);
return first || undefined;
} catch {
return undefined;
}