fix: always hide mpv primary subtitles for visible overlay

This commit is contained in:
2026-02-27 18:32:29 -08:00
parent dde51f8634
commit 9e4e588f33
22 changed files with 153 additions and 108 deletions

View File

@@ -100,6 +100,12 @@ Enable automatic Anki card creation and updates with media generation:
"enabled": true,
"url": "http://127.0.0.1:8765",
"pollingRate": 3000,
"proxy": {
"enabled": false,
"host": "127.0.0.1",
"port": 8766,
"upstreamUrl": "http://127.0.0.1:8765"
},
"tags": ["SubMiner"],
"deck": "Learning::Japanese",
"fields": {
@@ -163,7 +169,11 @@ This example is intentionally compact. The option table below documents availabl
| --------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `enabled` | `true`, `false` | Enable AnkiConnect integration (default: `false`) |
| `url` | string (URL) | AnkiConnect API URL (default: `http://127.0.0.1:8765`) |
| `pollingRate` | number (ms) | How often to check for new cards (default: `3000`) |
| `pollingRate` | number (ms) | How often to check for new cards in polling mode (default: `3000`; ignored for direct proxy `addNote`/`addNotes` updates) |
| `proxy.enabled` | `true`, `false` | Enable local AnkiConnect-compatible proxy for push-based auto-enrichment (default: `false`) |
| `proxy.host` | string | Bind host for local AnkiConnect proxy (default: `127.0.0.1`) |
| `proxy.port` | number | Bind port for local AnkiConnect proxy (default: `8766`) |
| `proxy.upstreamUrl` | string (URL) | Upstream AnkiConnect URL that proxy forwards to (default: `http://127.0.0.1:8765`) |
| `tags` | array of strings | Tags automatically added to cards mined/updated by SubMiner (default: `['SubMiner']`; set `[]` to disable automatic tagging). |
| `deck` | string | Anki deck to monitor for new cards |
| `ankiConnect.nPlusOne.decks` | array of strings | Decks used for N+1 known-word cache lookups. When omitted/empty, falls back to `ankiConnect.deck`. |
@@ -340,20 +350,6 @@ Control whether the overlay automatically becomes visible when it connects to mp
The mpv plugin controls startup overlay visibility via `auto_start_visible_overlay` in `subminer.conf`.
### Visible Overlay Subtitle Binding
Control whether toggling the visible overlay also toggles MPV subtitle visibility:
```json
{
"bind_visible_overlay_to_mpv_sub_visibility": true
}
```
| Option | Values | Description |
| -------------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bind_visible_overlay_to_mpv_sub_visibility` | `true`, `false` | When `true` (default), visible overlay hides MPV primary/secondary subtitles and restores them when hidden. When `false`, visible overlay toggles do not change MPV subtitle visibility. |
### Auto Subtitle Sync
Sync the active subtitle track using `alass` (preferred) or `ffsubsync`:

View File

@@ -650,7 +650,7 @@ test('warns and ignores unknown top-level config keys', () => {
assert.ok(warnings.some((warning) => warning.path === 'unknownFeatureFlag'));
});
test('parses global shortcuts and startup visibility flags', () => {
test('parses global shortcuts and startup settings', () => {
const dir = makeTempDir();
fs.writeFileSync(
path.join(dir, 'config.jsonc'),
@@ -659,7 +659,6 @@ test('parses global shortcuts and startup visibility flags', () => {
"toggleVisibleOverlayGlobal": "Alt+Shift+U",
"openJimaku": "Ctrl+Alt+J"
},
"bind_visible_overlay_to_mpv_sub_visibility": false,
"youtubeSubgen": {
"primarySubLanguages": ["ja", "jpn", "jp"]
}
@@ -671,7 +670,6 @@ test('parses global shortcuts and startup visibility flags', () => {
const config = service.getConfig();
assert.equal(config.shortcuts.toggleVisibleOverlayGlobal, 'Alt+Shift+U');
assert.equal(config.shortcuts.openJimaku, 'Ctrl+Alt+J');
assert.equal(config.bind_visible_overlay_to_mpv_sub_visibility, false);
assert.deepEqual(config.youtubeSubgen.primarySubLanguages, ['ja', 'jpn', 'jp']);
});

View File

@@ -28,7 +28,6 @@ const {
secondarySub,
subsync,
auto_start_overlay,
bind_visible_overlay_to_mpv_sub_visibility,
} = CORE_DEFAULT_CONFIG;
const { ankiConnect, jimaku, anilist, jellyfin, discordPresence, youtubeSubgen } =
INTEGRATIONS_DEFAULT_CONFIG;
@@ -47,7 +46,6 @@ export const DEFAULT_CONFIG: ResolvedConfig = {
subsync,
subtitleStyle,
auto_start_overlay,
bind_visible_overlay_to_mpv_sub_visibility,
jimaku,
anilist,
jellyfin,

View File

@@ -11,7 +11,6 @@ export const CORE_DEFAULT_CONFIG: Pick<
| 'secondarySub'
| 'subsync'
| 'auto_start_overlay'
| 'bind_visible_overlay_to_mpv_sub_visibility'
> = {
subtitlePosition: { yPercent: 10 },
keybindings: [],
@@ -52,5 +51,4 @@ export const CORE_DEFAULT_CONFIG: Pick<
ffmpeg_path: '',
},
auto_start_overlay: false,
bind_visible_overlay_to_mpv_sub_visibility: true,
};

View File

@@ -38,12 +38,5 @@ export function buildCoreConfigOptionRegistry(
defaultValue: defaultConfig.shortcuts.multiCopyTimeoutMs,
description: 'Timeout for multi-copy/mine modes.',
},
{
path: 'bind_visible_overlay_to_mpv_sub_visibility',
kind: 'boolean',
defaultValue: defaultConfig.bind_visible_overlay_to_mpv_sub_visibility,
description:
'Link visible overlay toggles to MPV primary subtitle visibility.',
},
];
}

View File

@@ -8,14 +8,6 @@ const CORE_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
],
key: 'auto_start_overlay',
},
{
title: 'Visible Overlay Subtitle Binding',
description: [
'Control whether visible overlay toggles also toggle MPV subtitle visibility.',
'When enabled, visible overlay hides MPV subtitles; when disabled, MPV subtitles are left unchanged.',
],
key: 'bind_visible_overlay_to_mpv_sub_visibility',
},
{
title: 'Texthooker Server',
description: ['Control whether browser opens automatically for texthooker.'],

View File

@@ -13,16 +13,4 @@ export function applyTopLevelConfig(context: ResolveContext): void {
if (asBoolean(src.auto_start_overlay) !== undefined) {
resolved.auto_start_overlay = src.auto_start_overlay as boolean;
}
if (asBoolean(src.bind_visible_overlay_to_mpv_sub_visibility) !== undefined) {
resolved.bind_visible_overlay_to_mpv_sub_visibility =
src.bind_visible_overlay_to_mpv_sub_visibility as boolean;
} else if (src.bind_visible_overlay_to_mpv_sub_visibility !== undefined) {
warn(
'bind_visible_overlay_to_mpv_sub_visibility',
src.bind_visible_overlay_to_mpv_sub_visibility,
resolved.bind_visible_overlay_to_mpv_sub_visibility,
'Expected boolean.',
);
}
}

View File

@@ -25,10 +25,10 @@ export { cycleSecondarySubMode } from './subtitle-position';
export {
isAutoUpdateEnabledRuntime,
shouldAutoInitializeOverlayRuntimeFromConfig,
shouldBindVisibleOverlayToMpvSubVisibility,
} from './startup';
export { openYomitanSettingsWindow } from './yomitan-settings';
export { createTokenizerDepsRuntime, tokenizeSubtitle } from './tokenizer';
export { syncYomitanDefaultAnkiServer } from './tokenizer/yomitan-parser-runtime';
export { createSubtitleProcessingController } from './subtitle-processing-controller';
export { createFrequencyDictionaryLookup } from './frequency-dictionary';
export { createJlptVocabularyLookup } from './jlpt-vocab';

View File

@@ -121,7 +121,6 @@ test('dispatchMpvProtocolMessage emits subtitle text on property change', async
test('dispatchMpvProtocolMessage enforces sub-visibility hidden when overlay suppression is enabled', async () => {
const { deps, state } = createDeps({
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
isVisibleOverlayVisible: () => true,
});
@@ -130,15 +129,22 @@ test('dispatchMpvProtocolMessage enforces sub-visibility hidden when overlay sup
deps,
);
assert.deepEqual(state.commands.pop(), {
command: ['set_property', 'sub-visibility', 'no'],
});
assert.deepEqual(state.commands, [
{
command: ['set_property', 'sub-visibility', false],
},
{
command: ['set_property', 'sub-visibility', 'no'],
},
{
command: ['set', 'sub-visibility', 'no'],
},
]);
});
test('dispatchMpvProtocolMessage skips sub-visibility suppression when overlay binding is disabled', async () => {
test('dispatchMpvProtocolMessage skips sub-visibility suppression when overlay is hidden', async () => {
const { deps, state } = createDeps({
shouldBindVisibleOverlayToMpvSubVisibility: () => false,
isVisibleOverlayVisible: () => true,
isVisibleOverlayVisible: () => false,
});
await dispatchMpvProtocolMessage(

View File

@@ -48,7 +48,6 @@ export interface MpvProtocolHandleMessageDeps {
};
getSubtitleMetrics: () => MpvSubtitleRenderMetrics;
isVisibleOverlayVisible: () => boolean;
shouldBindVisibleOverlayToMpvSubVisibility?: () => boolean;
emitSubtitleChange: (payload: { text: string; isOverlayVisible: boolean }) => void;
emitSubtitleAssChange: (payload: { text: string }) => void;
emitSubtitleTiming: (payload: { text: string; start: number; end: number }) => void;
@@ -218,12 +217,10 @@ export async function dispatchMpvProtocolMessage(
subScaleByWindow: asBoolean(msg.data, deps.getSubtitleMetrics().subScaleByWindow),
});
} else if (msg.name === 'sub-visibility') {
if (
deps.isVisibleOverlayVisible() &&
asBoolean(msg.data, false) &&
(deps.shouldBindVisibleOverlayToMpvSubVisibility?.() ?? true)
) {
if (deps.isVisibleOverlayVisible() && asBoolean(msg.data, false)) {
deps.sendCommand({ command: ['set_property', 'sub-visibility', false] });
deps.sendCommand({ command: ['set_property', 'sub-visibility', 'no'] });
deps.sendCommand({ command: ['set', 'sub-visibility', 'no'] });
}
} else if (msg.name === 'sub-use-margins') {
deps.emitSubtitleMetricsChange({

View File

@@ -13,7 +13,6 @@ function makeDeps(overrides: Partial<MpvIpcClientProtocolDeps> = {}): MpvIpcClie
getResolvedConfig: () => ({}) as any,
autoStartOverlay: false,
setOverlayVisible: () => {},
shouldBindVisibleOverlayToMpvSubVisibility: () => false,
isVisibleOverlayVisible: () => false,
getReconnectTimer: () => null,
setReconnectTimer: () => {},
@@ -311,7 +310,6 @@ test('MpvIpcClient connect does not force primary subtitle visibility from bindi
const client = new MpvIpcClient(
'/tmp/mpv.sock',
makeDeps({
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
isVisibleOverlayVisible: () => true,
}),
);
@@ -332,6 +330,29 @@ test('MpvIpcClient connect does not force primary subtitle visibility from bindi
assert.equal(hasPrimaryVisibilityMutation, false);
});
test('MpvIpcClient setSubVisibility writes compatibility commands for visibility toggle', () => {
const commands: unknown[] = [];
const client = new MpvIpcClient('/tmp/mpv.sock', makeDeps());
(client as any).send = (payload: unknown) => {
commands.push(payload);
return true;
};
client.setSubVisibility(false);
assert.deepEqual(commands, [
{
command: ['set_property', 'sub-visibility', false],
},
{
command: ['set_property', 'sub-visibility', 'no'],
},
{
command: ['set', 'sub-visibility', 'no'],
},
]);
});
test('MpvIpcClient captures and disables secondary subtitle visibility on request', async () => {
const commands: unknown[] = [];
const client = new MpvIpcClient('/tmp/mpv.sock', makeDeps());

View File

@@ -99,7 +99,6 @@ export interface MpvIpcClientProtocolDeps {
getResolvedConfig: () => Config;
autoStartOverlay: boolean;
setOverlayVisible: (visible: boolean) => void;
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
isVisibleOverlayVisible: () => boolean;
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
@@ -297,8 +296,6 @@ export class MpvIpcClient implements MpvClient {
getResolvedConfig: () => this.deps.getResolvedConfig(),
getSubtitleMetrics: () => this.mpvSubtitleRenderMetrics,
isVisibleOverlayVisible: () => this.deps.isVisibleOverlayVisible(),
shouldBindVisibleOverlayToMpvSubVisibility: () =>
this.deps.shouldBindVisibleOverlayToMpvSubVisibility(),
emitSubtitleChange: (payload) => {
this.emit('subtitle-change', payload);
},
@@ -474,6 +471,9 @@ export class MpvIpcClient implements MpvClient {
setSubVisibility(visible: boolean): void {
const value = visible ? 'yes' : 'no';
this.send({
command: ['set_property', 'sub-visibility', visible],
});
this.send({
command: ['set_property', 'sub-visibility', value],
});

View File

@@ -3,12 +3,10 @@ import assert from 'node:assert/strict';
import {
isAutoUpdateEnabledRuntime,
shouldAutoInitializeOverlayRuntimeFromConfig,
shouldBindVisibleOverlayToMpvSubVisibility,
} from './startup';
const BASE_CONFIG = {
auto_start_overlay: false,
bind_visible_overlay_to_mpv_sub_visibility: true,
ankiConnect: {
behavior: {
autoUpdateNewCards: true,
@@ -27,17 +25,6 @@ test('shouldAutoInitializeOverlayRuntimeFromConfig respects auto start', () => {
);
});
test('shouldBindVisibleOverlayToMpvSubVisibility returns config value', () => {
assert.equal(shouldBindVisibleOverlayToMpvSubVisibility(BASE_CONFIG), true);
assert.equal(
shouldBindVisibleOverlayToMpvSubVisibility({
...BASE_CONFIG,
bind_visible_overlay_to_mpv_sub_visibility: false,
}),
false,
);
});
test('isAutoUpdateEnabledRuntime prefers runtime option and falls back to config', () => {
assert.equal(
isAutoUpdateEnabledRuntime(BASE_CONFIG, {

View File

@@ -18,7 +18,6 @@ interface RuntimeAutoUpdateOptionManagerLike {
export interface RuntimeConfigLike {
auto_start_overlay?: boolean;
bind_visible_overlay_to_mpv_sub_visibility: boolean;
ankiConnect?: {
behavior?: {
autoUpdateNewCards?: boolean;
@@ -156,10 +155,6 @@ export function shouldAutoInitializeOverlayRuntimeFromConfig(config: RuntimeConf
return config.auto_start_overlay === true;
}
export function shouldBindVisibleOverlayToMpvSubVisibility(config: RuntimeConfigLike): boolean {
return config.bind_visible_overlay_to_mpv_sub_visibility;
}
export function isAutoUpdateEnabledRuntime(
config: ResolvedConfig | RuntimeConfigLike,
runtimeOptionsManager: RuntimeAutoUpdateOptionManagerLike | null,

View File

@@ -106,6 +106,7 @@ import type { CliArgs, CliCommandSource } from './cli/args';
import { printHelp } from './cli/help';
import {
buildConfigParseErrorDetails,
buildConfigWarningDialogDetails,
buildConfigWarningNotificationBody,
failStartupFromConfig,
} from './main/config-validation';
@@ -353,6 +354,7 @@ import {
resolveJellyfinPlaybackPlanRuntime,
runStartupBootstrapRuntime,
saveSubtitlePosition as saveSubtitlePositionCore,
syncYomitanDefaultAnkiServer as syncYomitanDefaultAnkiServerCore,
sendMpvCommandRuntime,
setMpvSubVisibilityRuntime,
setOverlayDebugVisualizationEnabledRuntime,
@@ -758,10 +760,7 @@ const restoreOverlayMpvSubtitles = createRestoreOverlayMpvSubtitlesHandler({
});
function shouldSuppressMpvSubtitlesForOverlay(): boolean {
return (
overlayManager.getVisibleOverlayVisible() &&
configDerivedRuntime.shouldBindVisibleOverlayToMpvSubVisibility()
);
return overlayManager.getVisibleOverlayVisible();
}
function syncOverlayMpvSubtitleSuppression(): void {
@@ -961,6 +960,12 @@ const buildConfigHotReloadRuntimeMainDepsHandler = createBuildConfigHotReloadRun
showDesktopNotification('SubMiner', {
body: buildConfigWarningNotificationBody(configPath, warnings),
});
if (process.platform === 'darwin') {
dialog.showErrorBox(
'SubMiner config validation warning',
buildConfigWarningDialogDetails(configPath, warnings),
);
}
},
},
);
@@ -1931,6 +1936,10 @@ const { reloadConfig: reloadConfigHandler, appReadyRuntimeRunner } = composeAppR
logInfo: (message) => appLogger.logInfo(message),
logWarning: (message) => appLogger.logWarning(message),
showDesktopNotification: (title, options) => showDesktopNotification(title, options),
showConfigWarningsDialog:
process.platform === 'darwin'
? (title, details) => dialog.showErrorBox(title, details)
: undefined,
startConfigHotReload: () => configHotReloadRuntime.start(),
refreshAnilistClientSecretState: (options) => refreshAnilistClientSecretState(options),
failHandlers: {
@@ -2203,8 +2212,6 @@ const {
getResolvedConfig: () => getResolvedConfig(),
isAutoStartOverlayEnabled: () => appState.autoStartOverlay,
setOverlayVisible: (visible: boolean) => setOverlayVisible(visible),
shouldBindVisibleOverlayToMpvSubVisibility: () =>
configDerivedRuntime.shouldBindVisibleOverlayToMpvSubVisibility(),
isVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(),
getReconnectTimer: () => appState.reconnectTimer,
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => {
@@ -2372,11 +2379,70 @@ const enforceOverlayLayerOrder = createEnforceOverlayLayerOrderHandler(
);
async function loadYomitanExtension(): Promise<Extension | null> {
return yomitanExtensionRuntime.loadYomitanExtension();
const extension = await yomitanExtensionRuntime.loadYomitanExtension();
if (extension) {
await syncYomitanDefaultProfileAnkiServer();
}
return extension;
}
async function ensureYomitanExtensionLoaded(): Promise<Extension | null> {
return yomitanExtensionRuntime.ensureYomitanExtensionLoaded();
const extension = await yomitanExtensionRuntime.ensureYomitanExtensionLoaded();
if (extension) {
await syncYomitanDefaultProfileAnkiServer();
}
return extension;
}
let lastSyncedYomitanAnkiServer: string | null = null;
function getPreferredYomitanAnkiServerUrl(): string {
const config = getResolvedConfig().ankiConnect;
if (config.proxy?.enabled) {
const host = config.proxy.host || '127.0.0.1';
const port = config.proxy.port || 8766;
return `http://${host}:${port}`;
}
return config.url;
}
async function syncYomitanDefaultProfileAnkiServer(): Promise<void> {
const targetUrl = getPreferredYomitanAnkiServerUrl().trim();
if (!targetUrl || targetUrl === lastSyncedYomitanAnkiServer) {
return;
}
const updated = await syncYomitanDefaultAnkiServerCore(
targetUrl,
{
getYomitanExt: () => appState.yomitanExt,
getYomitanParserWindow: () => appState.yomitanParserWindow,
setYomitanParserWindow: (window) => {
appState.yomitanParserWindow = window;
},
getYomitanParserReadyPromise: () => appState.yomitanParserReadyPromise,
setYomitanParserReadyPromise: (promise) => {
appState.yomitanParserReadyPromise = promise;
},
getYomitanParserInitPromise: () => appState.yomitanParserInitPromise,
setYomitanParserInitPromise: (promise) => {
appState.yomitanParserInitPromise = promise;
},
},
{
error: (message, ...args) => {
logger.error(message, ...args);
},
info: (message, ...args) => {
logger.info(message, ...args);
},
},
);
if (updated) {
logger.info(`Yomitan default profile Anki server set to ${targetUrl}`);
}
lastSyncedYomitanAnkiServer = targetUrl;
}
function createOverlayWindow(kind: 'visible' | 'modal'): BrowserWindow {
@@ -2999,20 +3065,32 @@ function ensureOverlayWindowsReadyForVisibilityActions(): void {
function setVisibleOverlayVisible(visible: boolean): void {
ensureOverlayWindowsReadyForVisibilityActions();
if (visible) {
void ensureOverlayMpvSubtitlesHidden();
}
setVisibleOverlayVisibleHandler(visible);
syncOverlayMpvSubtitleSuppression();
}
function toggleVisibleOverlay(): void {
ensureOverlayWindowsReadyForVisibilityActions();
if (!overlayManager.getVisibleOverlayVisible()) {
void ensureOverlayMpvSubtitlesHidden();
}
toggleVisibleOverlayHandler();
syncOverlayMpvSubtitleSuppression();
}
function setOverlayVisible(visible: boolean): void {
if (visible) {
void ensureOverlayMpvSubtitlesHidden();
}
setOverlayVisibleHandler(visible);
syncOverlayMpvSubtitleSuppression();
}
function toggleOverlay(): void {
if (!overlayManager.getVisibleOverlayVisible()) {
void ensureOverlayMpvSubtitlesHidden();
}
toggleOverlayHandler();
syncOverlayMpvSubtitleSuppression();
}

View File

@@ -92,7 +92,6 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
getResolvedConfig: () => ({ auto_start_overlay: false }),
isAutoStartOverlayEnabled: () => true,
setOverlayVisible: () => {},
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
isVisibleOverlayVisible: () => false,
getReconnectTimer: () => null,
setReconnectTimer: () => {},

View File

@@ -7,7 +7,6 @@ import {
jimakuFetchJson as jimakuFetchJsonCore,
resolveJimakuApiKey as resolveJimakuApiKeyCore,
shouldAutoInitializeOverlayRuntimeFromConfig as shouldAutoInitializeOverlayRuntimeFromConfigCore,
shouldBindVisibleOverlayToMpvSubVisibility as shouldBindVisibleOverlayToMpvSubVisibilityCore,
} from '../../core/services';
export type ConfigDerivedRuntimeDeps = {
@@ -20,7 +19,6 @@ export type ConfigDerivedRuntimeDeps = {
export function createConfigDerivedRuntime(deps: ConfigDerivedRuntimeDeps): {
shouldAutoInitializeOverlayRuntimeFromConfig: () => boolean;
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
isAutoUpdateEnabledRuntime: () => boolean;
getJimakuLanguagePreference: () => JimakuLanguagePreference;
getJimakuMaxEntryResults: () => number;
@@ -33,8 +31,6 @@ export function createConfigDerivedRuntime(deps: ConfigDerivedRuntimeDeps): {
return {
shouldAutoInitializeOverlayRuntimeFromConfig: () =>
shouldAutoInitializeOverlayRuntimeFromConfigCore(deps.getResolvedConfig()),
shouldBindVisibleOverlayToMpvSubVisibility: () =>
shouldBindVisibleOverlayToMpvSubVisibilityCore(deps.getResolvedConfig()),
isAutoUpdateEnabledRuntime: () =>
isAutoUpdateEnabledRuntimeCore(deps.getResolvedConfig(), deps.getRuntimeOptionsManager()),
getJimakuLanguagePreference: () =>

View File

@@ -16,7 +16,6 @@ test('mpv runtime service main deps builder maps state and callbacks', () => {
getResolvedConfig: () => ({ mode: 'test' }),
isAutoStartOverlayEnabled: () => true,
setOverlayVisible: (visible) => calls.push(`overlay:${visible}`),
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
isVisibleOverlayVisible: () => false,
getReconnectTimer: () => reconnectTimer,
setReconnectTimer: (timer) => {
@@ -29,7 +28,6 @@ test('mpv runtime service main deps builder maps state and callbacks', () => {
const deps = build();
assert.equal(deps.socketPath, '/tmp/mpv.sock');
assert.equal(deps.options.autoStartOverlay, true);
assert.equal(deps.options.shouldBindVisibleOverlayToMpvSubVisibility(), true);
assert.equal(deps.options.isVisibleOverlayVisible(), false);
assert.deepEqual(deps.options.getResolvedConfig(), { mode: 'test' });

View File

@@ -8,7 +8,6 @@ export function createBuildMpvClientRuntimeServiceFactoryDepsHandler<
getResolvedConfig: () => TResolvedConfig;
isAutoStartOverlayEnabled: () => boolean;
setOverlayVisible: (visible: boolean) => void;
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
isVisibleOverlayVisible: () => boolean;
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
@@ -21,8 +20,6 @@ export function createBuildMpvClientRuntimeServiceFactoryDepsHandler<
getResolvedConfig: () => deps.getResolvedConfig(),
autoStartOverlay: deps.isAutoStartOverlayEnabled(),
setOverlayVisible: (visible: boolean) => deps.setOverlayVisible(visible),
shouldBindVisibleOverlayToMpvSubVisibility: () =>
deps.shouldBindVisibleOverlayToMpvSubVisibility(),
isVisibleOverlayVisible: () => deps.isVisibleOverlayVisible(),
getReconnectTimer: () => deps.getReconnectTimer(),
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => deps.setReconnectTimer(timer),

View File

@@ -23,7 +23,6 @@ test('mpv runtime service factory constructs client, binds handlers, and connect
getResolvedConfig: () => ({}),
autoStartOverlay: true,
setOverlayVisible: () => {},
shouldBindVisibleOverlayToMpvSubVisibility: () => false,
isVisibleOverlayVisible: () => false,
getReconnectTimer: () => null,
setReconnectTimer: () => {},

View File

@@ -4,7 +4,6 @@ export type MpvClientRuntimeServiceOptions = {
getResolvedConfig: () => Config;
autoStartOverlay: boolean;
setOverlayVisible: (visible: boolean) => void;
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
isVisibleOverlayVisible: () => boolean;
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;

View File

@@ -192,6 +192,12 @@ export interface AnkiConnectConfig {
enabled?: boolean;
url?: string;
pollingRate?: number;
proxy?: {
enabled?: boolean;
host?: string;
port?: number;
upstreamUrl?: string;
};
tags?: string[];
fields?: {
audio?: string;
@@ -413,7 +419,6 @@ export interface Config {
subsync?: SubsyncConfig;
subtitleStyle?: SubtitleStyleConfig;
auto_start_overlay?: boolean;
bind_visible_overlay_to_mpv_sub_visibility?: boolean;
jimaku?: JimakuConfig;
anilist?: AnilistConfig;
jellyfin?: JellyfinConfig;
@@ -436,6 +441,12 @@ export interface ResolvedConfig {
enabled: boolean;
url: string;
pollingRate: number;
proxy: {
enabled: boolean;
host: string;
port: number;
upstreamUrl: string;
};
tags: string[];
fields: {
audio: string;
@@ -514,7 +525,6 @@ export interface ResolvedConfig {
};
};
auto_start_overlay: boolean;
bind_visible_overlay_to_mpv_sub_visibility: boolean;
jimaku: JimakuConfig & {
apiBaseUrl: string;
languagePreference: JimakuLanguagePreference;