mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-04-08 10:19:21 -07:00
[codex] Replace mpv fullscreen toggle with launch mode config (#48)
Co-authored-by: bee <autumn@skerritt.blog>
This commit is contained in:
@@ -58,6 +58,7 @@ function createContext(): LauncherCommandContext {
|
||||
jellyfinServer: '',
|
||||
jellyfinUsername: '',
|
||||
jellyfinPassword: '',
|
||||
launchMode: 'normal',
|
||||
},
|
||||
scriptPath: '/tmp/subminer',
|
||||
scriptName: 'subminer',
|
||||
|
||||
@@ -2,6 +2,7 @@ import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { parseLauncherYoutubeSubgenConfig } from './config/youtube-subgen-config.js';
|
||||
import { parseLauncherJellyfinConfig } from './config/jellyfin-config.js';
|
||||
import { parseLauncherMpvConfig } from './config/mpv-config.js';
|
||||
import { readExternalYomitanProfilePath } from './config.js';
|
||||
import {
|
||||
getPluginConfigCandidates,
|
||||
@@ -80,6 +81,27 @@ test('parseLauncherJellyfinConfig omits legacy token and user id fields', () =>
|
||||
assert.equal('userId' in parsed, false);
|
||||
});
|
||||
|
||||
test('parseLauncherMpvConfig reads launch mode preference', () => {
|
||||
const parsed = parseLauncherMpvConfig({
|
||||
mpv: {
|
||||
launchMode: ' maximized ',
|
||||
executablePath: 'ignored-here',
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(parsed.launchMode, 'maximized');
|
||||
});
|
||||
|
||||
test('parseLauncherMpvConfig ignores invalid launch mode values', () => {
|
||||
const parsed = parseLauncherMpvConfig({
|
||||
mpv: {
|
||||
launchMode: 'wide',
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(parsed.launchMode, undefined);
|
||||
});
|
||||
|
||||
test('parsePluginRuntimeConfigContent reads socket path and startup gate options', () => {
|
||||
const parsed = parsePluginRuntimeConfigContent(`
|
||||
# comment
|
||||
|
||||
@@ -2,6 +2,7 @@ import { fail } from './log.js';
|
||||
import type {
|
||||
Args,
|
||||
LauncherJellyfinConfig,
|
||||
LauncherMpvConfig,
|
||||
LauncherYoutubeSubgenConfig,
|
||||
LogLevel,
|
||||
PluginRuntimeConfig,
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
} from './config/args-normalizer.js';
|
||||
import { parseCliPrograms, resolveTopLevelCommand } from './config/cli-parser-builder.js';
|
||||
import { parseLauncherJellyfinConfig } from './config/jellyfin-config.js';
|
||||
import { parseLauncherMpvConfig } from './config/mpv-config.js';
|
||||
import { readPluginRuntimeConfig as readPluginRuntimeConfigValue } from './config/plugin-runtime-config.js';
|
||||
import { readLauncherMainConfigObject } from './config/shared-config-reader.js';
|
||||
import { parseLauncherYoutubeSubgenConfig } from './config/youtube-subgen-config.js';
|
||||
@@ -44,6 +46,12 @@ export function loadLauncherJellyfinConfig(): LauncherJellyfinConfig {
|
||||
return parseLauncherJellyfinConfig(root);
|
||||
}
|
||||
|
||||
export function loadLauncherMpvConfig(): LauncherMpvConfig {
|
||||
const root = readLauncherMainConfigObject();
|
||||
if (!root) return {};
|
||||
return parseLauncherMpvConfig(root);
|
||||
}
|
||||
|
||||
export function hasLauncherExternalYomitanProfileConfig(): boolean {
|
||||
return readExternalYomitanProfilePath(readLauncherMainConfigObject()) !== null;
|
||||
}
|
||||
@@ -56,9 +64,10 @@ export function parseArgs(
|
||||
argv: string[],
|
||||
scriptName: string,
|
||||
launcherConfig: LauncherYoutubeSubgenConfig,
|
||||
launcherMpvConfig: LauncherMpvConfig = {},
|
||||
): Args {
|
||||
const topLevelCommand = resolveTopLevelCommand(argv);
|
||||
const parsed = createDefaultArgs(launcherConfig);
|
||||
const parsed = createDefaultArgs(launcherConfig, launcherMpvConfig);
|
||||
|
||||
if (topLevelCommand && (topLevelCommand.name === 'app' || topLevelCommand.name === 'bin')) {
|
||||
parsed.appPassthrough = true;
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fail } from '../log.js';
|
||||
import type { Args, Backend, LauncherYoutubeSubgenConfig, LogLevel } from '../types.js';
|
||||
import type {
|
||||
Args,
|
||||
Backend,
|
||||
LauncherMpvConfig,
|
||||
LauncherYoutubeSubgenConfig,
|
||||
LogLevel,
|
||||
} from '../types.js';
|
||||
import {
|
||||
DEFAULT_JIMAKU_API_BASE_URL,
|
||||
DEFAULT_YOUTUBE_PRIMARY_SUB_LANGS,
|
||||
@@ -83,7 +89,10 @@ function parseDictionaryTarget(value: string): string {
|
||||
return resolved;
|
||||
}
|
||||
|
||||
export function createDefaultArgs(launcherConfig: LauncherYoutubeSubgenConfig): Args {
|
||||
export function createDefaultArgs(
|
||||
launcherConfig: LauncherYoutubeSubgenConfig,
|
||||
mpvConfig: LauncherMpvConfig = {},
|
||||
): Args {
|
||||
const configuredSecondaryLangs = uniqueNormalizedLangCodes(
|
||||
launcherConfig.secondarySubLanguages ?? [],
|
||||
);
|
||||
@@ -148,6 +157,7 @@ export function createDefaultArgs(launcherConfig: LauncherYoutubeSubgenConfig):
|
||||
jellyfinServer: '',
|
||||
jellyfinUsername: '',
|
||||
jellyfinPassword: '',
|
||||
launchMode: mpvConfig.launchMode ?? 'normal',
|
||||
youtubePrimarySubLangs: primarySubLangs,
|
||||
youtubeSecondarySubLangs: secondarySubLangs,
|
||||
youtubeAudioLangs,
|
||||
|
||||
12
launcher/config/mpv-config.ts
Normal file
12
launcher/config/mpv-config.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { parseMpvLaunchMode } from '../../src/shared/mpv-launch-mode.js';
|
||||
import type { LauncherMpvConfig } from '../types.js';
|
||||
|
||||
export function parseLauncherMpvConfig(root: Record<string, unknown>): LauncherMpvConfig {
|
||||
const mpvRaw = root.mpv;
|
||||
if (!mpvRaw || typeof mpvRaw !== 'object') return {};
|
||||
const mpv = mpvRaw as Record<string, unknown>;
|
||||
|
||||
return {
|
||||
launchMode: parseMpvLaunchMode(mpv.launchMode),
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import path from 'node:path';
|
||||
import {
|
||||
loadLauncherJellyfinConfig,
|
||||
loadLauncherMpvConfig,
|
||||
loadLauncherYoutubeSubgenConfig,
|
||||
parseArgs,
|
||||
readPluginRuntimeConfig,
|
||||
@@ -52,7 +53,8 @@ async function main(): Promise<void> {
|
||||
const scriptPath = process.argv[1] || 'subminer';
|
||||
const scriptName = path.basename(scriptPath);
|
||||
const launcherConfig = loadLauncherYoutubeSubgenConfig();
|
||||
const args = parseArgs(process.argv.slice(2), scriptName, launcherConfig);
|
||||
const launcherMpvConfig = loadLauncherMpvConfig();
|
||||
const args = parseArgs(process.argv.slice(2), scriptName, launcherConfig, launcherMpvConfig);
|
||||
const pluginRuntimeConfig = readPluginRuntimeConfig(args.logLevel);
|
||||
const appPath = findAppBinary(scriptPath);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import net from 'node:net';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import type { Args } from './types';
|
||||
import {
|
||||
buildConfiguredMpvDefaultArgs,
|
||||
buildMpvBackendArgs,
|
||||
buildMpvEnv,
|
||||
cleanupPlaybackSession,
|
||||
@@ -234,6 +235,33 @@ test('buildMpvBackendArgs keeps supported Hyprland and Sway auto backends unchan
|
||||
});
|
||||
});
|
||||
|
||||
test('buildConfiguredMpvDefaultArgs appends maximized launch mode to configured defaults', () => {
|
||||
withPlatform('linux', () => {
|
||||
assert.deepEqual(
|
||||
buildConfiguredMpvDefaultArgs(makeArgs({ launchMode: 'maximized' }), {
|
||||
DISPLAY: ':1',
|
||||
WAYLAND_DISPLAY: 'wayland-0',
|
||||
XDG_SESSION_TYPE: 'wayland',
|
||||
XDG_CURRENT_DESKTOP: 'KDE',
|
||||
XDG_SESSION_DESKTOP: 'plasma',
|
||||
}),
|
||||
[
|
||||
'--sub-auto=fuzzy',
|
||||
'--sub-file-paths=.;subs;subtitles',
|
||||
'--sid=auto',
|
||||
'--secondary-sid=auto',
|
||||
'--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',
|
||||
'--vo=gpu',
|
||||
'--gpu-api=opengl',
|
||||
'--gpu-context=x11egl,x11',
|
||||
'--window-maximized=yes',
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('launchTexthookerOnly exits non-zero when app binary cannot be spawned', () => {
|
||||
const error = withProcessExitIntercept(() => {
|
||||
launchTexthookerOnly('/definitely-missing-subminer-binary', makeArgs());
|
||||
@@ -401,6 +429,7 @@ function makeArgs(overrides: Partial<Args> = {}): Args {
|
||||
jellyfinServer: '',
|
||||
jellyfinUsername: '',
|
||||
jellyfinPassword: '',
|
||||
launchMode: 'normal',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import net from 'node:net';
|
||||
import { spawn, spawnSync } from 'node:child_process';
|
||||
import { buildMpvLaunchModeArgs } from '../src/shared/mpv-launch-mode.js';
|
||||
import type { LogLevel, Backend, Args, MpvTrack } from './types.js';
|
||||
import { DEFAULT_MPV_SUBMINER_ARGS, DEFAULT_YOUTUBE_YTDL_FORMAT } from './types.js';
|
||||
import { appendToAppLog, getAppLogPath, log, fail, getMpvLogPath } from './log.js';
|
||||
@@ -673,9 +674,7 @@ export async function startMpv(
|
||||
}
|
||||
|
||||
const mpvArgs: string[] = [];
|
||||
if (args.profile) mpvArgs.push(`--profile=${args.profile}`);
|
||||
mpvArgs.push(...DEFAULT_MPV_SUBMINER_ARGS);
|
||||
mpvArgs.push(...buildMpvBackendArgs(args));
|
||||
mpvArgs.push(...buildConfiguredMpvDefaultArgs(args));
|
||||
if (targetKind === 'url' && isYoutubeTarget(target)) {
|
||||
log('info', args.logLevel, 'Applying URL playback options');
|
||||
mpvArgs.push('--ytdl=yes');
|
||||
@@ -703,7 +702,6 @@ export async function startMpv(
|
||||
if (args.mpvArgs) {
|
||||
mpvArgs.push(...parseMpvArgString(args.mpvArgs));
|
||||
}
|
||||
|
||||
if (preloadedSubtitles?.primaryPath) {
|
||||
mpvArgs.push(`--sub-file=${preloadedSubtitles.primaryPath}`);
|
||||
}
|
||||
@@ -979,6 +977,18 @@ export function buildMpvBackendArgs(
|
||||
return ['--vo=gpu', '--gpu-api=opengl', '--gpu-context=x11egl,x11'];
|
||||
}
|
||||
|
||||
export function buildConfiguredMpvDefaultArgs(
|
||||
args: Pick<Args, 'profile' | 'backend' | 'launchMode'>,
|
||||
baseEnv: NodeJS.ProcessEnv = process.env,
|
||||
): string[] {
|
||||
const mpvArgs: string[] = [];
|
||||
if (args.profile) mpvArgs.push(`--profile=${args.profile}`);
|
||||
mpvArgs.push(...DEFAULT_MPV_SUBMINER_ARGS);
|
||||
mpvArgs.push(...buildMpvBackendArgs(args, baseEnv));
|
||||
mpvArgs.push(...buildMpvLaunchModeArgs(args.launchMode));
|
||||
return mpvArgs;
|
||||
}
|
||||
|
||||
function appendCapturedAppOutput(kind: 'STDOUT' | 'STDERR', chunk: string): void {
|
||||
const normalized = chunk.replace(/\r\n/g, '\n');
|
||||
for (const line of normalized.split('\n')) {
|
||||
@@ -1209,10 +1219,7 @@ export function launchMpvIdleDetached(
|
||||
// ignore
|
||||
}
|
||||
|
||||
const mpvArgs: string[] = [];
|
||||
if (args.profile) mpvArgs.push(`--profile=${args.profile}`);
|
||||
mpvArgs.push(...DEFAULT_MPV_SUBMINER_ARGS);
|
||||
mpvArgs.push(...buildMpvBackendArgs(args));
|
||||
const mpvArgs: string[] = buildConfiguredMpvDefaultArgs(args);
|
||||
if (args.mpvArgs) {
|
||||
mpvArgs.push(...parseMpvArgString(args.mpvArgs));
|
||||
}
|
||||
|
||||
@@ -85,6 +85,12 @@ test('parseArgs maps mpv idle action', () => {
|
||||
assert.equal(parsed.mpvStatus, false);
|
||||
});
|
||||
|
||||
test('parseArgs applies configured mpv launch mode default', () => {
|
||||
const parsed = parseArgs([], 'subminer', {}, { launchMode: 'maximized' });
|
||||
|
||||
assert.equal(parsed.launchMode, 'maximized');
|
||||
});
|
||||
|
||||
test('parseArgs maps dictionary command and log-level override', () => {
|
||||
const parsed = parseArgs(['dictionary', '.', '--log-level', 'debug'], 'subminer', {});
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import type { MpvLaunchMode } from '../src/types/config.js';
|
||||
import { resolveDefaultLogFilePath } from '../src/shared/log-files.js';
|
||||
export { VIDEO_EXTENSIONS } from '../src/shared/video-extensions.js';
|
||||
|
||||
@@ -140,6 +141,7 @@ export interface Args {
|
||||
jellyfinServer: string;
|
||||
jellyfinUsername: string;
|
||||
jellyfinPassword: string;
|
||||
launchMode: MpvLaunchMode;
|
||||
}
|
||||
|
||||
export interface LauncherYoutubeSubgenConfig {
|
||||
@@ -167,6 +169,10 @@ export interface LauncherJellyfinConfig {
|
||||
iconCacheDir?: string;
|
||||
}
|
||||
|
||||
export interface LauncherMpvConfig {
|
||||
launchMode?: MpvLaunchMode;
|
||||
}
|
||||
|
||||
export interface PluginRuntimeConfig {
|
||||
socketPath: string;
|
||||
autoStart: boolean;
|
||||
|
||||
Reference in New Issue
Block a user