fix: address CodeRabbit follow-ups for PR #40

This commit is contained in:
2026-04-03 01:14:57 -07:00
parent 61ab1b76fc
commit bf06463bb3
14 changed files with 341 additions and 116 deletions

View File

@@ -14,7 +14,6 @@ import {
isExecutable,
resolveBinaryPathCandidate,
resolveCommandInvocation,
realpathMaybe,
isYoutubeTarget,
uniqueNormalizedLangCodes,
sleep,
@@ -35,6 +34,8 @@ type SpawnTarget = {
args: string[];
};
type PathModule = Pick<typeof path, 'join' | 'extname' | 'delimiter' | 'sep' | 'resolve'>;
const DETACHED_IDLE_MPV_PID_FILE = path.join(os.tmpdir(), 'subminer-idle-mpv.pid');
const OVERLAY_START_SOCKET_READY_TIMEOUT_MS = 900;
const OVERLAY_START_COMMAND_SETTLE_TIMEOUT_MS = 700;
@@ -243,29 +244,30 @@ export function detectBackend(backend: Backend): Exclude<Backend, 'auto'> {
fail('Could not detect display backend');
}
function resolveAppBinaryCandidate(candidate: string): string {
function resolveAppBinaryCandidate(candidate: string, pathModule: PathModule = path): string {
const direct = resolveBinaryPathCandidate(candidate);
if (!direct) return '';
if (isExecutable(direct)) {
return direct;
}
if (process.platform === 'win32') {
try {
if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
for (const candidateBinary of ['SubMiner.exe', 'subminer.exe']) {
const nestedCandidate = path.join(direct, candidateBinary);
const nestedCandidate = pathModule.join(direct, candidateBinary);
if (isExecutable(nestedCandidate)) {
return nestedCandidate;
}
}
return '';
}
} catch {
// ignore
}
if (!path.extname(direct)) {
if (isExecutable(direct)) {
return direct;
}
if (!pathModule.extname(direct)) {
for (const extension of ['.exe', '.cmd', '.bat']) {
const withExtension = `${direct}${extension}`;
if (isExecutable(withExtension)) {
@@ -277,6 +279,10 @@ function resolveAppBinaryCandidate(candidate: string): string {
return '';
}
if (isExecutable(direct)) {
return direct;
}
if (process.platform !== 'darwin') {
return '';
}
@@ -291,8 +297,8 @@ function resolveAppBinaryCandidate(candidate: string): string {
if (!appPath) return '';
const candidates = [
path.join(appPath, 'Contents', 'MacOS', 'SubMiner'),
path.join(appPath, 'Contents', 'MacOS', 'subminer'),
pathModule.join(appPath, 'Contents', 'MacOS', 'SubMiner'),
pathModule.join(appPath, 'Contents', 'MacOS', 'subminer'),
];
for (const candidateBinary of candidates) {
@@ -304,20 +310,20 @@ function resolveAppBinaryCandidate(candidate: string): string {
return '';
}
function findCommandOnPath(candidates: string[]): string {
const pathDirs = getPathEnv().split(path.delimiter);
function findCommandOnPath(candidates: string[], pathModule: PathModule = path): string {
const pathDirs = getPathEnv().split(pathModule.delimiter);
for (const candidateName of candidates) {
for (const dir of pathDirs) {
if (!dir) continue;
const directCandidate = path.join(dir, candidateName);
const directCandidate = pathModule.join(dir, candidateName);
if (isExecutable(directCandidate)) {
return directCandidate;
}
if (process.platform === 'win32' && !path.extname(candidateName)) {
if (process.platform === 'win32' && !pathModule.extname(candidateName)) {
for (const extension of ['.exe', '.cmd', '.bat']) {
const extendedCandidate = path.join(dir, `${candidateName}${extension}`);
const extendedCandidate = pathModule.join(dir, `${candidateName}${extension}`);
if (isExecutable(extendedCandidate)) {
return extendedCandidate;
}
@@ -329,13 +335,13 @@ function findCommandOnPath(candidates: string[]): string {
return '';
}
export function findAppBinary(selfPath: string): string | null {
export function findAppBinary(selfPath: string, pathModule: PathModule = path): string | null {
const envPaths = [process.env.SUBMINER_APPIMAGE_PATH, process.env.SUBMINER_BINARY_PATH].filter(
(candidate): candidate is string => Boolean(candidate),
);
for (const envPath of envPaths) {
const resolved = resolveAppBinaryCandidate(envPath);
const resolved = resolveAppBinaryCandidate(envPath, pathModule);
if (resolved) {
return resolved;
}
@@ -345,36 +351,37 @@ export function findAppBinary(selfPath: string): string | null {
if (process.platform === 'win32') {
const localAppData =
process.env.LOCALAPPDATA?.trim() ||
(process.env.APPDATA?.trim() || '').replace(/[\\/]Roaming$/i, `${path.sep}Local`) ||
path.join(os.homedir(), 'AppData', 'Local');
(process.env.APPDATA?.trim() || '').replace(/[\\/]Roaming$/i, `${pathModule.sep}Local`) ||
pathModule.join(os.homedir(), 'AppData', 'Local');
const programFiles = process.env.ProgramFiles?.trim() || 'C:\\Program Files';
const programFilesX86 = process.env['ProgramFiles(x86)']?.trim() || 'C:\\Program Files (x86)';
candidates.push(path.join(localAppData, 'Programs', 'SubMiner', 'SubMiner.exe'));
candidates.push(path.join(programFiles, 'SubMiner', 'SubMiner.exe'));
candidates.push(path.join(programFilesX86, 'SubMiner', 'SubMiner.exe'));
candidates.push(pathModule.join(localAppData, 'Programs', 'SubMiner', 'SubMiner.exe'));
candidates.push(pathModule.join(programFiles, 'SubMiner', 'SubMiner.exe'));
candidates.push(pathModule.join(programFilesX86, 'SubMiner', 'SubMiner.exe'));
candidates.push('C:\\SubMiner\\SubMiner.exe');
} else if (process.platform === 'darwin') {
candidates.push('/Applications/SubMiner.app/Contents/MacOS/SubMiner');
candidates.push('/Applications/SubMiner.app/Contents/MacOS/subminer');
candidates.push(path.join(os.homedir(), 'Applications/SubMiner.app/Contents/MacOS/SubMiner'));
candidates.push(path.join(os.homedir(), 'Applications/SubMiner.app/Contents/MacOS/subminer'));
candidates.push(pathModule.join(os.homedir(), 'Applications/SubMiner.app/Contents/MacOS/SubMiner'));
candidates.push(pathModule.join(os.homedir(), 'Applications/SubMiner.app/Contents/MacOS/subminer'));
} else {
candidates.push(path.join(os.homedir(), '.local/bin/SubMiner.AppImage'));
candidates.push(pathModule.join(os.homedir(), '.local/bin/SubMiner.AppImage'));
candidates.push('/opt/SubMiner/SubMiner.AppImage');
}
for (const candidate of candidates) {
const resolved = resolveAppBinaryCandidate(candidate);
const resolved = resolveAppBinaryCandidate(candidate, pathModule);
if (resolved) return resolved;
}
const fromPath = findCommandOnPath(
process.platform === 'win32' ? ['SubMiner', 'subminer'] : ['subminer'],
pathModule,
);
if (fromPath) {
const resolvedSelf = realpathMaybe(selfPath);
const resolvedCandidate = realpathMaybe(fromPath);
const resolvedSelf = pathModule.resolve(selfPath);
const resolvedCandidate = pathModule.resolve(fromPath);
if (resolvedSelf !== resolvedCandidate) return fromPath;
}