mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
fix: always hide mpv primary subtitles for visible overlay
This commit is contained in:
@@ -100,6 +100,12 @@ Enable automatic Anki card creation and updates with media generation:
|
|||||||
"enabled": true,
|
"enabled": true,
|
||||||
"url": "http://127.0.0.1:8765",
|
"url": "http://127.0.0.1:8765",
|
||||||
"pollingRate": 3000,
|
"pollingRate": 3000,
|
||||||
|
"proxy": {
|
||||||
|
"enabled": false,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"port": 8766,
|
||||||
|
"upstreamUrl": "http://127.0.0.1:8765"
|
||||||
|
},
|
||||||
"tags": ["SubMiner"],
|
"tags": ["SubMiner"],
|
||||||
"deck": "Learning::Japanese",
|
"deck": "Learning::Japanese",
|
||||||
"fields": {
|
"fields": {
|
||||||
@@ -163,7 +169,11 @@ This example is intentionally compact. The option table below documents availabl
|
|||||||
| --------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
| --------------------------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `enabled` | `true`, `false` | Enable AnkiConnect integration (default: `false`) |
|
| `enabled` | `true`, `false` | Enable AnkiConnect integration (default: `false`) |
|
||||||
| `url` | string (URL) | AnkiConnect API URL (default: `http://127.0.0.1:8765`) |
|
| `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). |
|
| `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 |
|
| `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`. |
|
| `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`.
|
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
|
### Auto Subtitle Sync
|
||||||
|
|
||||||
Sync the active subtitle track using `alass` (preferred) or `ffsubsync`:
|
Sync the active subtitle track using `alass` (preferred) or `ffsubsync`:
|
||||||
|
|||||||
@@ -650,7 +650,7 @@ test('warns and ignores unknown top-level config keys', () => {
|
|||||||
assert.ok(warnings.some((warning) => warning.path === 'unknownFeatureFlag'));
|
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();
|
const dir = makeTempDir();
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
path.join(dir, 'config.jsonc'),
|
path.join(dir, 'config.jsonc'),
|
||||||
@@ -659,7 +659,6 @@ test('parses global shortcuts and startup visibility flags', () => {
|
|||||||
"toggleVisibleOverlayGlobal": "Alt+Shift+U",
|
"toggleVisibleOverlayGlobal": "Alt+Shift+U",
|
||||||
"openJimaku": "Ctrl+Alt+J"
|
"openJimaku": "Ctrl+Alt+J"
|
||||||
},
|
},
|
||||||
"bind_visible_overlay_to_mpv_sub_visibility": false,
|
|
||||||
"youtubeSubgen": {
|
"youtubeSubgen": {
|
||||||
"primarySubLanguages": ["ja", "jpn", "jp"]
|
"primarySubLanguages": ["ja", "jpn", "jp"]
|
||||||
}
|
}
|
||||||
@@ -671,7 +670,6 @@ test('parses global shortcuts and startup visibility flags', () => {
|
|||||||
const config = service.getConfig();
|
const config = service.getConfig();
|
||||||
assert.equal(config.shortcuts.toggleVisibleOverlayGlobal, 'Alt+Shift+U');
|
assert.equal(config.shortcuts.toggleVisibleOverlayGlobal, 'Alt+Shift+U');
|
||||||
assert.equal(config.shortcuts.openJimaku, 'Ctrl+Alt+J');
|
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']);
|
assert.deepEqual(config.youtubeSubgen.primarySubLanguages, ['ja', 'jpn', 'jp']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ const {
|
|||||||
secondarySub,
|
secondarySub,
|
||||||
subsync,
|
subsync,
|
||||||
auto_start_overlay,
|
auto_start_overlay,
|
||||||
bind_visible_overlay_to_mpv_sub_visibility,
|
|
||||||
} = CORE_DEFAULT_CONFIG;
|
} = CORE_DEFAULT_CONFIG;
|
||||||
const { ankiConnect, jimaku, anilist, jellyfin, discordPresence, youtubeSubgen } =
|
const { ankiConnect, jimaku, anilist, jellyfin, discordPresence, youtubeSubgen } =
|
||||||
INTEGRATIONS_DEFAULT_CONFIG;
|
INTEGRATIONS_DEFAULT_CONFIG;
|
||||||
@@ -47,7 +46,6 @@ export const DEFAULT_CONFIG: ResolvedConfig = {
|
|||||||
subsync,
|
subsync,
|
||||||
subtitleStyle,
|
subtitleStyle,
|
||||||
auto_start_overlay,
|
auto_start_overlay,
|
||||||
bind_visible_overlay_to_mpv_sub_visibility,
|
|
||||||
jimaku,
|
jimaku,
|
||||||
anilist,
|
anilist,
|
||||||
jellyfin,
|
jellyfin,
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ export const CORE_DEFAULT_CONFIG: Pick<
|
|||||||
| 'secondarySub'
|
| 'secondarySub'
|
||||||
| 'subsync'
|
| 'subsync'
|
||||||
| 'auto_start_overlay'
|
| 'auto_start_overlay'
|
||||||
| 'bind_visible_overlay_to_mpv_sub_visibility'
|
|
||||||
> = {
|
> = {
|
||||||
subtitlePosition: { yPercent: 10 },
|
subtitlePosition: { yPercent: 10 },
|
||||||
keybindings: [],
|
keybindings: [],
|
||||||
@@ -52,5 +51,4 @@ export const CORE_DEFAULT_CONFIG: Pick<
|
|||||||
ffmpeg_path: '',
|
ffmpeg_path: '',
|
||||||
},
|
},
|
||||||
auto_start_overlay: false,
|
auto_start_overlay: false,
|
||||||
bind_visible_overlay_to_mpv_sub_visibility: true,
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,12 +38,5 @@ export function buildCoreConfigOptionRegistry(
|
|||||||
defaultValue: defaultConfig.shortcuts.multiCopyTimeoutMs,
|
defaultValue: defaultConfig.shortcuts.multiCopyTimeoutMs,
|
||||||
description: 'Timeout for multi-copy/mine modes.',
|
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.',
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,14 +8,6 @@ const CORE_TEMPLATE_SECTIONS: ConfigTemplateSection[] = [
|
|||||||
],
|
],
|
||||||
key: 'auto_start_overlay',
|
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',
|
title: 'Texthooker Server',
|
||||||
description: ['Control whether browser opens automatically for texthooker.'],
|
description: ['Control whether browser opens automatically for texthooker.'],
|
||||||
|
|||||||
@@ -13,16 +13,4 @@ export function applyTopLevelConfig(context: ResolveContext): void {
|
|||||||
if (asBoolean(src.auto_start_overlay) !== undefined) {
|
if (asBoolean(src.auto_start_overlay) !== undefined) {
|
||||||
resolved.auto_start_overlay = src.auto_start_overlay as boolean;
|
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.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ export { cycleSecondarySubMode } from './subtitle-position';
|
|||||||
export {
|
export {
|
||||||
isAutoUpdateEnabledRuntime,
|
isAutoUpdateEnabledRuntime,
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfig,
|
shouldAutoInitializeOverlayRuntimeFromConfig,
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility,
|
|
||||||
} from './startup';
|
} from './startup';
|
||||||
export { openYomitanSettingsWindow } from './yomitan-settings';
|
export { openYomitanSettingsWindow } from './yomitan-settings';
|
||||||
export { createTokenizerDepsRuntime, tokenizeSubtitle } from './tokenizer';
|
export { createTokenizerDepsRuntime, tokenizeSubtitle } from './tokenizer';
|
||||||
|
export { syncYomitanDefaultAnkiServer } from './tokenizer/yomitan-parser-runtime';
|
||||||
export { createSubtitleProcessingController } from './subtitle-processing-controller';
|
export { createSubtitleProcessingController } from './subtitle-processing-controller';
|
||||||
export { createFrequencyDictionaryLookup } from './frequency-dictionary';
|
export { createFrequencyDictionaryLookup } from './frequency-dictionary';
|
||||||
export { createJlptVocabularyLookup } from './jlpt-vocab';
|
export { createJlptVocabularyLookup } from './jlpt-vocab';
|
||||||
|
|||||||
@@ -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 () => {
|
test('dispatchMpvProtocolMessage enforces sub-visibility hidden when overlay suppression is enabled', async () => {
|
||||||
const { deps, state } = createDeps({
|
const { deps, state } = createDeps({
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
|
||||||
isVisibleOverlayVisible: () => true,
|
isVisibleOverlayVisible: () => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -130,15 +129,22 @@ test('dispatchMpvProtocolMessage enforces sub-visibility hidden when overlay sup
|
|||||||
deps,
|
deps,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(state.commands.pop(), {
|
assert.deepEqual(state.commands, [
|
||||||
command: ['set_property', 'sub-visibility', 'no'],
|
{
|
||||||
});
|
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({
|
const { deps, state } = createDeps({
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => false,
|
isVisibleOverlayVisible: () => false,
|
||||||
isVisibleOverlayVisible: () => true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await dispatchMpvProtocolMessage(
|
await dispatchMpvProtocolMessage(
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export interface MpvProtocolHandleMessageDeps {
|
|||||||
};
|
};
|
||||||
getSubtitleMetrics: () => MpvSubtitleRenderMetrics;
|
getSubtitleMetrics: () => MpvSubtitleRenderMetrics;
|
||||||
isVisibleOverlayVisible: () => boolean;
|
isVisibleOverlayVisible: () => boolean;
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility?: () => boolean;
|
|
||||||
emitSubtitleChange: (payload: { text: string; isOverlayVisible: boolean }) => void;
|
emitSubtitleChange: (payload: { text: string; isOverlayVisible: boolean }) => void;
|
||||||
emitSubtitleAssChange: (payload: { text: string }) => void;
|
emitSubtitleAssChange: (payload: { text: string }) => void;
|
||||||
emitSubtitleTiming: (payload: { text: string; start: number; end: number }) => 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),
|
subScaleByWindow: asBoolean(msg.data, deps.getSubtitleMetrics().subScaleByWindow),
|
||||||
});
|
});
|
||||||
} else if (msg.name === 'sub-visibility') {
|
} else if (msg.name === 'sub-visibility') {
|
||||||
if (
|
if (deps.isVisibleOverlayVisible() && asBoolean(msg.data, false)) {
|
||||||
deps.isVisibleOverlayVisible() &&
|
deps.sendCommand({ command: ['set_property', 'sub-visibility', false] });
|
||||||
asBoolean(msg.data, false) &&
|
|
||||||
(deps.shouldBindVisibleOverlayToMpvSubVisibility?.() ?? true)
|
|
||||||
) {
|
|
||||||
deps.sendCommand({ command: ['set_property', 'sub-visibility', 'no'] });
|
deps.sendCommand({ command: ['set_property', 'sub-visibility', 'no'] });
|
||||||
|
deps.sendCommand({ command: ['set', 'sub-visibility', 'no'] });
|
||||||
}
|
}
|
||||||
} else if (msg.name === 'sub-use-margins') {
|
} else if (msg.name === 'sub-use-margins') {
|
||||||
deps.emitSubtitleMetricsChange({
|
deps.emitSubtitleMetricsChange({
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ function makeDeps(overrides: Partial<MpvIpcClientProtocolDeps> = {}): MpvIpcClie
|
|||||||
getResolvedConfig: () => ({}) as any,
|
getResolvedConfig: () => ({}) as any,
|
||||||
autoStartOverlay: false,
|
autoStartOverlay: false,
|
||||||
setOverlayVisible: () => {},
|
setOverlayVisible: () => {},
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => false,
|
|
||||||
isVisibleOverlayVisible: () => false,
|
isVisibleOverlayVisible: () => false,
|
||||||
getReconnectTimer: () => null,
|
getReconnectTimer: () => null,
|
||||||
setReconnectTimer: () => {},
|
setReconnectTimer: () => {},
|
||||||
@@ -311,7 +310,6 @@ test('MpvIpcClient connect does not force primary subtitle visibility from bindi
|
|||||||
const client = new MpvIpcClient(
|
const client = new MpvIpcClient(
|
||||||
'/tmp/mpv.sock',
|
'/tmp/mpv.sock',
|
||||||
makeDeps({
|
makeDeps({
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
|
||||||
isVisibleOverlayVisible: () => true,
|
isVisibleOverlayVisible: () => true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -332,6 +330,29 @@ test('MpvIpcClient connect does not force primary subtitle visibility from bindi
|
|||||||
assert.equal(hasPrimaryVisibilityMutation, false);
|
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 () => {
|
test('MpvIpcClient captures and disables secondary subtitle visibility on request', async () => {
|
||||||
const commands: unknown[] = [];
|
const commands: unknown[] = [];
|
||||||
const client = new MpvIpcClient('/tmp/mpv.sock', makeDeps());
|
const client = new MpvIpcClient('/tmp/mpv.sock', makeDeps());
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ export interface MpvIpcClientProtocolDeps {
|
|||||||
getResolvedConfig: () => Config;
|
getResolvedConfig: () => Config;
|
||||||
autoStartOverlay: boolean;
|
autoStartOverlay: boolean;
|
||||||
setOverlayVisible: (visible: boolean) => void;
|
setOverlayVisible: (visible: boolean) => void;
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
|
||||||
isVisibleOverlayVisible: () => boolean;
|
isVisibleOverlayVisible: () => boolean;
|
||||||
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
||||||
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
||||||
@@ -297,8 +296,6 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
getResolvedConfig: () => this.deps.getResolvedConfig(),
|
getResolvedConfig: () => this.deps.getResolvedConfig(),
|
||||||
getSubtitleMetrics: () => this.mpvSubtitleRenderMetrics,
|
getSubtitleMetrics: () => this.mpvSubtitleRenderMetrics,
|
||||||
isVisibleOverlayVisible: () => this.deps.isVisibleOverlayVisible(),
|
isVisibleOverlayVisible: () => this.deps.isVisibleOverlayVisible(),
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
|
||||||
this.deps.shouldBindVisibleOverlayToMpvSubVisibility(),
|
|
||||||
emitSubtitleChange: (payload) => {
|
emitSubtitleChange: (payload) => {
|
||||||
this.emit('subtitle-change', payload);
|
this.emit('subtitle-change', payload);
|
||||||
},
|
},
|
||||||
@@ -474,6 +471,9 @@ export class MpvIpcClient implements MpvClient {
|
|||||||
|
|
||||||
setSubVisibility(visible: boolean): void {
|
setSubVisibility(visible: boolean): void {
|
||||||
const value = visible ? 'yes' : 'no';
|
const value = visible ? 'yes' : 'no';
|
||||||
|
this.send({
|
||||||
|
command: ['set_property', 'sub-visibility', visible],
|
||||||
|
});
|
||||||
this.send({
|
this.send({
|
||||||
command: ['set_property', 'sub-visibility', value],
|
command: ['set_property', 'sub-visibility', value],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ import assert from 'node:assert/strict';
|
|||||||
import {
|
import {
|
||||||
isAutoUpdateEnabledRuntime,
|
isAutoUpdateEnabledRuntime,
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfig,
|
shouldAutoInitializeOverlayRuntimeFromConfig,
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility,
|
|
||||||
} from './startup';
|
} from './startup';
|
||||||
|
|
||||||
const BASE_CONFIG = {
|
const BASE_CONFIG = {
|
||||||
auto_start_overlay: false,
|
auto_start_overlay: false,
|
||||||
bind_visible_overlay_to_mpv_sub_visibility: true,
|
|
||||||
ankiConnect: {
|
ankiConnect: {
|
||||||
behavior: {
|
behavior: {
|
||||||
autoUpdateNewCards: true,
|
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', () => {
|
test('isAutoUpdateEnabledRuntime prefers runtime option and falls back to config', () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
isAutoUpdateEnabledRuntime(BASE_CONFIG, {
|
isAutoUpdateEnabledRuntime(BASE_CONFIG, {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ interface RuntimeAutoUpdateOptionManagerLike {
|
|||||||
|
|
||||||
export interface RuntimeConfigLike {
|
export interface RuntimeConfigLike {
|
||||||
auto_start_overlay?: boolean;
|
auto_start_overlay?: boolean;
|
||||||
bind_visible_overlay_to_mpv_sub_visibility: boolean;
|
|
||||||
ankiConnect?: {
|
ankiConnect?: {
|
||||||
behavior?: {
|
behavior?: {
|
||||||
autoUpdateNewCards?: boolean;
|
autoUpdateNewCards?: boolean;
|
||||||
@@ -156,10 +155,6 @@ export function shouldAutoInitializeOverlayRuntimeFromConfig(config: RuntimeConf
|
|||||||
return config.auto_start_overlay === true;
|
return config.auto_start_overlay === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function shouldBindVisibleOverlayToMpvSubVisibility(config: RuntimeConfigLike): boolean {
|
|
||||||
return config.bind_visible_overlay_to_mpv_sub_visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isAutoUpdateEnabledRuntime(
|
export function isAutoUpdateEnabledRuntime(
|
||||||
config: ResolvedConfig | RuntimeConfigLike,
|
config: ResolvedConfig | RuntimeConfigLike,
|
||||||
runtimeOptionsManager: RuntimeAutoUpdateOptionManagerLike | null,
|
runtimeOptionsManager: RuntimeAutoUpdateOptionManagerLike | null,
|
||||||
|
|||||||
94
src/main.ts
94
src/main.ts
@@ -106,6 +106,7 @@ import type { CliArgs, CliCommandSource } from './cli/args';
|
|||||||
import { printHelp } from './cli/help';
|
import { printHelp } from './cli/help';
|
||||||
import {
|
import {
|
||||||
buildConfigParseErrorDetails,
|
buildConfigParseErrorDetails,
|
||||||
|
buildConfigWarningDialogDetails,
|
||||||
buildConfigWarningNotificationBody,
|
buildConfigWarningNotificationBody,
|
||||||
failStartupFromConfig,
|
failStartupFromConfig,
|
||||||
} from './main/config-validation';
|
} from './main/config-validation';
|
||||||
@@ -353,6 +354,7 @@ import {
|
|||||||
resolveJellyfinPlaybackPlanRuntime,
|
resolveJellyfinPlaybackPlanRuntime,
|
||||||
runStartupBootstrapRuntime,
|
runStartupBootstrapRuntime,
|
||||||
saveSubtitlePosition as saveSubtitlePositionCore,
|
saveSubtitlePosition as saveSubtitlePositionCore,
|
||||||
|
syncYomitanDefaultAnkiServer as syncYomitanDefaultAnkiServerCore,
|
||||||
sendMpvCommandRuntime,
|
sendMpvCommandRuntime,
|
||||||
setMpvSubVisibilityRuntime,
|
setMpvSubVisibilityRuntime,
|
||||||
setOverlayDebugVisualizationEnabledRuntime,
|
setOverlayDebugVisualizationEnabledRuntime,
|
||||||
@@ -758,10 +760,7 @@ const restoreOverlayMpvSubtitles = createRestoreOverlayMpvSubtitlesHandler({
|
|||||||
});
|
});
|
||||||
|
|
||||||
function shouldSuppressMpvSubtitlesForOverlay(): boolean {
|
function shouldSuppressMpvSubtitlesForOverlay(): boolean {
|
||||||
return (
|
return overlayManager.getVisibleOverlayVisible();
|
||||||
overlayManager.getVisibleOverlayVisible() &&
|
|
||||||
configDerivedRuntime.shouldBindVisibleOverlayToMpvSubVisibility()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncOverlayMpvSubtitleSuppression(): void {
|
function syncOverlayMpvSubtitleSuppression(): void {
|
||||||
@@ -961,6 +960,12 @@ const buildConfigHotReloadRuntimeMainDepsHandler = createBuildConfigHotReloadRun
|
|||||||
showDesktopNotification('SubMiner', {
|
showDesktopNotification('SubMiner', {
|
||||||
body: buildConfigWarningNotificationBody(configPath, warnings),
|
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),
|
logInfo: (message) => appLogger.logInfo(message),
|
||||||
logWarning: (message) => appLogger.logWarning(message),
|
logWarning: (message) => appLogger.logWarning(message),
|
||||||
showDesktopNotification: (title, options) => showDesktopNotification(title, options),
|
showDesktopNotification: (title, options) => showDesktopNotification(title, options),
|
||||||
|
showConfigWarningsDialog:
|
||||||
|
process.platform === 'darwin'
|
||||||
|
? (title, details) => dialog.showErrorBox(title, details)
|
||||||
|
: undefined,
|
||||||
startConfigHotReload: () => configHotReloadRuntime.start(),
|
startConfigHotReload: () => configHotReloadRuntime.start(),
|
||||||
refreshAnilistClientSecretState: (options) => refreshAnilistClientSecretState(options),
|
refreshAnilistClientSecretState: (options) => refreshAnilistClientSecretState(options),
|
||||||
failHandlers: {
|
failHandlers: {
|
||||||
@@ -2203,8 +2212,6 @@ const {
|
|||||||
getResolvedConfig: () => getResolvedConfig(),
|
getResolvedConfig: () => getResolvedConfig(),
|
||||||
isAutoStartOverlayEnabled: () => appState.autoStartOverlay,
|
isAutoStartOverlayEnabled: () => appState.autoStartOverlay,
|
||||||
setOverlayVisible: (visible: boolean) => setOverlayVisible(visible),
|
setOverlayVisible: (visible: boolean) => setOverlayVisible(visible),
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
|
||||||
configDerivedRuntime.shouldBindVisibleOverlayToMpvSubVisibility(),
|
|
||||||
isVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(),
|
isVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(),
|
||||||
getReconnectTimer: () => appState.reconnectTimer,
|
getReconnectTimer: () => appState.reconnectTimer,
|
||||||
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => {
|
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => {
|
||||||
@@ -2372,11 +2379,70 @@ const enforceOverlayLayerOrder = createEnforceOverlayLayerOrderHandler(
|
|||||||
);
|
);
|
||||||
|
|
||||||
async function loadYomitanExtension(): Promise<Extension | null> {
|
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> {
|
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 {
|
function createOverlayWindow(kind: 'visible' | 'modal'): BrowserWindow {
|
||||||
@@ -2999,20 +3065,32 @@ function ensureOverlayWindowsReadyForVisibilityActions(): void {
|
|||||||
|
|
||||||
function setVisibleOverlayVisible(visible: boolean): void {
|
function setVisibleOverlayVisible(visible: boolean): void {
|
||||||
ensureOverlayWindowsReadyForVisibilityActions();
|
ensureOverlayWindowsReadyForVisibilityActions();
|
||||||
|
if (visible) {
|
||||||
|
void ensureOverlayMpvSubtitlesHidden();
|
||||||
|
}
|
||||||
setVisibleOverlayVisibleHandler(visible);
|
setVisibleOverlayVisibleHandler(visible);
|
||||||
syncOverlayMpvSubtitleSuppression();
|
syncOverlayMpvSubtitleSuppression();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleVisibleOverlay(): void {
|
function toggleVisibleOverlay(): void {
|
||||||
ensureOverlayWindowsReadyForVisibilityActions();
|
ensureOverlayWindowsReadyForVisibilityActions();
|
||||||
|
if (!overlayManager.getVisibleOverlayVisible()) {
|
||||||
|
void ensureOverlayMpvSubtitlesHidden();
|
||||||
|
}
|
||||||
toggleVisibleOverlayHandler();
|
toggleVisibleOverlayHandler();
|
||||||
syncOverlayMpvSubtitleSuppression();
|
syncOverlayMpvSubtitleSuppression();
|
||||||
}
|
}
|
||||||
function setOverlayVisible(visible: boolean): void {
|
function setOverlayVisible(visible: boolean): void {
|
||||||
|
if (visible) {
|
||||||
|
void ensureOverlayMpvSubtitlesHidden();
|
||||||
|
}
|
||||||
setOverlayVisibleHandler(visible);
|
setOverlayVisibleHandler(visible);
|
||||||
syncOverlayMpvSubtitleSuppression();
|
syncOverlayMpvSubtitleSuppression();
|
||||||
}
|
}
|
||||||
function toggleOverlay(): void {
|
function toggleOverlay(): void {
|
||||||
|
if (!overlayManager.getVisibleOverlayVisible()) {
|
||||||
|
void ensureOverlayMpvSubtitlesHidden();
|
||||||
|
}
|
||||||
toggleOverlayHandler();
|
toggleOverlayHandler();
|
||||||
syncOverlayMpvSubtitleSuppression();
|
syncOverlayMpvSubtitleSuppression();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
|
|||||||
getResolvedConfig: () => ({ auto_start_overlay: false }),
|
getResolvedConfig: () => ({ auto_start_overlay: false }),
|
||||||
isAutoStartOverlayEnabled: () => true,
|
isAutoStartOverlayEnabled: () => true,
|
||||||
setOverlayVisible: () => {},
|
setOverlayVisible: () => {},
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
|
||||||
isVisibleOverlayVisible: () => false,
|
isVisibleOverlayVisible: () => false,
|
||||||
getReconnectTimer: () => null,
|
getReconnectTimer: () => null,
|
||||||
setReconnectTimer: () => {},
|
setReconnectTimer: () => {},
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
jimakuFetchJson as jimakuFetchJsonCore,
|
jimakuFetchJson as jimakuFetchJsonCore,
|
||||||
resolveJimakuApiKey as resolveJimakuApiKeyCore,
|
resolveJimakuApiKey as resolveJimakuApiKeyCore,
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfig as shouldAutoInitializeOverlayRuntimeFromConfigCore,
|
shouldAutoInitializeOverlayRuntimeFromConfig as shouldAutoInitializeOverlayRuntimeFromConfigCore,
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility as shouldBindVisibleOverlayToMpvSubVisibilityCore,
|
|
||||||
} from '../../core/services';
|
} from '../../core/services';
|
||||||
|
|
||||||
export type ConfigDerivedRuntimeDeps = {
|
export type ConfigDerivedRuntimeDeps = {
|
||||||
@@ -20,7 +19,6 @@ export type ConfigDerivedRuntimeDeps = {
|
|||||||
|
|
||||||
export function createConfigDerivedRuntime(deps: ConfigDerivedRuntimeDeps): {
|
export function createConfigDerivedRuntime(deps: ConfigDerivedRuntimeDeps): {
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfig: () => boolean;
|
shouldAutoInitializeOverlayRuntimeFromConfig: () => boolean;
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
|
||||||
isAutoUpdateEnabledRuntime: () => boolean;
|
isAutoUpdateEnabledRuntime: () => boolean;
|
||||||
getJimakuLanguagePreference: () => JimakuLanguagePreference;
|
getJimakuLanguagePreference: () => JimakuLanguagePreference;
|
||||||
getJimakuMaxEntryResults: () => number;
|
getJimakuMaxEntryResults: () => number;
|
||||||
@@ -33,8 +31,6 @@ export function createConfigDerivedRuntime(deps: ConfigDerivedRuntimeDeps): {
|
|||||||
return {
|
return {
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfig: () =>
|
shouldAutoInitializeOverlayRuntimeFromConfig: () =>
|
||||||
shouldAutoInitializeOverlayRuntimeFromConfigCore(deps.getResolvedConfig()),
|
shouldAutoInitializeOverlayRuntimeFromConfigCore(deps.getResolvedConfig()),
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
|
||||||
shouldBindVisibleOverlayToMpvSubVisibilityCore(deps.getResolvedConfig()),
|
|
||||||
isAutoUpdateEnabledRuntime: () =>
|
isAutoUpdateEnabledRuntime: () =>
|
||||||
isAutoUpdateEnabledRuntimeCore(deps.getResolvedConfig(), deps.getRuntimeOptionsManager()),
|
isAutoUpdateEnabledRuntimeCore(deps.getResolvedConfig(), deps.getRuntimeOptionsManager()),
|
||||||
getJimakuLanguagePreference: () =>
|
getJimakuLanguagePreference: () =>
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ test('mpv runtime service main deps builder maps state and callbacks', () => {
|
|||||||
getResolvedConfig: () => ({ mode: 'test' }),
|
getResolvedConfig: () => ({ mode: 'test' }),
|
||||||
isAutoStartOverlayEnabled: () => true,
|
isAutoStartOverlayEnabled: () => true,
|
||||||
setOverlayVisible: (visible) => calls.push(`overlay:${visible}`),
|
setOverlayVisible: (visible) => calls.push(`overlay:${visible}`),
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
|
||||||
isVisibleOverlayVisible: () => false,
|
isVisibleOverlayVisible: () => false,
|
||||||
getReconnectTimer: () => reconnectTimer,
|
getReconnectTimer: () => reconnectTimer,
|
||||||
setReconnectTimer: (timer) => {
|
setReconnectTimer: (timer) => {
|
||||||
@@ -29,7 +28,6 @@ test('mpv runtime service main deps builder maps state and callbacks', () => {
|
|||||||
const deps = build();
|
const deps = build();
|
||||||
assert.equal(deps.socketPath, '/tmp/mpv.sock');
|
assert.equal(deps.socketPath, '/tmp/mpv.sock');
|
||||||
assert.equal(deps.options.autoStartOverlay, true);
|
assert.equal(deps.options.autoStartOverlay, true);
|
||||||
assert.equal(deps.options.shouldBindVisibleOverlayToMpvSubVisibility(), true);
|
|
||||||
assert.equal(deps.options.isVisibleOverlayVisible(), false);
|
assert.equal(deps.options.isVisibleOverlayVisible(), false);
|
||||||
assert.deepEqual(deps.options.getResolvedConfig(), { mode: 'test' });
|
assert.deepEqual(deps.options.getResolvedConfig(), { mode: 'test' });
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export function createBuildMpvClientRuntimeServiceFactoryDepsHandler<
|
|||||||
getResolvedConfig: () => TResolvedConfig;
|
getResolvedConfig: () => TResolvedConfig;
|
||||||
isAutoStartOverlayEnabled: () => boolean;
|
isAutoStartOverlayEnabled: () => boolean;
|
||||||
setOverlayVisible: (visible: boolean) => void;
|
setOverlayVisible: (visible: boolean) => void;
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
|
||||||
isVisibleOverlayVisible: () => boolean;
|
isVisibleOverlayVisible: () => boolean;
|
||||||
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
||||||
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
||||||
@@ -21,8 +20,6 @@ export function createBuildMpvClientRuntimeServiceFactoryDepsHandler<
|
|||||||
getResolvedConfig: () => deps.getResolvedConfig(),
|
getResolvedConfig: () => deps.getResolvedConfig(),
|
||||||
autoStartOverlay: deps.isAutoStartOverlayEnabled(),
|
autoStartOverlay: deps.isAutoStartOverlayEnabled(),
|
||||||
setOverlayVisible: (visible: boolean) => deps.setOverlayVisible(visible),
|
setOverlayVisible: (visible: boolean) => deps.setOverlayVisible(visible),
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
|
||||||
deps.shouldBindVisibleOverlayToMpvSubVisibility(),
|
|
||||||
isVisibleOverlayVisible: () => deps.isVisibleOverlayVisible(),
|
isVisibleOverlayVisible: () => deps.isVisibleOverlayVisible(),
|
||||||
getReconnectTimer: () => deps.getReconnectTimer(),
|
getReconnectTimer: () => deps.getReconnectTimer(),
|
||||||
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => deps.setReconnectTimer(timer),
|
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => deps.setReconnectTimer(timer),
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ test('mpv runtime service factory constructs client, binds handlers, and connect
|
|||||||
getResolvedConfig: () => ({}),
|
getResolvedConfig: () => ({}),
|
||||||
autoStartOverlay: true,
|
autoStartOverlay: true,
|
||||||
setOverlayVisible: () => {},
|
setOverlayVisible: () => {},
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => false,
|
|
||||||
isVisibleOverlayVisible: () => false,
|
isVisibleOverlayVisible: () => false,
|
||||||
getReconnectTimer: () => null,
|
getReconnectTimer: () => null,
|
||||||
setReconnectTimer: () => {},
|
setReconnectTimer: () => {},
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ export type MpvClientRuntimeServiceOptions = {
|
|||||||
getResolvedConfig: () => Config;
|
getResolvedConfig: () => Config;
|
||||||
autoStartOverlay: boolean;
|
autoStartOverlay: boolean;
|
||||||
setOverlayVisible: (visible: boolean) => void;
|
setOverlayVisible: (visible: boolean) => void;
|
||||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
|
||||||
isVisibleOverlayVisible: () => boolean;
|
isVisibleOverlayVisible: () => boolean;
|
||||||
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
||||||
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
||||||
|
|||||||
14
src/types.ts
14
src/types.ts
@@ -192,6 +192,12 @@ export interface AnkiConnectConfig {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
url?: string;
|
url?: string;
|
||||||
pollingRate?: number;
|
pollingRate?: number;
|
||||||
|
proxy?: {
|
||||||
|
enabled?: boolean;
|
||||||
|
host?: string;
|
||||||
|
port?: number;
|
||||||
|
upstreamUrl?: string;
|
||||||
|
};
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
fields?: {
|
fields?: {
|
||||||
audio?: string;
|
audio?: string;
|
||||||
@@ -413,7 +419,6 @@ export interface Config {
|
|||||||
subsync?: SubsyncConfig;
|
subsync?: SubsyncConfig;
|
||||||
subtitleStyle?: SubtitleStyleConfig;
|
subtitleStyle?: SubtitleStyleConfig;
|
||||||
auto_start_overlay?: boolean;
|
auto_start_overlay?: boolean;
|
||||||
bind_visible_overlay_to_mpv_sub_visibility?: boolean;
|
|
||||||
jimaku?: JimakuConfig;
|
jimaku?: JimakuConfig;
|
||||||
anilist?: AnilistConfig;
|
anilist?: AnilistConfig;
|
||||||
jellyfin?: JellyfinConfig;
|
jellyfin?: JellyfinConfig;
|
||||||
@@ -436,6 +441,12 @@ export interface ResolvedConfig {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
url: string;
|
url: string;
|
||||||
pollingRate: number;
|
pollingRate: number;
|
||||||
|
proxy: {
|
||||||
|
enabled: boolean;
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
upstreamUrl: string;
|
||||||
|
};
|
||||||
tags: string[];
|
tags: string[];
|
||||||
fields: {
|
fields: {
|
||||||
audio: string;
|
audio: string;
|
||||||
@@ -514,7 +525,6 @@ export interface ResolvedConfig {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
auto_start_overlay: boolean;
|
auto_start_overlay: boolean;
|
||||||
bind_visible_overlay_to_mpv_sub_visibility: boolean;
|
|
||||||
jimaku: JimakuConfig & {
|
jimaku: JimakuConfig & {
|
||||||
apiBaseUrl: string;
|
apiBaseUrl: string;
|
||||||
languagePreference: JimakuLanguagePreference;
|
languagePreference: JimakuLanguagePreference;
|
||||||
|
|||||||
Reference in New Issue
Block a user