mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-11-10 04:46:07 -08:00
63 lines
2.5 KiB
TypeScript
63 lines
2.5 KiB
TypeScript
import { execFile } from 'node:child_process';
|
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
import type {
|
|
SubtitleGenerationAcceleration,
|
|
SubtitleGenerationToolStatus,
|
|
} from '../../shared/subtitle-generation';
|
|
|
|
function probe(command: string, args: string[], env: NodeJS.ProcessEnv) {
|
|
return new Promise<{ code: number; stdout: string; stderr: string } | null>((resolve) => {
|
|
execFile(
|
|
command,
|
|
args,
|
|
{ env, timeout: 3000, killSignal: 'SIGKILL', maxBuffer: 64 * 1024, windowsHide: true },
|
|
(error, stdout, stderr) => {
|
|
const code = error?.code ?? 0;
|
|
if (typeof code !== 'number' || error?.killed || error?.signal) {
|
|
resolve(null);
|
|
return;
|
|
}
|
|
resolve({ code, stdout, stderr });
|
|
},
|
|
);
|
|
});
|
|
}
|
|
|
|
/** Require both a working NVIDIA driver and CUDA device discovery in the selected Whisper binary. */
|
|
export async function detectSubtitleGenerationAcceleration(
|
|
whisper: SubtitleGenerationToolStatus,
|
|
env: NodeJS.ProcessEnv = process.env,
|
|
): Promise<SubtitleGenerationAcceleration> {
|
|
const unavailable: SubtitleGenerationAcceleration = { kind: 'unavailable' };
|
|
if (whisper.kind === 'missing') return unavailable;
|
|
let directory: string | undefined;
|
|
try {
|
|
const nvidia = await probe('nvidia-smi', ['--query-gpu=name', '--format=csv,noheader'], env);
|
|
const gpuName = nvidia?.stdout.trim().split(/\r?\n/)[0]?.trim();
|
|
if (nvidia?.code !== 0 || !gpuName) return unavailable;
|
|
|
|
directory = await mkdtemp(path.join(tmpdir(), 'subminer-cuda-check-'));
|
|
const model = path.join(directory, 'probe.bin');
|
|
await writeFile(model, Buffer.alloc(4));
|
|
// whisper.cpp discovers backends before checking model magic. This deliberately invalid
|
|
// local file stops before allocating a model or decoding audio, even on a fresh install.
|
|
const result = await probe(whisper.path, ['-m', model, '-f', model], env);
|
|
if (
|
|
result &&
|
|
result.code !== 0 &&
|
|
/ggml_cuda_init:\s+found\s+[1-9]\d*\s+CUDA devices?\b/i.test(result.stderr) &&
|
|
/invalid model data \(bad magic\)/i.test(result.stderr)
|
|
) {
|
|
return { kind: 'nvidia-cuda', gpuName };
|
|
}
|
|
return unavailable;
|
|
} catch {
|
|
// Detection is advisory; unsupported builds and driver failures must not block generation.
|
|
return unavailable;
|
|
} finally {
|
|
if (directory) await rm(directory, { recursive: true, force: true }).catch(() => {});
|
|
}
|
|
}
|