mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-27 04:49:49 -07:00
fix(tsukihime): preserve Animetosho aliases
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
type: added
|
type: added
|
||||||
area: overlay
|
area: overlay
|
||||||
|
|
||||||
- Added a TsukiHime integration for downloading English (and Japanese) subtitles, mirroring the Jimaku flow: `Ctrl+Shift+T` (configurable via `shortcuts.openTsukihime`) opens an in-overlay modal with two language tabs (the first follows `secondarySub.secondarySubLanguages`, defaulting to English; the second is Japanese) that parses the current video filename, searches TsukiHime releases, lists extracted text subtitle tracks filtered by the active tab, then downloads the chosen track, decompresses it (requires the `xz` binary), saves it next to the video with a language suffix (`<video>.en.<ext>`, `.ja` for Japanese tracks, etc.), and loads it into mpv immediately - Japanese tracks as the primary subtitle, other languages as the secondary subtitle. No API key is required; also reachable via `subminer --open-tsukihime`, the `__tsukihime-open` keybinding command, and configurable under a new `tsukihime` config section. TsukiHime is the successor to Animetosho, which stops processing new releases in May 2026; it imported the Animetosho index and mirrors its attachment storage, so older releases stay reachable.
|
- Added a TsukiHime integration for downloading English (and Japanese) subtitles, mirroring the Jimaku flow: `Ctrl+Shift+T` (configurable via `shortcuts.openTsukihime`) opens an in-overlay modal with two language tabs (the first follows `secondarySub.secondarySubLanguages`, defaulting to English; the second is Japanese) that parses the current video filename, searches TsukiHime releases, lists extracted text subtitle tracks filtered by the active tab, then downloads the chosen track, decompresses it (requires the `xz` binary), saves it next to the video with a language suffix (`<video>.en.<ext>`, `.ja` for Japanese tracks, etc.), and loads it into mpv immediately - Japanese tracks as the primary subtitle, other languages as the secondary subtitle. No API key is required; also reachable via `subminer --open-tsukihime`, the `__tsukihime-open` keybinding command, and configurable under a new `tsukihime` config section. TsukiHime is the successor to Animetosho, which stops processing new releases in May 2026; it imported the Animetosho index and mirrors its attachment storage, so older releases stay reachable. Existing `animetosho`, `shortcuts.openAnimetosho`, `--open-animetosho`, and `__animetosho-open` inputs remain accepted as deprecated aliases.
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ Mouse-hover playback behavior is configured separately from shortcuts: `subtitle
|
|||||||
| `` ` `` | Toggle stats overlay | `stats.toggleKey` |
|
| `` ` `` | Toggle stats overlay | `stats.toggleKey` |
|
||||||
| `W` | Mark current video watched and advance to next in queue | `stats.markWatchedKey` |
|
| `W` | Mark current video watched and advance to next in queue | `stats.markWatchedKey` |
|
||||||
|
|
||||||
|
`shortcuts.openAnimetosho` remains accepted as a deprecated alias for `shortcuts.openTsukihime`. The current name takes precedence when both are configured.
|
||||||
|
|
||||||
The stats toggle is handled inside the focused visible overlay window. It is configurable through the top-level `stats.toggleKey` setting and defaults to `Backquote`.
|
The stats toggle is handled inside the focused visible overlay window. It is configurable through the top-level `stats.toggleKey` setting and defaults to `Backquote`.
|
||||||
|
|
||||||
The subtitle sidebar toggle is overlay-local and only opens when SubMiner has a parsed cue list for the active subtitle source.
|
The subtitle sidebar toggle is overlay-local and only opens when SubMiner has a parsed cue list for the active subtitle source.
|
||||||
|
|||||||
@@ -64,11 +64,15 @@ The keyboard shortcut is configured separately under `shortcuts`:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Existing Animetosho configuration remains compatible. SubMiner treats the old `animetosho` section and `shortcuts.openAnimetosho` setting as deprecated aliases. When old and current names are both present, `tsukihime` and `shortcuts.openTsukihime` take precedence.
|
||||||
|
|
||||||
## Other Ways to Open It
|
## Other Ways to Open It
|
||||||
|
|
||||||
- CLI: `subminer --open-tsukihime`
|
- CLI: `subminer --open-tsukihime`
|
||||||
- Keybinding command: bind any key to `["__tsukihime-open"]` in the `keybindings` array
|
- Keybinding command: bind any key to `["__tsukihime-open"]` in the `keybindings` array
|
||||||
|
|
||||||
|
The previous `--open-animetosho` flag and `__animetosho-open` keybinding command remain accepted as deprecated aliases.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
- **"xz binary not found"** - install `xz`/`xz-utils` with your package manager.
|
- **"xz binary not found"** - install `xz`/`xz-utils` with your package manager.
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ SubMiner.AppImage --show-visible-overlay # Force show visible overl
|
|||||||
SubMiner.AppImage --hide-visible-overlay # Force hide visible overlay
|
SubMiner.AppImage --hide-visible-overlay # Force hide visible overlay
|
||||||
SubMiner.AppImage --toggle-primary-subtitle-bar # Toggle primary subtitle bar visibility
|
SubMiner.AppImage --toggle-primary-subtitle-bar # Toggle primary subtitle bar visibility
|
||||||
SubMiner.AppImage --toggle-subtitle-sidebar # Toggle the subtitle sidebar
|
SubMiner.AppImage --toggle-subtitle-sidebar # Toggle the subtitle sidebar
|
||||||
|
SubMiner.AppImage --open-tsukihime # Open TsukiHime subtitle search
|
||||||
SubMiner.AppImage --start --dev # Enable app/dev mode only
|
SubMiner.AppImage --start --dev # Enable app/dev mode only
|
||||||
SubMiner.AppImage --start --debug # Alias for --dev
|
SubMiner.AppImage --start --debug # Alias for --dev
|
||||||
SubMiner.AppImage --start --log-level debug # Force verbose logging without app/dev mode
|
SubMiner.AppImage --start --log-level debug # Force verbose logging without app/dev mode
|
||||||
@@ -165,6 +166,8 @@ SubMiner.AppImage --dictionary-select --dictionary-anilist-id 21355 # Pin corre
|
|||||||
SubMiner.AppImage --help # Show all options
|
SubMiner.AppImage --help # Show all options
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The previous `--open-animetosho` flag remains accepted as a deprecated alias for `--open-tsukihime`.
|
||||||
|
|
||||||
The tray menu includes `Export Logs`, which creates the same sanitized local-date log ZIP as `subminer logs -e` and shows the archive path when complete. Export sanitization masks common PII and secrets, including home-directory usernames, IP addresses, emails, auth/cookie headers, yt-dlp cookie arguments, URL credentials, token/key/password fields, and signed YouTube media URL query strings. The exported copy is sanitized; source log files remain unredacted on disk.
|
The tray menu includes `Export Logs`, which creates the same sanitized local-date log ZIP as `subminer logs -e` and shows the archive path when complete. Export sanitization masks common PII and secrets, including home-directory usernames, IP addresses, emails, auth/cookie headers, yt-dlp cookie arguments, URL credentials, token/key/password fields, and signed YouTube media URL query strings. The exported copy is sanitized; source log files remain unredacted on disk.
|
||||||
|
|
||||||
Once Jellyfin is configured, the tray menu includes `Jellyfin Discovery` for starting or stopping cast discovery in the current app session without changing config.
|
Once Jellyfin is configured, the tray menu includes `Jellyfin Discovery` for starting or stopping cast discovery in the current app session without changing config.
|
||||||
|
|||||||
@@ -254,7 +254,7 @@ function M.create(ctx)
|
|||||||
return { "--open-runtime-options" }
|
return { "--open-runtime-options" }
|
||||||
elseif action_id == "openJimaku" then
|
elseif action_id == "openJimaku" then
|
||||||
return { "--open-jimaku" }
|
return { "--open-jimaku" }
|
||||||
elseif action_id == "openTsukihime" then
|
elseif action_id == "openTsukihime" or action_id == "openAnimetosho" then
|
||||||
return { "--open-tsukihime" }
|
return { "--open-tsukihime" }
|
||||||
elseif action_id == "openYoutubePicker" then
|
elseif action_id == "openYoutubePicker" then
|
||||||
return { "--open-youtube-picker" }
|
return { "--open-youtube-picker" }
|
||||||
|
|||||||
@@ -237,6 +237,14 @@ local ctx = {
|
|||||||
actionType = "session-action",
|
actionType = "session-action",
|
||||||
actionId = "openPlaylistBrowser",
|
actionId = "openPlaylistBrowser",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key = {
|
||||||
|
code = "KeyT",
|
||||||
|
modifiers = { "ctrl", "alt" },
|
||||||
|
},
|
||||||
|
actionType = "session-action",
|
||||||
|
actionId = "openAnimetosho",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key = {
|
key = {
|
||||||
code = "KeyH",
|
code = "KeyH",
|
||||||
@@ -387,6 +395,7 @@ end
|
|||||||
local expected_cli_bindings = {
|
local expected_cli_bindings = {
|
||||||
{ keys = "Ctrl+Alt+c", flag = "--open-youtube-picker" },
|
{ keys = "Ctrl+Alt+c", flag = "--open-youtube-picker" },
|
||||||
{ keys = "Ctrl+Alt+p", flag = "--open-playlist-browser" },
|
{ keys = "Ctrl+Alt+p", flag = "--open-playlist-browser" },
|
||||||
|
{ keys = "Ctrl+Alt+t", flag = "--open-tsukihime" },
|
||||||
{ keys = "Ctrl+H", flag = "--replay-current-subtitle" },
|
{ keys = "Ctrl+H", flag = "--replay-current-subtitle" },
|
||||||
{ keys = "Ctrl+L", flag = "--play-next-subtitle" },
|
{ keys = "Ctrl+L", flag = "--play-next-subtitle" },
|
||||||
{ keys = "w", flag = "--mark-watched" },
|
{ keys = "w", flag = "--mark-watched" },
|
||||||
|
|||||||
@@ -148,6 +148,14 @@ test('parseArgs captures session action forwarding flags', () => {
|
|||||||
assert.equal(shouldStartApp(args), true);
|
assert.equal(shouldStartApp(args), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('parseArgs keeps the legacy Animetosho open flag as a TsukiHime alias', () => {
|
||||||
|
const args = parseArgs(['--open-animetosho']);
|
||||||
|
|
||||||
|
assert.equal(args.openTsukihime, true);
|
||||||
|
assert.equal(hasExplicitCommand(args), true);
|
||||||
|
assert.equal(shouldStartApp(args), true);
|
||||||
|
});
|
||||||
|
|
||||||
test('parseArgs ignores retired subtitle delay shift flags', () => {
|
test('parseArgs ignores retired subtitle delay shift flags', () => {
|
||||||
const args = parseArgs(['--shift-sub-delay-prev-line', '--shift-sub-delay-next-line']);
|
const args = parseArgs(['--shift-sub-delay-prev-line', '--shift-sub-delay-next-line']);
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -294,8 +294,9 @@ export function parseArgs(argv: string[]): CliArgs {
|
|||||||
else if (arg === '--open-controller-select') args.openControllerSelect = true;
|
else if (arg === '--open-controller-select') args.openControllerSelect = true;
|
||||||
else if (arg === '--open-controller-debug') args.openControllerDebug = true;
|
else if (arg === '--open-controller-debug') args.openControllerDebug = true;
|
||||||
else if (arg === '--open-jimaku') args.openJimaku = true;
|
else if (arg === '--open-jimaku') args.openJimaku = true;
|
||||||
else if (arg === '--open-tsukihime') args.openTsukihime = true;
|
else if (arg === '--open-tsukihime' || arg === '--open-animetosho') {
|
||||||
else if (arg === '--open-youtube-picker') args.openYoutubePicker = true;
|
args.openTsukihime = true;
|
||||||
|
} else if (arg === '--open-youtube-picker') args.openYoutubePicker = true;
|
||||||
else if (arg === '--open-playlist-browser') args.openPlaylistBrowser = true;
|
else if (arg === '--open-playlist-browser') args.openPlaylistBrowser = true;
|
||||||
else if (arg === '--replay-current-subtitle') args.replayCurrentSubtitle = true;
|
else if (arg === '--replay-current-subtitle') args.replayCurrentSubtitle = true;
|
||||||
else if (arg === '--play-next-subtitle') args.playNextSubtitle = true;
|
else if (arg === '--play-next-subtitle') args.playNextSubtitle = true;
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export const SPECIAL_COMMANDS = {
|
|||||||
SUBSYNC_TRIGGER: '__subsync-trigger',
|
SUBSYNC_TRIGGER: '__subsync-trigger',
|
||||||
RUNTIME_OPTIONS_OPEN: '__runtime-options-open',
|
RUNTIME_OPTIONS_OPEN: '__runtime-options-open',
|
||||||
JIMAKU_OPEN: '__jimaku-open',
|
JIMAKU_OPEN: '__jimaku-open',
|
||||||
|
/** @deprecated Use TSUKIHIME_OPEN. */
|
||||||
|
ANIMETOSHO_OPEN: '__animetosho-open',
|
||||||
TSUKIHIME_OPEN: '__tsukihime-open',
|
TSUKIHIME_OPEN: '__tsukihime-open',
|
||||||
RUNTIME_OPTION_CYCLE_PREFIX: '__runtime-option-cycle:',
|
RUNTIME_OPTION_CYCLE_PREFIX: '__runtime-option-cycle:',
|
||||||
REPLAY_SUBTITLE: '__replay-subtitle',
|
REPLAY_SUBTITLE: '__replay-subtitle',
|
||||||
|
|||||||
@@ -254,6 +254,20 @@ export function applyCoreDomainConfig(context: ResolveContext): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src.shortcuts.openTsukihime === undefined) {
|
||||||
|
const legacyOpenTsukihime = src.shortcuts.openAnimetosho;
|
||||||
|
if (typeof legacyOpenTsukihime === 'string' || legacyOpenTsukihime === null) {
|
||||||
|
resolved.shortcuts.openTsukihime = legacyOpenTsukihime;
|
||||||
|
} else if (legacyOpenTsukihime !== undefined) {
|
||||||
|
warn(
|
||||||
|
'shortcuts.openAnimetosho',
|
||||||
|
legacyOpenTsukihime,
|
||||||
|
resolved.shortcuts.openTsukihime,
|
||||||
|
'Expected string or null.',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const timeout = asNumber(src.shortcuts.multiCopyTimeoutMs);
|
const timeout = asNumber(src.shortcuts.multiCopyTimeoutMs);
|
||||||
if (timeout !== undefined && timeout > 0) {
|
if (timeout !== undefined && timeout > 0) {
|
||||||
resolved.shortcuts.multiCopyTimeoutMs = Math.floor(timeout);
|
resolved.shortcuts.multiCopyTimeoutMs = Math.floor(timeout);
|
||||||
|
|||||||
@@ -80,17 +80,26 @@ export function applySubtitleDomainConfig(context: ResolveContext): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isObject(src.tsukihime)) {
|
const currentTsukihimeSource = isObject(src.tsukihime) ? src.tsukihime : null;
|
||||||
const apiBaseUrl = asString(src.tsukihime.apiBaseUrl);
|
if (src.tsukihime !== undefined && !currentTsukihimeSource) {
|
||||||
|
warn('tsukihime', src.tsukihime, resolved.tsukihime, 'Expected object.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyTsukihimeSource =
|
||||||
|
src.tsukihime === undefined && isObject(src.animetosho) ? src.animetosho : null;
|
||||||
|
const tsukihimeSource = currentTsukihimeSource ?? legacyTsukihimeSource;
|
||||||
|
const tsukihimeSourcePath = currentTsukihimeSource ? 'tsukihime' : 'animetosho';
|
||||||
|
if (tsukihimeSource) {
|
||||||
|
const apiBaseUrl = asString(tsukihimeSource.apiBaseUrl);
|
||||||
if (apiBaseUrl !== undefined) resolved.tsukihime.apiBaseUrl = apiBaseUrl;
|
if (apiBaseUrl !== undefined) resolved.tsukihime.apiBaseUrl = apiBaseUrl;
|
||||||
|
|
||||||
const maxSearchResults = asNumber(src.tsukihime.maxSearchResults);
|
const maxSearchResults = asNumber(tsukihimeSource.maxSearchResults);
|
||||||
if (maxSearchResults !== undefined && Math.floor(maxSearchResults) > 0) {
|
if (maxSearchResults !== undefined && Math.floor(maxSearchResults) > 0) {
|
||||||
resolved.tsukihime.maxSearchResults = Math.floor(maxSearchResults);
|
resolved.tsukihime.maxSearchResults = Math.floor(maxSearchResults);
|
||||||
} else if (src.tsukihime.maxSearchResults !== undefined) {
|
} else if (tsukihimeSource.maxSearchResults !== undefined) {
|
||||||
warn(
|
warn(
|
||||||
'tsukihime.maxSearchResults',
|
`${tsukihimeSourcePath}.maxSearchResults`,
|
||||||
src.tsukihime.maxSearchResults,
|
tsukihimeSource.maxSearchResults,
|
||||||
resolved.tsukihime.maxSearchResults,
|
resolved.tsukihime.maxSearchResults,
|
||||||
'Expected positive number.',
|
'Expected positive number.',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { asBoolean } from './shared';
|
|||||||
|
|
||||||
export function applyTopLevelConfig(context: ResolveContext): void {
|
export function applyTopLevelConfig(context: ResolveContext): void {
|
||||||
const { src, resolved, warn } = context;
|
const { src, resolved, warn } = context;
|
||||||
const knownTopLevelKeys = new Set(Object.keys(resolved));
|
const knownTopLevelKeys = new Set([...Object.keys(resolved), 'animetosho']);
|
||||||
for (const key of Object.keys(src)) {
|
for (const key of Object.keys(src)) {
|
||||||
if (!knownTopLevelKeys.has(key)) {
|
if (!knownTopLevelKeys.has(key)) {
|
||||||
warn(key, src[key], undefined, 'Unknown top-level config key; ignored.');
|
warn(key, src[key], undefined, 'Unknown top-level config key; ignored.');
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { resolveConfig } from '../resolve';
|
||||||
|
|
||||||
|
test('resolveConfig maps legacy Animetosho settings to TsukiHime', () => {
|
||||||
|
const { resolved, warnings } = resolveConfig({
|
||||||
|
animetosho: {
|
||||||
|
apiBaseUrl: 'https://legacy.example/v1',
|
||||||
|
maxSearchResults: 23,
|
||||||
|
},
|
||||||
|
shortcuts: {
|
||||||
|
openAnimetosho: 'Ctrl+Alt+T',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(resolved.tsukihime.apiBaseUrl, 'https://legacy.example/v1');
|
||||||
|
assert.equal(resolved.tsukihime.maxSearchResults, 23);
|
||||||
|
assert.equal(resolved.shortcuts.openTsukihime, 'Ctrl+Alt+T');
|
||||||
|
assert.deepEqual(warnings, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolveConfig gives current TsukiHime settings precedence over legacy aliases', () => {
|
||||||
|
const { resolved, warnings } = resolveConfig({
|
||||||
|
animetosho: {
|
||||||
|
apiBaseUrl: 'https://legacy.example/v1',
|
||||||
|
maxSearchResults: 23,
|
||||||
|
},
|
||||||
|
tsukihime: {
|
||||||
|
apiBaseUrl: 'https://current.example/v1',
|
||||||
|
maxSearchResults: 7,
|
||||||
|
},
|
||||||
|
shortcuts: {
|
||||||
|
openAnimetosho: 'Ctrl+Alt+T',
|
||||||
|
openTsukihime: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(resolved.tsukihime.apiBaseUrl, 'https://current.example/v1');
|
||||||
|
assert.equal(resolved.tsukihime.maxSearchResults, 7);
|
||||||
|
assert.equal(resolved.shortcuts.openTsukihime, null);
|
||||||
|
assert.deepEqual(warnings, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('resolveConfig does not fall back when the current TsukiHime setting is invalid', () => {
|
||||||
|
const { resolved, warnings } = resolveConfig({
|
||||||
|
animetosho: {
|
||||||
|
apiBaseUrl: 'https://legacy.example/v1',
|
||||||
|
maxSearchResults: 23,
|
||||||
|
},
|
||||||
|
tsukihime: 'invalid' as never,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(resolved.tsukihime.apiBaseUrl, 'https://api.tsukihime.org/v1');
|
||||||
|
assert.equal(resolved.tsukihime.maxSearchResults, 10);
|
||||||
|
assert.deepEqual(warnings, [
|
||||||
|
{
|
||||||
|
path: 'tsukihime',
|
||||||
|
value: 'invalid',
|
||||||
|
fallback: resolved.tsukihime,
|
||||||
|
message: 'Expected object.',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
@@ -12,6 +12,7 @@ function createOptions(overrides: Partial<Parameters<typeof handleMpvCommandFrom
|
|||||||
SUBSYNC_TRIGGER: '__subsync-trigger',
|
SUBSYNC_TRIGGER: '__subsync-trigger',
|
||||||
RUNTIME_OPTIONS_OPEN: '__runtime-options-open',
|
RUNTIME_OPTIONS_OPEN: '__runtime-options-open',
|
||||||
JIMAKU_OPEN: '__jimaku-open',
|
JIMAKU_OPEN: '__jimaku-open',
|
||||||
|
ANIMETOSHO_OPEN: '__animetosho-open',
|
||||||
TSUKIHIME_OPEN: '__tsukihime-open',
|
TSUKIHIME_OPEN: '__tsukihime-open',
|
||||||
RUNTIME_OPTION_CYCLE_PREFIX: '__runtime-option-cycle:',
|
RUNTIME_OPTION_CYCLE_PREFIX: '__runtime-option-cycle:',
|
||||||
REPLAY_SUBTITLE: '__replay-subtitle',
|
REPLAY_SUBTITLE: '__replay-subtitle',
|
||||||
@@ -152,6 +153,15 @@ test('handleMpvCommandFromIpc dispatches special jimaku open command', () => {
|
|||||||
assert.deepEqual(osd, []);
|
assert.deepEqual(osd, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('handleMpvCommandFromIpc keeps the legacy Animetosho command as a TsukiHime alias', () => {
|
||||||
|
const { options, calls, sentCommands } = createOptions();
|
||||||
|
|
||||||
|
handleMpvCommandFromIpc(['__animetosho-open'], options);
|
||||||
|
|
||||||
|
assert.deepEqual(calls, ['tsukihime']);
|
||||||
|
assert.deepEqual(sentCommands, []);
|
||||||
|
});
|
||||||
|
|
||||||
test('handleMpvCommandFromIpc dispatches special playlist browser open command', async () => {
|
test('handleMpvCommandFromIpc dispatches special playlist browser open command', async () => {
|
||||||
const { options, calls, sentCommands, osd } = createOptions();
|
const { options, calls, sentCommands, osd } = createOptions();
|
||||||
handleMpvCommandFromIpc(['__playlist-browser-open'], options);
|
handleMpvCommandFromIpc(['__playlist-browser-open'], options);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface HandleMpvCommandFromIpcOptions {
|
|||||||
SUBSYNC_TRIGGER: string;
|
SUBSYNC_TRIGGER: string;
|
||||||
RUNTIME_OPTIONS_OPEN: string;
|
RUNTIME_OPTIONS_OPEN: string;
|
||||||
JIMAKU_OPEN: string;
|
JIMAKU_OPEN: string;
|
||||||
|
ANIMETOSHO_OPEN: string;
|
||||||
TSUKIHIME_OPEN: string;
|
TSUKIHIME_OPEN: string;
|
||||||
RUNTIME_OPTION_CYCLE_PREFIX: string;
|
RUNTIME_OPTION_CYCLE_PREFIX: string;
|
||||||
REPLAY_SUBTITLE: string;
|
REPLAY_SUBTITLE: string;
|
||||||
@@ -114,7 +115,10 @@ export function handleMpvCommandFromIpc(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first === options.specialCommands.TSUKIHIME_OPEN) {
|
if (
|
||||||
|
first === options.specialCommands.TSUKIHIME_OPEN ||
|
||||||
|
first === options.specialCommands.ANIMETOSHO_OPEN
|
||||||
|
) {
|
||||||
options.openTsukihime();
|
options.openTsukihime();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,6 +320,21 @@ test('compileSessionBindings wires every default keybinding to an overlay or mpv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('compileSessionBindings maps the legacy Animetosho command to the TsukiHime action', () => {
|
||||||
|
const result = compileSessionBindings({
|
||||||
|
shortcuts: createShortcuts(),
|
||||||
|
keybindings: [createKeybinding('Ctrl+Alt+T', ['__animetosho-open'])],
|
||||||
|
platform: 'linux',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(result.warnings, []);
|
||||||
|
assert.equal(result.bindings[0]?.actionType, 'session-action');
|
||||||
|
assert.equal(
|
||||||
|
result.bindings[0]?.actionType === 'session-action' ? result.bindings[0].actionId : null,
|
||||||
|
'openTsukihime',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('compileSessionBindings leaves retired subtitle-delay shift tokens as mpv commands', () => {
|
test('compileSessionBindings leaves retired subtitle-delay shift tokens as mpv commands', () => {
|
||||||
const result = compileSessionBindings({
|
const result = compileSessionBindings({
|
||||||
shortcuts: createShortcuts(),
|
shortcuts: createShortcuts(),
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ function resolveCommandBinding(
|
|||||||
if (command.length !== 1) return null;
|
if (command.length !== 1) return null;
|
||||||
return { actionType: 'session-action', actionId: 'openJimaku' };
|
return { actionType: 'session-action', actionId: 'openJimaku' };
|
||||||
}
|
}
|
||||||
if (first === SPECIAL_COMMANDS.TSUKIHIME_OPEN) {
|
if (first === SPECIAL_COMMANDS.TSUKIHIME_OPEN || first === SPECIAL_COMMANDS.ANIMETOSHO_OPEN) {
|
||||||
if (command.length !== 1) return null;
|
if (command.length !== 1) return null;
|
||||||
return { actionType: 'session-action', actionId: 'openTsukihime' };
|
return { actionType: 'session-action', actionId: 'openTsukihime' };
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -132,6 +132,11 @@ export interface ShortcutsConfig {
|
|||||||
appendClipboardVideoToQueue?: string | null;
|
appendClipboardVideoToQueue?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RawShortcutsConfig extends ShortcutsConfig {
|
||||||
|
/** @deprecated Use openTsukihime. */
|
||||||
|
openAnimetosho?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
subtitlePosition?: SubtitlePosition;
|
subtitlePosition?: SubtitlePosition;
|
||||||
keybindings?: Keybinding[];
|
keybindings?: Keybinding[];
|
||||||
@@ -141,7 +146,7 @@ export interface Config {
|
|||||||
mpv?: MpvConfig;
|
mpv?: MpvConfig;
|
||||||
controller?: ControllerConfig;
|
controller?: ControllerConfig;
|
||||||
ankiConnect?: AnkiConnectConfig;
|
ankiConnect?: AnkiConnectConfig;
|
||||||
shortcuts?: ShortcutsConfig;
|
shortcuts?: RawShortcutsConfig;
|
||||||
secondarySub?: SecondarySubConfig;
|
secondarySub?: SecondarySubConfig;
|
||||||
subsync?: SubsyncConfig;
|
subsync?: SubsyncConfig;
|
||||||
startupWarmups?: StartupWarmupsConfig;
|
startupWarmups?: StartupWarmupsConfig;
|
||||||
@@ -149,6 +154,8 @@ export interface Config {
|
|||||||
subtitleSidebar?: SubtitleSidebarConfig;
|
subtitleSidebar?: SubtitleSidebarConfig;
|
||||||
auto_start_overlay?: boolean;
|
auto_start_overlay?: boolean;
|
||||||
jimaku?: JimakuConfig;
|
jimaku?: JimakuConfig;
|
||||||
|
/** @deprecated Use tsukihime. */
|
||||||
|
animetosho?: TsukihimeConfig;
|
||||||
tsukihime?: TsukihimeConfig;
|
tsukihime?: TsukihimeConfig;
|
||||||
anilist?: AnilistConfig;
|
anilist?: AnilistConfig;
|
||||||
yomitan?: YomitanConfig;
|
yomitan?: YomitanConfig;
|
||||||
|
|||||||
Reference in New Issue
Block a user