mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-10 04:19:25 -07:00
Fix Windows mpv shortcut launch and subtitle dedupe
This commit is contained in:
@@ -184,6 +184,39 @@ test('dispatchMpvProtocolMessage sets secondary subtitle track based on track li
|
||||
assert.deepEqual(state.commands, [{ command: ['set_property', 'secondary-sid', 2] }]);
|
||||
});
|
||||
|
||||
test('dispatchMpvProtocolMessage prefers the already selected matching secondary track', async () => {
|
||||
const { deps, state } = createDeps();
|
||||
|
||||
await dispatchMpvProtocolMessage(
|
||||
{
|
||||
request_id: MPV_REQUEST_ID_TRACK_LIST_SECONDARY,
|
||||
data: [
|
||||
{
|
||||
type: 'sub',
|
||||
id: 2,
|
||||
lang: 'ja',
|
||||
title: 'ja.srt',
|
||||
selected: false,
|
||||
external: true,
|
||||
'external-filename': '/tmp/dupe.srt',
|
||||
},
|
||||
{
|
||||
type: 'sub',
|
||||
id: 3,
|
||||
lang: 'ja',
|
||||
title: 'ja.srt',
|
||||
selected: true,
|
||||
external: true,
|
||||
'external-filename': '/tmp/dupe.srt',
|
||||
},
|
||||
],
|
||||
},
|
||||
deps,
|
||||
);
|
||||
|
||||
assert.deepEqual(state.commands, [{ command: ['set_property', 'secondary-sid', 3] }]);
|
||||
});
|
||||
|
||||
test('dispatchMpvProtocolMessage restores secondary visibility on shutdown', async () => {
|
||||
const { deps, state } = createDeps();
|
||||
|
||||
|
||||
@@ -93,6 +93,97 @@ export interface MpvProtocolHandleMessageDeps {
|
||||
restorePreviousSecondarySubVisibility: () => void;
|
||||
}
|
||||
|
||||
type SubtitleTrackCandidate = {
|
||||
id: number;
|
||||
lang: string;
|
||||
title: string;
|
||||
selected: boolean;
|
||||
external: boolean;
|
||||
externalFilename: string | null;
|
||||
};
|
||||
|
||||
function normalizeSubtitleTrackCandidate(track: Record<string, unknown>): SubtitleTrackCandidate | null {
|
||||
const id =
|
||||
typeof track.id === 'number'
|
||||
? track.id
|
||||
: typeof track.id === 'string'
|
||||
? Number(track.id.trim())
|
||||
: Number.NaN;
|
||||
if (!Number.isInteger(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const externalFilename =
|
||||
typeof track['external-filename'] === 'string' && track['external-filename'].trim().length > 0
|
||||
? track['external-filename'].trim()
|
||||
: typeof track.external_filename === 'string' && track.external_filename.trim().length > 0
|
||||
? track.external_filename.trim()
|
||||
: null;
|
||||
|
||||
return {
|
||||
id,
|
||||
lang: String(track.lang || '').trim().toLowerCase(),
|
||||
title: String(track.title || '').trim().toLowerCase(),
|
||||
selected: track.selected === true,
|
||||
external: track.external === true,
|
||||
externalFilename,
|
||||
};
|
||||
}
|
||||
|
||||
function getSubtitleTrackIdentity(track: SubtitleTrackCandidate): string {
|
||||
if (track.externalFilename) {
|
||||
return `external:${track.externalFilename.toLowerCase()}`;
|
||||
}
|
||||
if (track.title.length > 0) {
|
||||
return `title:${track.title}`;
|
||||
}
|
||||
return `id:${track.id}`;
|
||||
}
|
||||
|
||||
function pickSecondarySubtitleTrackId(
|
||||
tracks: Array<Record<string, unknown>>,
|
||||
preferredLanguages: string[],
|
||||
): number | null {
|
||||
const normalizedLanguages = preferredLanguages
|
||||
.map((language) => language.trim().toLowerCase())
|
||||
.filter((language) => language.length > 0);
|
||||
if (normalizedLanguages.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const subtitleTracks = tracks
|
||||
.filter((track) => track.type === 'sub')
|
||||
.map(normalizeSubtitleTrackCandidate)
|
||||
.filter((track): track is SubtitleTrackCandidate => track !== null);
|
||||
|
||||
const dedupedTracks = new Map<string, SubtitleTrackCandidate>();
|
||||
for (const track of subtitleTracks) {
|
||||
const identity = getSubtitleTrackIdentity(track);
|
||||
const existing = dedupedTracks.get(identity);
|
||||
if (!existing || (track.selected && !existing.selected)) {
|
||||
dedupedTracks.set(identity, track);
|
||||
}
|
||||
}
|
||||
|
||||
const uniqueTracks = [...dedupedTracks.values()];
|
||||
|
||||
for (const language of normalizedLanguages) {
|
||||
const selectedMatch = uniqueTracks.find(
|
||||
(track) => track.selected && track.lang === language,
|
||||
);
|
||||
if (selectedMatch) {
|
||||
return selectedMatch.id;
|
||||
}
|
||||
|
||||
const match = uniqueTracks.find((track) => track.lang === language);
|
||||
if (match) {
|
||||
return match.id;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function splitMpvMessagesFromBuffer(
|
||||
buffer: string,
|
||||
onMessage?: MpvMessageParser,
|
||||
@@ -283,15 +374,11 @@ export async function dispatchMpvProtocolMessage(
|
||||
if (Array.isArray(tracks)) {
|
||||
const config = deps.getResolvedConfig();
|
||||
const languages = config.secondarySub?.secondarySubLanguages || [];
|
||||
const subTracks = tracks.filter((track) => track.type === 'sub');
|
||||
for (const language of languages) {
|
||||
const match = subTracks.find((track) => track.lang === language);
|
||||
if (match) {
|
||||
deps.sendCommand({
|
||||
command: ['set_property', 'secondary-sid', match.id],
|
||||
});
|
||||
break;
|
||||
}
|
||||
const secondaryTrackId = pickSecondarySubtitleTrackId(tracks, languages);
|
||||
if (secondaryTrackId !== null) {
|
||||
deps.sendCommand({
|
||||
command: ['set_property', 'secondary-sid', secondaryTrackId],
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if (msg.request_id === MPV_REQUEST_ID_TRACK_LIST_AUDIO) {
|
||||
|
||||
@@ -92,6 +92,52 @@ test('triggerSubsyncFromConfig opens manual picker in manual mode', async () =>
|
||||
assert.equal(inProgressState, false);
|
||||
});
|
||||
|
||||
test('triggerSubsyncFromConfig dedupes repeated subtitle source tracks', async () => {
|
||||
let payloadTrackCount = 0;
|
||||
|
||||
await triggerSubsyncFromConfig(
|
||||
makeDeps({
|
||||
getMpvClient: () => ({
|
||||
connected: true,
|
||||
currentAudioStreamIndex: null,
|
||||
send: () => {},
|
||||
requestProperty: async (name: string) => {
|
||||
if (name === 'path') return '/tmp/video.mkv';
|
||||
if (name === 'sid') return 1;
|
||||
if (name === 'secondary-sid') return 2;
|
||||
if (name === 'track-list') {
|
||||
return [
|
||||
{ id: 1, type: 'sub', selected: true, lang: 'jpn' },
|
||||
{
|
||||
id: 2,
|
||||
type: 'sub',
|
||||
selected: true,
|
||||
external: true,
|
||||
lang: 'eng',
|
||||
'external-filename': '/tmp/ref.srt',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
type: 'sub',
|
||||
selected: false,
|
||||
external: true,
|
||||
lang: 'eng',
|
||||
'external-filename': '/tmp/ref.srt',
|
||||
},
|
||||
];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}),
|
||||
openManualPicker: (payload) => {
|
||||
payloadTrackCount = payload.sourceTracks.length;
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
assert.equal(payloadTrackCount, 1);
|
||||
});
|
||||
|
||||
test('triggerSubsyncFromConfig reports failures to OSD', async () => {
|
||||
const osd: string[] = [];
|
||||
await triggerSubsyncFromConfig(
|
||||
|
||||
@@ -76,6 +76,31 @@ function normalizeTrackIds(tracks: unknown[]): MpvTrack[] {
|
||||
});
|
||||
}
|
||||
|
||||
function getSourceTrackIdentity(track: MpvTrack): string {
|
||||
if (track.external && typeof track['external-filename'] === 'string' && track['external-filename'].length > 0) {
|
||||
return `external:${track['external-filename'].toLowerCase()}`;
|
||||
}
|
||||
if (typeof track.id === 'number') {
|
||||
return `id:${track.id}`;
|
||||
}
|
||||
if (typeof track.title === 'string' && track.title.length > 0) {
|
||||
return `title:${track.title.toLowerCase()}`;
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
function dedupeSourceTracks(tracks: MpvTrack[]): MpvTrack[] {
|
||||
const deduped = new Map<string, MpvTrack>();
|
||||
for (const track of tracks) {
|
||||
const identity = getSourceTrackIdentity(track);
|
||||
const existing = deduped.get(identity);
|
||||
if (!existing || (track.selected && !existing.selected)) {
|
||||
deduped.set(identity, track);
|
||||
}
|
||||
}
|
||||
return [...deduped.values()];
|
||||
}
|
||||
|
||||
export interface TriggerSubsyncFromConfigDeps extends SubsyncCoreDeps {
|
||||
isSubsyncInProgress: () => boolean;
|
||||
setSubsyncInProgress: (inProgress: boolean) => void;
|
||||
@@ -123,12 +148,13 @@ async function gatherSubsyncContext(client: MpvClientLike): Promise<SubsyncConte
|
||||
const filename = track['external-filename'];
|
||||
return typeof filename === 'string' && filename.length > 0;
|
||||
});
|
||||
const uniqueSourceTracks = dedupeSourceTracks(sourceTracks);
|
||||
|
||||
return {
|
||||
videoPath,
|
||||
primaryTrack,
|
||||
secondaryTrack,
|
||||
sourceTracks,
|
||||
sourceTracks: uniqueSourceTracks,
|
||||
audioStreamIndex: client.currentAudioStreamIndex,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
configureEarlyAppPaths,
|
||||
normalizeLaunchMpvExtraArgs,
|
||||
normalizeStartupArgv,
|
||||
normalizeLaunchMpvTargets,
|
||||
sanitizeHelpEnv,
|
||||
@@ -70,6 +71,41 @@ test('launch-mpv entry helpers detect and normalize targets', () => {
|
||||
assert.deepEqual(normalizeLaunchMpvTargets(['SubMiner.exe', '--launch-mpv', 'C:\\a.mkv']), [
|
||||
'C:\\a.mkv',
|
||||
]);
|
||||
assert.deepEqual(
|
||||
normalizeLaunchMpvExtraArgs([
|
||||
'SubMiner.exe',
|
||||
'--launch-mpv',
|
||||
'--profile=subminer',
|
||||
'--pause=yes',
|
||||
'C:\\a.mkv',
|
||||
]),
|
||||
['--profile=subminer', '--pause=yes'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
normalizeLaunchMpvExtraArgs([
|
||||
'SubMiner.exe',
|
||||
'--launch-mpv',
|
||||
'--input-ipc-server',
|
||||
'\\\\.\\pipe\\custom-subminer-socket',
|
||||
'--alang',
|
||||
'ja,jpn',
|
||||
'C:\\a.mkv',
|
||||
]),
|
||||
['--input-ipc-server', '\\\\.\\pipe\\custom-subminer-socket', '--alang', 'ja,jpn'],
|
||||
);
|
||||
assert.deepEqual(
|
||||
normalizeLaunchMpvTargets([
|
||||
'SubMiner.exe',
|
||||
'--launch-mpv',
|
||||
'--input-ipc-server',
|
||||
'\\\\.\\pipe\\custom-subminer-socket',
|
||||
'--alang',
|
||||
'ja,jpn',
|
||||
'C:\\a.mkv',
|
||||
'C:\\b.mkv',
|
||||
]),
|
||||
['C:\\a.mkv', 'C:\\b.mkv'],
|
||||
);
|
||||
});
|
||||
|
||||
test('stats-daemon entry helper detects internal daemon commands', () => {
|
||||
|
||||
@@ -121,7 +121,94 @@ export function shouldHandleStatsDaemonCommandAtEntry(
|
||||
}
|
||||
|
||||
export function normalizeLaunchMpvTargets(argv: string[]): string[] {
|
||||
return parseCliArgs(argv).launchMpvTargets;
|
||||
const launchMpvIndex = argv.findIndex((arg) => arg === '--launch-mpv');
|
||||
if (launchMpvIndex < 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const targets: string[] = [];
|
||||
const flagValueArgs = new Set([
|
||||
'--alang',
|
||||
'--input-ipc-server',
|
||||
'--log-file',
|
||||
'--profile',
|
||||
'--script',
|
||||
'--script-opts',
|
||||
'--scripts',
|
||||
'--slang',
|
||||
'--sub-file-paths',
|
||||
'--ytdl-format',
|
||||
]);
|
||||
|
||||
let parsingTargets = false;
|
||||
for (let i = launchMpvIndex + 1; i < argv.length; i += 1) {
|
||||
const token = argv[i];
|
||||
if (!token) continue;
|
||||
|
||||
if (parsingTargets) {
|
||||
targets.push(token);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token === '--') {
|
||||
parsingTargets = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token.startsWith('-')) {
|
||||
if (!token.includes('=') && flagValueArgs.has(token)) {
|
||||
i += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
parsingTargets = true;
|
||||
targets.push(token);
|
||||
}
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
export function normalizeLaunchMpvExtraArgs(argv: string[]): string[] {
|
||||
const launchMpvIndex = argv.findIndex((arg) => arg === '--launch-mpv');
|
||||
if (launchMpvIndex < 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const flagValueArgs = new Set([
|
||||
'--alang',
|
||||
'--input-ipc-server',
|
||||
'--log-file',
|
||||
'--profile',
|
||||
'--script',
|
||||
'--script-opts',
|
||||
'--scripts',
|
||||
'--slang',
|
||||
'--sub-file-paths',
|
||||
'--ytdl-format',
|
||||
]);
|
||||
|
||||
const extraArgs: string[] = [];
|
||||
for (let i = launchMpvIndex + 1; i < argv.length; i += 1) {
|
||||
const token = argv[i];
|
||||
if (!token) continue;
|
||||
if (token === '--') {
|
||||
break;
|
||||
}
|
||||
if (!token.startsWith('-')) {
|
||||
break;
|
||||
}
|
||||
|
||||
extraArgs.push(token);
|
||||
if (!token.includes('=') && flagValueArgs.has(token)) {
|
||||
const value = argv[i + 1];
|
||||
if (value && value !== '--') {
|
||||
extraArgs.push(value);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return extraArgs;
|
||||
}
|
||||
|
||||
export function sanitizeStartupEnv(baseEnv: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import path from 'node:path';
|
||||
import { spawn } from 'node:child_process';
|
||||
import { app, dialog } from 'electron';
|
||||
import { printHelp } from './cli/help';
|
||||
import {
|
||||
configureEarlyAppPaths,
|
||||
normalizeLaunchMpvExtraArgs,
|
||||
normalizeLaunchMpvTargets,
|
||||
normalizeStartupArgv,
|
||||
sanitizeStartupEnv,
|
||||
@@ -15,6 +17,7 @@ import {
|
||||
shouldHandleStatsDaemonCommandAtEntry,
|
||||
} from './main-entry-runtime';
|
||||
import { requestSingleInstanceLockEarly } from './main/early-single-instance';
|
||||
import { resolvePackagedFirstRunPluginAssets } from './main/runtime/first-run-setup-plugin';
|
||||
import { createWindowsMpvLaunchDeps, launchWindowsMpv } from './main/runtime/windows-mpv-launch';
|
||||
import { runStatsDaemonControlFromProcess } from './stats-daemon-entry';
|
||||
|
||||
@@ -32,6 +35,19 @@ function applySanitizedEnv(sanitizedEnv: NodeJS.ProcessEnv): void {
|
||||
}
|
||||
}
|
||||
|
||||
function resolveBundledWindowsMpvPluginEntrypoint(): string | undefined {
|
||||
const assets = resolvePackagedFirstRunPluginAssets({
|
||||
dirname: __dirname,
|
||||
appPath: app.getAppPath(),
|
||||
resourcesPath: process.resourcesPath,
|
||||
});
|
||||
if (!assets) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return path.join(assets.pluginDirSource, 'main.lua');
|
||||
}
|
||||
|
||||
process.argv = normalizeStartupArgv(process.argv, process.env);
|
||||
applySanitizedEnv(sanitizeStartupEnv(process.env));
|
||||
configureEarlyAppPaths(app);
|
||||
@@ -68,6 +84,9 @@ if (shouldHandleLaunchMpvAtEntry(process.argv, process.env)) {
|
||||
dialog.showErrorBox(title, content);
|
||||
},
|
||||
}),
|
||||
normalizeLaunchMpvExtraArgs(process.argv),
|
||||
process.execPath,
|
||||
resolveBundledWindowsMpvPluginEntrypoint(),
|
||||
);
|
||||
app.exit(result.ok ? 0 : 1);
|
||||
});
|
||||
|
||||
@@ -41,21 +41,57 @@ test('resolveWindowsMpvPath falls back to where.exe output', () => {
|
||||
});
|
||||
|
||||
test('buildWindowsMpvLaunchArgs uses explicit SubMiner defaults and targets', () => {
|
||||
assert.deepEqual(buildWindowsMpvLaunchArgs(['C:\\a.mkv', 'C:\\b.mkv']), [
|
||||
assert.deepEqual(
|
||||
buildWindowsMpvLaunchArgs(
|
||||
['C:\\a.mkv', 'C:\\b.mkv'],
|
||||
[],
|
||||
'C:\\SubMiner\\SubMiner.exe',
|
||||
'C:\\Program Files\\SubMiner\\resources\\plugin\\subminer\\main.lua',
|
||||
),
|
||||
[
|
||||
'--player-operation-mode=pseudo-gui',
|
||||
'--force-window=immediate',
|
||||
'--script=C:\\Program Files\\SubMiner\\resources\\plugin\\subminer\\main.lua',
|
||||
'--input-ipc-server=\\\\.\\pipe\\subminer-socket',
|
||||
'--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
|
||||
'--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
|
||||
'--sub-auto=fuzzy',
|
||||
'--sub-file-paths=.;subs;subtitles',
|
||||
'--sub-file-paths=subs;subtitles',
|
||||
'--sid=auto',
|
||||
'--secondary-sid=auto',
|
||||
'--secondary-sub-visibility=no',
|
||||
'--script-opts=subminer-binary_path=C:\\SubMiner\\SubMiner.exe,subminer-socket_path=\\\\.\\pipe\\subminer-socket',
|
||||
'C:\\a.mkv',
|
||||
'C:\\b.mkv',
|
||||
]);
|
||||
});
|
||||
|
||||
test('buildWindowsMpvLaunchArgs keeps shortcut-only launches in idle mode', () => {
|
||||
assert.deepEqual(
|
||||
buildWindowsMpvLaunchArgs(
|
||||
[],
|
||||
[],
|
||||
'C:\\SubMiner\\SubMiner.exe',
|
||||
'C:\\Program Files\\SubMiner\\resources\\plugin\\subminer\\main.lua',
|
||||
),
|
||||
[
|
||||
'--player-operation-mode=pseudo-gui',
|
||||
'--force-window=immediate',
|
||||
'--idle=yes',
|
||||
'--script=C:\\Program Files\\SubMiner\\resources\\plugin\\subminer\\main.lua',
|
||||
'--input-ipc-server=\\\\.\\pipe\\subminer-socket',
|
||||
'--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
|
||||
'--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
|
||||
'--sub-auto=fuzzy',
|
||||
'--sub-file-paths=subs;subtitles',
|
||||
'--sid=auto',
|
||||
'--secondary-sid=auto',
|
||||
'--secondary-sub-visibility=no',
|
||||
'--script-opts=subminer-binary_path=C:\\SubMiner\\SubMiner.exe,subminer-socket_path=\\\\.\\pipe\\subminer-socket',
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('launchWindowsMpv reports missing mpv path', () => {
|
||||
const errors: string[] = [];
|
||||
const result = launchWindowsMpv(
|
||||
@@ -82,13 +118,16 @@ test('launchWindowsMpv spawns detached mpv with targets', () => {
|
||||
calls.push(args.join('|'));
|
||||
},
|
||||
}),
|
||||
[],
|
||||
'C:\\SubMiner\\SubMiner.exe',
|
||||
'C:\\Program Files\\SubMiner\\resources\\plugin\\subminer\\main.lua',
|
||||
);
|
||||
|
||||
assert.equal(result.ok, true);
|
||||
assert.equal(result.mpvPath, 'C:\\mpv\\mpv.exe');
|
||||
assert.deepEqual(calls, [
|
||||
'C:\\mpv\\mpv.exe',
|
||||
'--player-operation-mode=pseudo-gui|--input-ipc-server=\\\\.\\pipe\\subminer-socket|--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us|--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us|--sub-auto=fuzzy|--sub-file-paths=.;subs;subtitles|--sid=auto|--secondary-sid=auto|--secondary-sub-visibility=no|C:\\video.mkv',
|
||||
'--player-operation-mode=pseudo-gui|--force-window=immediate|--script=C:\\Program Files\\SubMiner\\resources\\plugin\\subminer\\main.lua|--input-ipc-server=\\\\.\\pipe\\subminer-socket|--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us|--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us|--sub-auto=fuzzy|--sub-file-paths=subs;subtitles|--sid=auto|--secondary-sid=auto|--secondary-sub-visibility=no|--script-opts=subminer-binary_path=C:\\SubMiner\\SubMiner.exe,subminer-socket_path=\\\\.\\pipe\\subminer-socket|C:\\video.mkv',
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -33,17 +33,36 @@ export function resolveWindowsMpvPath(deps: WindowsMpvLaunchDeps): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
export function buildWindowsMpvLaunchArgs(targets: string[], extraArgs: string[] = []): string[] {
|
||||
export function buildWindowsMpvLaunchArgs(
|
||||
targets: string[],
|
||||
extraArgs: string[] = [],
|
||||
binaryPath?: string,
|
||||
pluginEntrypointPath?: string,
|
||||
): string[] {
|
||||
const launchIdle = targets.length === 0;
|
||||
const scriptOpts =
|
||||
typeof binaryPath === 'string' && binaryPath.trim().length > 0
|
||||
? `--script-opts=subminer-binary_path=${binaryPath.trim().replace(/,/g, '\\,')},subminer-socket_path=\\\\.\\pipe\\subminer-socket`
|
||||
: null;
|
||||
const scriptEntrypoint =
|
||||
typeof pluginEntrypointPath === 'string' && pluginEntrypointPath.trim().length > 0
|
||||
? `--script=${pluginEntrypointPath.trim()}`
|
||||
: null;
|
||||
|
||||
return [
|
||||
'--player-operation-mode=pseudo-gui',
|
||||
'--force-window=immediate',
|
||||
...(launchIdle ? ['--idle=yes'] : []),
|
||||
...(scriptEntrypoint ? [scriptEntrypoint] : []),
|
||||
'--input-ipc-server=\\\\.\\pipe\\subminer-socket',
|
||||
'--alang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
|
||||
'--slang=ja,jp,jpn,japanese,en,eng,english,enus,en-us',
|
||||
'--sub-auto=fuzzy',
|
||||
'--sub-file-paths=.;subs;subtitles',
|
||||
'--sub-file-paths=subs;subtitles',
|
||||
'--sid=auto',
|
||||
'--secondary-sid=auto',
|
||||
'--secondary-sub-visibility=no',
|
||||
...(scriptOpts ? [scriptOpts] : []),
|
||||
...extraArgs,
|
||||
...targets,
|
||||
];
|
||||
@@ -53,6 +72,8 @@ export function launchWindowsMpv(
|
||||
targets: string[],
|
||||
deps: WindowsMpvLaunchDeps,
|
||||
extraArgs: string[] = [],
|
||||
binaryPath?: string,
|
||||
pluginEntrypointPath?: string,
|
||||
): { ok: boolean; mpvPath: string } {
|
||||
const mpvPath = resolveWindowsMpvPath(deps);
|
||||
if (!mpvPath) {
|
||||
@@ -64,7 +85,10 @@ export function launchWindowsMpv(
|
||||
}
|
||||
|
||||
try {
|
||||
deps.spawnDetached(mpvPath, buildWindowsMpvLaunchArgs(targets, extraArgs));
|
||||
deps.spawnDetached(
|
||||
mpvPath,
|
||||
buildWindowsMpvLaunchArgs(targets, extraArgs, binaryPath, pluginEntrypointPath),
|
||||
);
|
||||
return { ok: true, mpvPath };
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
|
||||
Reference in New Issue
Block a user