Require mpv plugin in first-run setup

This commit is contained in:
2026-04-03 00:07:57 -07:00
parent 78d0da03dd
commit 8a5805550f
6 changed files with 58 additions and 18 deletions

View File

@@ -21,7 +21,7 @@ Look up words with Yomitan, export to Anki in one key, track your immersion —
SubMiner runs as an invisible Electron overlay on top of mpv. Subtitles render as an interactive layer. Move your cursor over any word and trigger a [Yomitan](https://github.com/yomidevs/yomitan) lookup. Press one key to snapshot the sentence, audio, and screenshot into Anki via AnkiConnect. SubMiner runs as an invisible Electron overlay on top of mpv. Subtitles render as an interactive layer. Move your cursor over any word and trigger a [Yomitan](https://github.com/yomidevs/yomitan) lookup. Press one key to snapshot the sentence, audio, and screenshot into Anki via AnkiConnect.
On Windows, the recommended playback entry point is the optional `SubMiner mpv` shortcut created during setup. First-run setup requires the mpv plugin before it can finish. The shortcut launches `mpv` with SubMiner's defaults directly, so you do not need an `mpv.conf` profile just to use it. First-run setup requires the mpv plugin before it can finish. On Windows, the optional `SubMiner mpv` shortcut created during setup is the recommended playback entry point because it launches `mpv` with SubMiner's defaults directly, so you do not need an `mpv.conf` profile just to use it.
## Features ## Features

View File

@@ -2,4 +2,4 @@ type: changed
area: setup area: setup
- Made mpv plugin installation mandatory in the first-run setup flow, removed the skip path, and kept Finish disabled until the plugin is installed. - Made mpv plugin installation mandatory in the first-run setup flow, removed the skip path, and kept Finish disabled until the plugin is installed.
- Updated the Windows setup copy to make the `SubMiner mpv` shortcut the recommended playback entry point after setup completes. - Clarified that the mpv plugin requirement applies to setup on every platform, while the optional `SubMiner mpv` shortcut remains the recommended Windows playback entry point.

View File

@@ -171,7 +171,7 @@ Install `mpv` separately and ensure `mpv.exe` is on `PATH`. `ffmpeg` is still re
### Windows Usage Notes ### Windows Usage Notes
- Launch `SubMiner.exe` once to let the first-run setup flow seed `%APPDATA%\\SubMiner\\config.jsonc`, require mpv plugin installation, open bundled Yomitan settings, and optionally create `SubMiner mpv` Start Menu/Desktop shortcuts. On Windows, that shortcut is the recommended way to launch mpv playback with SubMiner defaults. - Launch `SubMiner.exe` once to let the first-run setup flow seed `%APPDATA%\\SubMiner\\config.jsonc`, require mpv plugin installation, and open bundled Yomitan settings. The optional `SubMiner mpv` Start Menu/Desktop shortcut can also be created during setup, and on Windows it is the recommended way to launch mpv playback with SubMiner defaults.
- `SubMiner.exe --launch-mpv` and the optional `SubMiner mpv` shortcut pass SubMiner's default mpv socket/subtitle args directly, including the Windows-safe subtitle search paths that skip the extra current-directory scan; they do not require an `mpv.conf` profile named `subminer`. - `SubMiner.exe --launch-mpv` and the optional `SubMiner mpv` shortcut pass SubMiner's default mpv socket/subtitle args directly, including the Windows-safe subtitle search paths that skip the extra current-directory scan; they do not require an `mpv.conf` profile named `subminer`.
- First-run mpv plugin installs pin `binary_path` to the current `SubMiner.exe` automatically. Manual plugin configs can leave `binary_path` empty unless SubMiner is installed in a non-standard location. - First-run mpv plugin installs pin `binary_path` to the current `SubMiner.exe` automatically. Manual plugin configs can leave `binary_path` empty unless SubMiner is installed in a non-standard location.
- Windows plugin installs rewrite `socket_path` to `\\.\pipe\subminer-socket`; do not keep `/tmp/subminer-socket` on Windows. - Windows plugin installs rewrite `socket_path` to `\\.\pipe\subminer-socket`; do not keep `/tmp/subminer-socket` on Windows.

View File

@@ -117,8 +117,10 @@ SubMiner.AppImage --help # Show all options
### Windows mpv Shortcut ### Windows mpv Shortcut
First-run setup requires the mpv plugin before it can finish.
If you enabled the optional Windows shortcut during install, SubMiner creates a `SubMiner mpv` shortcut in the Start menu and/or on the desktop. On Windows, that shortcut is the recommended way to launch local files with SubMiner because it starts `mpv.exe` with the right defaults directly. If you enabled the optional Windows shortcut during install, SubMiner creates a `SubMiner mpv` shortcut in the Start menu and/or on the desktop. On Windows, that shortcut is the recommended way to launch local files with SubMiner because it starts `mpv.exe` with the right defaults directly.
First-run setup requires the mpv plugin before it can finish, so the shortcut is the normal Windows playback entry point after setup completes. After setup completes, the shortcut is the normal Windows playback entry point.
You can use it three ways: You can use it three ways:

View File

@@ -251,6 +251,48 @@ test('setup service reopens when external-yomitan completion later has no extern
}); });
}); });
test('setup service reopens when a completed setup no longer has the mpv plugin installed', async () => {
await withTempDir(async (root) => {
const configDir = path.join(root, 'SubMiner');
fs.mkdirSync(configDir, { recursive: true });
fs.writeFileSync(path.join(configDir, 'config.jsonc'), '{}');
const completedService = createFirstRunSetupService({
configDir,
getYomitanDictionaryCount: async () => 2,
detectPluginInstalled: () => true,
installPlugin: async () => ({
ok: true,
pluginInstallStatus: 'installed',
pluginInstallPathSummary: '/tmp/mpv',
message: 'ok',
}),
onStateChanged: () => undefined,
});
await completedService.ensureSetupStateInitialized();
await completedService.markSetupCompleted();
const service = createFirstRunSetupService({
configDir,
getYomitanDictionaryCount: async () => 2,
detectPluginInstalled: () => false,
installPlugin: async () => ({
ok: true,
pluginInstallStatus: 'installed',
pluginInstallPathSummary: null,
message: 'ok',
}),
onStateChanged: () => undefined,
});
const snapshot = await service.ensureSetupStateInitialized();
assert.equal(snapshot.state.status, 'incomplete');
assert.equal(snapshot.canFinish, false);
assert.equal(snapshot.pluginStatus, 'required');
});
});
test('setup service keeps completed when external-yomitan completion later has internal dictionaries available', async () => { test('setup service keeps completed when external-yomitan completion later has internal dictionaries available', async () => {
await withTempDir(async (root) => { await withTempDir(async (root) => {
const configDir = path.join(root, 'SubMiner'); const configDir = path.join(root, 'SubMiner');

View File

@@ -272,24 +272,20 @@ export function createFirstRunSetupService(deps: {
getYomitanDictionaryCount: deps.getYomitanDictionaryCount, getYomitanDictionaryCount: deps.getYomitanDictionaryCount,
isExternalYomitanConfigured: deps.isExternalYomitanConfigured, isExternalYomitanConfigured: deps.isExternalYomitanConfigured,
}); });
const yomitanSetupSatisfied = isYomitanSetupSatisfied({ const pluginInstalled = await deps.detectPluginInstalled();
const canFinish =
pluginInstalled &&
isYomitanSetupSatisfied({
configReady, configReady,
dictionaryCount, dictionaryCount,
externalYomitanConfigured, externalYomitanConfigured,
}); });
if ( if (isSetupCompleted(state) && canFinish) {
isSetupCompleted(state) &&
!(
state.yomitanSetupMode === 'external' &&
!externalYomitanConfigured &&
!yomitanSetupSatisfied
)
) {
completed = true; completed = true;
return refreshWithState(state); return refreshWithState(state);
} }
if (yomitanSetupSatisfied) { if (canFinish) {
const completedState = writeState({ const completedState = writeState({
...state, ...state,
status: 'completed', status: 'completed',