Fix Windows mpv handoff and tray setup (#82)

This commit is contained in:
2026-05-25 01:34:01 -07:00
committed by GitHub
parent 17d97f0b7e
commit 920cbab1bc
31 changed files with 751 additions and 220 deletions
+1
View File
@@ -256,6 +256,7 @@ test('buildConfiguredMpvDefaultArgs appends maximized launch mode to configured
'--sub-file-paths=.;subs;subtitles',
'--sid=auto',
'--secondary-sid=auto',
'--sub-visibility=no',
'--secondary-sub-visibility=no',
'--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
'--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
+13 -4
View File
@@ -47,7 +47,10 @@ type SpawnTarget = {
env?: NodeJS.ProcessEnv;
};
type PathModule = Pick<typeof path, 'join' | 'extname' | 'delimiter' | 'sep' | 'resolve'>;
type PathModule = Pick<
typeof path,
'dirname' | 'join' | 'extname' | 'delimiter' | 'sep' | 'resolve' | 'isAbsolute' | 'normalize'
>;
const DETACHED_IDLE_MPV_PID_FILE = path.join(os.tmpdir(), 'subminer-idle-mpv.pid');
const OVERLAY_START_SOCKET_READY_TIMEOUT_MS = 900;
@@ -62,6 +65,12 @@ export interface LauncherRuntimePluginPlan {
errorMessage: string | null;
}
function resolvePluginCandidatePath(candidate: string, pathModule: PathModule): string {
return pathModule.isAbsolute(candidate)
? pathModule.normalize(candidate)
: pathModule.resolve(candidate);
}
export function parseMpvArgString(input: string): string[] {
const chars = input;
const args: string[] = [];
@@ -291,12 +300,12 @@ export function resolveLauncherRuntimePluginPath(options: {
pathModule?: typeof path;
existsSync?: (candidate: string) => boolean;
}): string | null {
const platform = options.platform ?? process.platform;
const pathModule = options.pathModule ?? path;
const existsSync = options.existsSync ?? fs.existsSync;
const env = options.env ?? process.env;
const dirname = options.dirname ?? __dirname;
const cwd = options.cwd ?? process.cwd();
const platform = options.platform ?? process.platform;
const homeDir = options.homeDir ?? os.homedir();
const candidates: string[] = [];
@@ -344,7 +353,7 @@ export function resolveLauncherRuntimePluginPath(options: {
const seen = new Set<string>();
for (const candidate of candidates) {
const resolved = pathModule.resolve(candidate);
const resolved = resolvePluginCandidatePath(candidate, pathModule);
if (seen.has(resolved)) continue;
seen.add(resolved);
const entrypoint = normalizeRuntimePluginEntrypoint(resolved, { pathModule, existsSync });
@@ -1704,7 +1713,7 @@ export async function waitForUnixSocketReady(
const deadline = nowMs() + timeoutMs;
while (nowMs() < deadline) {
try {
if (fs.existsSync(socketPath)) {
if (process.platform === 'win32' || fs.existsSync(socketPath)) {
const ready = await canConnectUnixSocket(socketPath);
if (ready) return true;
}
+2 -2
View File
@@ -365,8 +365,8 @@ export function findRofiTheme(scriptPath: string): string | null {
} else {
const xdgDataHome = process.env.XDG_DATA_HOME || path.join(os.homedir(), '.local/share');
candidates.push(path.join(xdgDataHome, 'SubMiner/themes', ROFI_THEME_FILE));
candidates.push(path.join('/usr/local/share/SubMiner/themes', ROFI_THEME_FILE));
candidates.push(path.join('/usr/share/SubMiner/themes', ROFI_THEME_FILE));
candidates.push(path.posix.join('/usr/local/share/SubMiner/themes', ROFI_THEME_FILE));
candidates.push(path.posix.join('/usr/share/SubMiner/themes', ROFI_THEME_FILE));
}
candidates.push(path.join(scriptDir, 'assets', 'themes', ROFI_THEME_FILE));
+28 -8
View File
@@ -40,6 +40,19 @@ function writeExecutable(filePath: string, body: string): void {
fs.chmodSync(filePath, 0o755);
}
function writeFixtureExecutable(basePath: string, body: string): string {
if (process.platform !== 'win32') {
writeExecutable(basePath, body);
return basePath;
}
const scriptPath = `${basePath}.js`;
const commandPath = `${basePath}.cmd`;
fs.writeFileSync(scriptPath, body);
fs.writeFileSync(commandPath, `@echo off\r\n"${process.execPath}" "${scriptPath}" %*\r\n`);
return commandPath;
}
function createSmokeCase(name: string): SmokeCase {
const baseDir = path.join(process.cwd(), '.tmp', 'launcher-smoke');
fs.mkdirSync(baseDir, { recursive: true });
@@ -52,8 +65,8 @@ function createSmokeCase(name: string): SmokeCase {
const socketDir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-smoke-sock-'));
const socketPath = path.join(socketDir, 'subminer.sock');
const videoPath = path.join(root, 'video.mkv');
const fakeAppPath = path.join(binDir, 'fake-subminer');
const fakeMpvPath = path.join(binDir, 'mpv');
const fakeAppBasePath = path.join(binDir, 'fake-subminer');
const fakeMpvBasePath = path.join(binDir, 'mpv');
const mpvOverlayLogPath = path.join(artifactsDir, 'mpv-overlay.log');
fs.mkdirSync(artifactsDir, { recursive: true });
@@ -74,8 +87,8 @@ function createSmokeCase(name: string): SmokeCase {
const fakeAppStartLogPath = path.join(artifactsDir, 'fake-app-start.log');
const fakeAppStopLogPath = path.join(artifactsDir, 'fake-app-stop.log');
writeExecutable(
fakeMpvPath,
const fakeMpvPath = writeFixtureExecutable(
fakeMpvBasePath,
`#!/usr/bin/env bun
const fs = require('node:fs');
const net = require('node:net');
@@ -113,8 +126,8 @@ process.on('SIGTERM', closeAndExit);
`,
);
writeExecutable(
fakeAppPath,
const fakeAppPath = writeFixtureExecutable(
fakeAppBasePath,
`#!/usr/bin/env bun
const fs = require('node:fs');
@@ -157,14 +170,21 @@ process.exit(0);
}
function makeTestEnv(smokeCase: SmokeCase): NodeJS.ProcessEnv {
return {
const env: NodeJS.ProcessEnv = {
...process.env,
HOME: smokeCase.homeDir,
XDG_CONFIG_HOME: smokeCase.xdgConfigHome,
SUBMINER_APPIMAGE_PATH: smokeCase.fakeAppPath,
SUBMINER_MPV_LOG: smokeCase.mpvOverlayLogPath,
PATH: `${smokeCase.binDir}${path.delimiter}${process.env.PATH || ''}`,
};
const pathKey = Object.keys(env).find((key) => key.toLowerCase() === 'path') ?? 'PATH';
env[pathKey] = `${smokeCase.binDir}${path.delimiter}${env[pathKey] || ''}`;
for (const key of Object.keys(env)) {
if (key !== pathKey && key.toLowerCase() === 'path') {
delete env[key];
}
}
return env;
}
function runLauncher(
+1
View File
@@ -60,6 +60,7 @@ export const DEFAULT_MPV_SUBMINER_ARGS = [
'--sub-file-paths=.;subs;subtitles',
'--sid=auto',
'--secondary-sid=auto',
'--sub-visibility=no',
'--secondary-sub-visibility=no',
'--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
'--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',