diff --git a/README.md b/README.md index d2dfcfda..71754a35 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # SubMiner -Look up words with Yomitan, export to Anki in one key, track your immersion — all without leaving mpv. +Integrates Yomitan and mpv - on-screen lookups, mine to Anki, and track immersion without leaving the player [Installation](#quick-start) · [Requirements](#requirements) · [Usage](https://docs.subminer.moe/usage) · [Documentation](https://docs.subminer.moe) @@ -23,7 +23,7 @@ Look up words with Yomitan, export to Anki in one key, track your immersion — ### Dictionary Lookups -Yomitan runs inside the overlay. Trigger a lookup on any word for full dictionary popups — definitions, pitch accent, frequency data — without ever leaving mpv. +Hover over any word and trigger a lookup to get the full Yomitan popup - definitions, pitch accent, and frequency data - without ever leaving mpv.
Yomitan dictionary popup over annotated subtitles in mpv @@ -43,7 +43,7 @@ Create an Anki card with the sentence, audio clip, screenshot, and machine trans ### Reading Annotations -Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targeting, and a character name dictionary. Known words fade back; new words stand out. Grammar-only tokens render as plain text so you focus on what matters. +Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targeting, and a character name dictionary. Grammar-only tokens and particles render as plain text so you focus on what matters.
Annotated subtitles with frequency coloring, JLPT underlines, and N+1 targets @@ -53,7 +53,7 @@ Real-time subtitle annotations with frequency highlighting, JLPT tags, N+1 targe ### Immersion Dashboard -Local stats dashboard — watch time, anime library, vocabulary growth, mining throughput, session history, and trends. All stored locally, no third-party tracking. +Local stats dashboard tracking watch time, vocabulary growth, mining throughput, session history, and trends. All stored locally, no third-party tracking.
Stats dashboard showing watch time, cards mined, streaks, and tracking data @@ -92,11 +92,11 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open alass / ffsubsync - Automatic subtitle retiming — requires alass or ffsubsync on your PATH (optional; subtitle syncing is disabled without them) + Manual subtitle retiming — requires alass or ffsubsync on your PATH (optional; subtitle syncing is disabled without them) WebSocket - Annotated subtitle feed for external clients (texthooker pages, custom tools) + Plain subtitle feed plus a dedicated annotated feed for texthooker pages and custom tools @@ -110,65 +110,36 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open ## Requirements -| | Required | Recommended | Optional | -| -------------- | --------------------------------------- | ------------------------------------ | --------------------------------------------------------------------------------------------------------------- | -| **Player** | [`mpv`](https://mpv.io) with IPC socket | — | — | -| **Processing** | — | `ffmpeg` (audio clips & screenshots) | `mecab` + `mecab-ipadic` (annotation POS filtering), `guessit` (AniSkip), `alass` / `ffsubsync` (subtitle sync) | -| **Media** | — | — | `yt-dlp`, `chafa`, `ffmpegthumbnailer` | -| **Selection** | — | — | `fzf` / `rofi` | +Only **mpv** and Anki+AnkiConnect are required. Everything else is optional but enhances the experience. -> [!TIP] -> `ffmpeg` is not strictly required to run SubMiner, but without it audio clips and screenshots will not be attached to Anki cards. Most users will want it installed. - -> [!NOTE] -> [`bun`](https://bun.sh) is required if building from source or using the CLI wrapper: `subminer`. Pre-built releases (AppImage, DMG, installer) do not require it. - -**Platform-specific:** - -| Linux | macOS | Windows | -| ------------------------------------------------------------ | ------------------------ | ------------- | -| Hyprland (`hyprctl`) · X11/Xwayland (`xdotool` + `xwininfo`) | Accessibility permission | No extra deps | - -> [!NOTE] -> **Wayland support is compositor-specific.** Wayland has no universal API for window positioning and each compositor exposes its own IPC, so SubMiner needs a dedicated backend per compositor. Hyprland is the only native Wayland backend supported currenlty. All other Linux compositors require both mpv and SubMiner to run under X11 or Xwayland. The launcher detects your compositor and configures this automatically. +| Dependency | Status | What it does | +| -------------------- | ----------- | ---------------------------------------- | +| mpv | Required | The video player SubMiner overlays on | +| Anki + AnkiConnect | Required | Card creation from the Yomitan popup | +| ffmpeg | Recommended | Audio clips & screenshots for Anki cards | +| MeCab + mecab-ipadic | Recommended | More precise annotations and filtering | +| yt-dlp | Optional | YouTube playback | +| fzf / rofi | Optional | Video picker in the launcher | +| alass / ffsubsync | Optional | Subtitle sync |
-Arch Linux +Platform-specific install commands + +**Arch Linux:** ```bash -paru -S --needed mpv ffmpeg -# Optional -paru -S --needed mecab-git mecab-ipadic yt-dlp fzf rofi chafa ffmpegthumbnailer xdotool xorg-xwininfo -# Optional: subtitle sync (install at least one for subtitle syncing to work) -paru -S --needed alass python-ffsubsync -# X11 / Xwayland (required for non-Hyprland compositors) -paru -S --needed xdotool xorg-xwininfo +sudo pacman -S --needed mpv ffmpeg mecab mecab-ipadic ``` -
- -
-macOS +**macOS:** ```bash -brew install mpv ffmpeg -# Optional -brew install mecab mecab-ipadic yt-dlp fzf rofi chafa ffmpegthumbnailer -# Optional: subtitle sync (install at least one for subtitle syncing to work) -brew install alass -pip install ffsubsync +brew install mpv ffmpeg mecab mecab-ipadic ``` -Grant Accessibility permission to SubMiner in **System Settings > Privacy & Security > Accessibility**. +**Windows:** Install [mpv](https://mpv.io/installation/) and [ffmpeg](https://ffmpeg.org/download.html) and ensure both are on `PATH`. -
- -
-Windows - -Install [`mpv`](https://mpv.io/installation/) and [`ffmpeg`](https://ffmpeg.org/download.html) and ensure both are on your `PATH`. - -Optionally install [MeCab for Windows](https://taku910.github.io/mecab/#download) with the UTF-8 dictionary for additional metadata enrichment. +See the [full requirements list](https://docs.subminer.moe/installation#1-install-requirements) for optional dependencies.
@@ -176,7 +147,7 @@ Optionally install [MeCab for Windows](https://taku910.github.io/mecab/#download ## Quick Start -### 1. Install +### 1. Install SubMiner
Arch Linux (AUR) @@ -185,12 +156,6 @@ Optionally install [MeCab for Windows](https://taku910.github.io/mecab/#download paru -S subminer-bin ``` -Or manually: - -```bash -git clone https://aur.archlinux.org/subminer-bin.git && cd subminer-bin && makepkg -si -``` -
@@ -199,40 +164,24 @@ git clone https://aur.archlinux.org/subminer-bin.git && cd subminer-bin && makep ```bash mkdir -p ~/.local/bin wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage \ - && chmod +x ~/.local/bin/SubMiner.AppImage + && chmod +x ~/.local/bin/SubMiner.AppImage wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer \ - && chmod +x ~/.local/bin/subminer + && chmod +x ~/.local/bin/subminer ``` -> [!NOTE] -> The `subminer` wrapper uses a [Bun](https://bun.sh) shebang. First-run setup can optionally install Bun and the launcher into an existing writable PATH directory. -
-macOS +macOS (DMG) -Download the latest DMG or ZIP from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`. - -Also download the `subminer` launcher (recommended): - -```bash -mkdir -p ~/.local/bin -curl -fSL https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -o ~/.local/bin/subminer \ - && chmod +x ~/.local/bin/subminer -``` - -> [!NOTE] -> The `subminer` launcher uses a [Bun](https://bun.sh) shebang. First-run setup can optionally install Bun and the launcher into an existing writable PATH directory. Make sure `~/.local/bin` is on your PATH before installing there. +Download the latest DMG from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest) and drag `SubMiner.app` into `/Applications`.
Windows -Download the latest installer (`.exe`) [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest). Make sure `mpv` is on your `PATH`. - -**Note:** On Windows the recommended way to run playback is with the **SubMiner mpv** shortcut created during first-run setup. First-run setup can also optionally install Bun and a `subminer.cmd` command shim to your user PATH, so new terminals can run `subminer` without adding `SubMiner.exe` to PATH. +Download and run the latest installer (`.exe`) from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest).
@@ -243,28 +192,29 @@ See the [build-from-source guide](https://docs.subminer.moe/installation#from-so -### 2. First Launch +### 2. Launch & Set Up + +Run SubMiner and the first-run setup wizard will guide you through importing Yomitan dictionaries and optionally installing the `subminer` command-line launcher. ```bash -subminer app --setup # launch the first-run setup wizard +# Linux +subminer app --setup + +# macOS — open SubMiner.app, or: +subminer app --setup ``` -SubMiner creates a default config, starts in the system tray, and opens a setup popup that walks you through installing Yomitan dictionaries. The setup popup can also optionally install Bun and the `subminer` command-line launcher; those choices do not block setup completion. - -> [!NOTE] -> On Windows, run `SubMiner.exe` directly — it opens the setup wizard automatically on first launch. +On **Windows**, just run `SubMiner.exe` and the setup will open automatically on first launch. ### 3. Mine ```bash -subminer video.mkv # play video with overlay -subminer --start video.mkv # explicit overlay start -subminer stats # open immersion dashboard -subminer stats -b # stats daemon in background -subminer stats -s # stop background stats daemon +subminer video.mkv # launch mpv with SubMiner +subminer /path/to/dir # pick a file with fzf +subminer -R /path/to/dir # pick a file with rofi (Linux only) ``` -On **Windows**, use the **SubMiner mpv** shortcut created during first-run setup — double-click it to open mpv, or drag a video file onto it. You can also run `SubMiner.exe --launch-mpv` from a terminal. +On **Windows**, use the **SubMiner mpv** shortcut created during setup. Double-click it or drag a video file onto it. ## Documentation diff --git a/app-updater.js b/app-updater.js deleted file mode 100644 index 4709efa2..00000000 --- a/app-updater.js +++ /dev/null @@ -1,193 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.resolveMacAppBundlePath = resolveMacAppBundlePath; -exports.isMacApplicationsFolderBundle = isMacApplicationsFolderBundle; -exports.isKnownLinuxPackageManagedAppImage = isKnownLinuxPackageManagedAppImage; -exports.isNativeUpdaterSupported = isNativeUpdaterSupported; -exports.configureAutoUpdater = configureAutoUpdater; -exports.createElectronAppUpdater = createElectronAppUpdater; -const node_fs_1 = require("node:fs"); -const node_child_process_1 = require("node:child_process"); -const node_os_1 = __importDefault(require("node:os")); -const node_path_1 = __importDefault(require("node:path")); -const node_util_1 = require("node:util"); -const electron_updater_1 = require("electron-updater"); -const release_assets_1 = require("./release-assets"); -const updaterErrorListeners = new WeakMap(); -const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile); -function resolveMacAppBundlePath(execPath) { - const marker = '.app/Contents/MacOS/'; - const markerIndex = execPath.indexOf(marker); - if (markerIndex < 0) - return null; - return execPath.slice(0, markerIndex + '.app'.length); -} -async function readMacCodeSignature(appBundlePath) { - try { - const result = await execFileAsync('/usr/bin/codesign', ['-dv', '--verbose=4', appBundlePath], { - encoding: 'utf8', - }); - return `${result.stdout ?? ''}\n${result.stderr ?? ''}`; - } - catch { - return null; - } -} -function realpathOrOriginal(filePath) { - try { - return (0, node_fs_1.realpathSync)(filePath); - } - catch { - return filePath; - } -} -function isSameOrInsideDirectory(parentPath, candidatePath) { - const relative = node_path_1.default.relative(parentPath, candidatePath); - return (relative === '' || - (relative.length > 0 && !relative.startsWith('..') && !node_path_1.default.isAbsolute(relative))); -} -function isMacApplicationsFolderBundle(appBundlePath, homeDir = node_os_1.default.homedir()) { - const resolvedBundlePath = node_path_1.default.resolve(appBundlePath); - return (isSameOrInsideDirectory('/Applications', resolvedBundlePath) || - isSameOrInsideDirectory(node_path_1.default.join(homeDir, 'Applications'), resolvedBundlePath)); -} -function isKnownLinuxPackageManagedAppImage(appImagePath) { - return realpathOrOriginal(appImagePath) === '/opt/SubMiner/SubMiner.AppImage'; -} -async function isNativeUpdaterSupported(options) { - if (!options.isPackaged) { - options.log?.('Skipping native updater because this build is not packaged.'); - return false; - } - if (options.platform === 'linux') { - options.log?.('Skipping native Linux updater because Linux tray checks use GitHub release assets.'); - return false; - } - if (options.platform !== 'darwin') { - options.log?.('Skipping native updater because this platform uses GitHub metadata checks.'); - return false; - } - const appBundlePath = resolveMacAppBundlePath(options.execPath); - if (!appBundlePath) { - options.log?.('Skipping native macOS updater because the app bundle path could not be resolved.'); - return false; - } - if (!isMacApplicationsFolderBundle(appBundlePath, options.homeDir)) { - options.log?.('Skipping native macOS updater because the app is not installed in an Applications folder.'); - return false; - } - const signature = await (options.readCodeSignature ?? readMacCodeSignature)(appBundlePath); - if (!signature) { - options.log?.('Skipping native macOS updater because the app code signature could not be read.'); - return false; - } - if (/Signature=adhoc\b/.test(signature) || /TeamIdentifier=not set\b/.test(signature)) { - options.log?.('Skipping native macOS updater because this build is ad-hoc signed.'); - return false; - } - return true; -} -function configureAutoUpdater(updater, log = () => { }, channel = 'stable') { - updater.autoDownload = false; - // On macOS this avoids invoking Squirrel until the explicit restart/install step. - updater.autoInstallOnAppQuit = false; - updater.allowPrerelease = channel === 'prerelease'; - updater.allowDowngrade = false; - updater.logger = { - info: () => { }, - debug: () => { }, - warn: (message) => log(message), - error: (message) => log(message), - }; - const previousErrorListener = updaterErrorListeners.get(updater); - if (previousErrorListener) { - if (updater.off) { - updater.off('error', previousErrorListener); - } - else { - updater.removeListener?.('error', previousErrorListener); - } - } - if (updater.on) { - const errorListener = (error) => { - const message = error instanceof Error ? error.message : String(error); - log(`Updater error event: ${message}`); - }; - updater.on('error', errorListener); - updaterErrorListeners.set(updater, errorListener); - } - return updater; -} -function createElectronAppUpdater(options) { - const getChannel = options.getChannel ?? (() => 'stable'); - const updater = configureAutoUpdater(options.updater ?? electron_updater_1.autoUpdater, options.log, getChannel()); - if (options.configureHttpExecutor) { - // electron-updater has no public executor hook; keep the macOS cURL override localized. - updater.httpExecutor = options.configureHttpExecutor(); - } - if (options.disableDifferentialDownload !== undefined) { - updater.disableDifferentialDownload = options.disableDifferentialDownload; - } - let nativeUpdaterSupported = null; - async function getNativeUpdaterSupported() { - if (!options.isNativeUpdaterSupported) - return true; - if (nativeUpdaterSupported === null) { - nativeUpdaterSupported = Promise.resolve(options.isNativeUpdaterSupported()); - } - return nativeUpdaterSupported; - } - return { - async checkForUpdates(channel) { - if (!options.isPackaged) { - return { - available: false, - version: options.currentVersion, - canUpdate: false, - }; - } - if (!(await getNativeUpdaterSupported())) { - options.log('Skipping native app update check because native updater is unsupported.'); - return { - available: false, - version: options.currentVersion, - canUpdate: false, - }; - } - configureAutoUpdater(updater, options.log, channel ?? getChannel()); - const result = await updater.checkForUpdates(); - const version = result?.updateInfo?.version ?? options.currentVersion; - return { - available: (0, release_assets_1.compareSemverLike)(version, options.currentVersion) > 0, - version, - canUpdate: true, - }; - }, - async downloadUpdate() { - if (!options.isPackaged) { - options.log('Skipping app update download because this build is not packaged.'); - return; - } - if (!(await getNativeUpdaterSupported())) { - options.log('Skipping app update download because native updater is unsupported.'); - return; - } - await updater.downloadUpdate(); - }, - async quitAndInstall() { - if (!options.isPackaged) { - options.log('Skipping app update install because this build is not packaged.'); - return; - } - if (!(await getNativeUpdaterSupported())) { - options.log('Skipping app update install because native updater is unsupported.'); - return; - } - updater.quitAndInstall(false, true); - }, - }; -} -//# sourceMappingURL=app-updater.js.map diff --git a/changes/aniskip-key-learn.md b/changes/aniskip-key-learn.md new file mode 100644 index 00000000..5243999f --- /dev/null +++ b/changes/aniskip-key-learn.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Settings: Changed the AniSkip button key setting to use click-to-learn key capture instead of raw text entry. diff --git a/changes/background-launcher-reuse.md b/changes/background-launcher-reuse.md new file mode 100644 index 00000000..8956e279 --- /dev/null +++ b/changes/background-launcher-reuse.md @@ -0,0 +1,4 @@ +type: fixed +area: launcher + +- Reused an already-running background SubMiner app for launcher-opened videos, closed launcher-owned tray apps after playback ends, and reapplied preferred subtitles for warm launches. diff --git a/changes/config-example-settings-css.md b/changes/config-example-settings-css.md new file mode 100644 index 00000000..d0b2b787 --- /dev/null +++ b/changes/config-example-settings-css.md @@ -0,0 +1,4 @@ +type: fixed +area: config + +- Updated the generated example config to use the same CSS declaration paths written by the Settings window for subtitle and sidebar appearance. diff --git a/changes/config-load-preserves-user-files.md b/changes/config-load-preserves-user-files.md new file mode 100644 index 00000000..5ab26e50 --- /dev/null +++ b/changes/config-load-preserves-user-files.md @@ -0,0 +1,4 @@ +type: fixed +area: config + +- Preserved user config files during legacy config compatibility handling. diff --git a/changes/config-settings-controls.md b/changes/config-settings-controls.md new file mode 100644 index 00000000..9f919be4 --- /dev/null +++ b/changes/config-settings-controls.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Reorganized the Settings window into clearer Appearance, Behavior, Anki, input, and integration sections with learned keybinding controls and AnkiConnect-backed deck, field, and note-type pickers. diff --git a/changes/config-settings-option-labels.md b/changes/config-settings-option-labels.md new file mode 100644 index 00000000..1a7fb00e --- /dev/null +++ b/changes/config-settings-option-labels.md @@ -0,0 +1,4 @@ +type: changed +area: settings + +- Simplified configuration option rows by hiding raw config paths and placing the live/restart status beside each option title. diff --git a/changes/config-settings-search.md b/changes/config-settings-search.md new file mode 100644 index 00000000..516993f9 --- /dev/null +++ b/changes/config-settings-search.md @@ -0,0 +1,4 @@ +type: fixed +area: config + +- Fixed Settings window search so it searches across all categories, narrows on multi-word terms, hides settings owned by richer editors, and no longer shows the Open File button. diff --git a/changes/config-settings-window.md b/changes/config-settings-window.md index 26d8d662..0b6bde3c 100644 --- a/changes/config-settings-window.md +++ b/changes/config-settings-window.md @@ -1,6 +1,8 @@ type: added area: config -- Added a dedicated Configuration window with launcher entry points via `subminer --config` and `subminer config`. -- Fixed the Configuration window preload so launcher-opened windows can initialize even when Electron sandboxing is active. -- Kept config-window startup lightweight by skipping AniList token refresh and automatic update polling. +- Added a dedicated Settings window with launcher entry points via `subminer --settings` and `subminer settings`. +- Fixed the Settings window preload so launcher-opened windows can initialize even when Electron sandboxing is active. +- Kept settings-window startup lightweight by skipping AniList token refresh and automatic update polling. +- Marked safe live config options in the Settings window and expanded hot reload for stats keys, logging level, Jimaku, Subsync, YouTube language defaults, Anki media/sentence/misc field mappings, sentence card model, and selected Anki annotation/runtime options. +- Hid AI and translation fields from the Settings window while keeping them supported in config files. diff --git a/changes/default-opt-in-warmup-name-highlighting.md b/changes/default-opt-in-warmup-name-highlighting.md new file mode 100644 index 00000000..9c326dc3 --- /dev/null +++ b/changes/default-opt-in-warmup-name-highlighting.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Defaulted Jellyfin remote-session startup warmup and character-name subtitle highlighting to off. diff --git a/changes/fix-autoplay-subtitle-prime.md b/changes/fix-autoplay-subtitle-prime.md new file mode 100644 index 00000000..8b34d453 --- /dev/null +++ b/changes/fix-autoplay-subtitle-prime.md @@ -0,0 +1,4 @@ +type: fixed +area: overlay + +- Primed the first startup subtitle before autoplay resumes so the overlay can render text before video playback begins. diff --git a/changes/fix-background-attach-autoplay-gate.md b/changes/fix-background-attach-autoplay-gate.md new file mode 100644 index 00000000..fa64bb5b --- /dev/null +++ b/changes/fix-background-attach-autoplay-gate.md @@ -0,0 +1,5 @@ +type: fixed +area: launcher + +- Kept launcher-opened videos paused when attaching to an already-running background app until subtitle priming and tokenization readiness complete. +- Moved mpv plugin subtitle auto-selection to pre-load so launch-time subtitle choices are not reset after the video opens. diff --git a/changes/fix-determiner-noun-frequency.md b/changes/fix-determiner-noun-frequency.md new file mode 100644 index 00000000..a075c01a --- /dev/null +++ b/changes/fix-determiner-noun-frequency.md @@ -0,0 +1,4 @@ +type: fixed +area: subtitles + +- Kept frequency highlighting for determiner-led noun compounds like `その場` while still filtering standalone determiners. diff --git a/changes/fix-known-words-decks-row-overflow.md b/changes/fix-known-words-decks-row-overflow.md new file mode 100644 index 00000000..467a358d --- /dev/null +++ b/changes/fix-known-words-decks-row-overflow.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Reorganized each known-words deck row in the Settings window into a card with the deck name on its own header line so longer deck names stay readable instead of being truncated. diff --git a/changes/fix-launcher-electron-menu-diagnostic.md b/changes/fix-launcher-electron-menu-diagnostic.md new file mode 100644 index 00000000..de4cc3bc --- /dev/null +++ b/changes/fix-launcher-electron-menu-diagnostic.md @@ -0,0 +1,4 @@ +type: fixed +area: launcher + +- Suppressed Electron macOS menu diagnostics from `subminer settings` launcher output. diff --git a/changes/fix-macos-mpv-shortcuts.md b/changes/fix-macos-mpv-shortcuts.md new file mode 100644 index 00000000..47fac5df --- /dev/null +++ b/changes/fix-macos-mpv-shortcuts.md @@ -0,0 +1,4 @@ +type: fixed +area: shortcuts + +- Disabled native mpv menu shortcuts during managed macOS playback so configured SubMiner shortcuts also work while mpv has focus. diff --git a/changes/fix-managed-playback-overlay-lifecycle.md b/changes/fix-managed-playback-overlay-lifecycle.md new file mode 100644 index 00000000..d88dab2e --- /dev/null +++ b/changes/fix-managed-playback-overlay-lifecycle.md @@ -0,0 +1,4 @@ +type: fixed +area: playback + +- Fixed managed mpv startup so launcher-owned videos quit SubMiner when playback ends, background/tray sessions stay alive, and pause-until-ready waits for the overlay and tokenization readiness before playback resumes. diff --git a/changes/hide-setup-runtime-plugin.md b/changes/hide-setup-runtime-plugin.md new file mode 100644 index 00000000..2274043d --- /dev/null +++ b/changes/hide-setup-runtime-plugin.md @@ -0,0 +1,4 @@ +type: changed +area: setup + +- Setup: Removed the bundled mpv runtime plugin readiness card; legacy mpv plugin removal still appears when needed. diff --git a/changes/known-word-color-config.md b/changes/known-word-color-config.md new file mode 100644 index 00000000..016e6bd1 --- /dev/null +++ b/changes/known-word-color-config.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Config: Moved known-word and N+1 annotation colors to `subtitleStyle.knownWordColor` and `subtitleStyle.nPlusOneColor`; legacy Anki color keys are still accepted with warnings. diff --git a/changes/linux-tray-appimage-update.md b/changes/linux-tray-appimage-update.md new file mode 100644 index 00000000..f6be21e0 --- /dev/null +++ b/changes/linux-tray-appimage-update.md @@ -0,0 +1,5 @@ +type: changed +area: updater + +- Linux tray "Check for Updates" now installs the new AppImage automatically via `electron-updater`, matching the macOS and Windows tray flow, instead of stopping at a "manual update required" dialog. AppImages managed by a system package (AUR `/opt/SubMiner/SubMiner.AppImage`) and non-AppImage launches (no `APPIMAGE` env) still fall back to the GitHub-asset flow. +- Routed `electron-updater` HTTP through `/usr/bin/curl` on Linux and disabled differential downloads, matching the macOS path, so background update checks stay off Electron's network service. diff --git a/changes/linux-updater-curl-fetch.md b/changes/linux-updater-curl-fetch.md new file mode 100644 index 00000000..1973ddcc --- /dev/null +++ b/changes/linux-updater-curl-fetch.md @@ -0,0 +1,4 @@ +type: fixed +area: updater + +- Fixed Linux automatic update checks to avoid Electron networking, preventing native Electron network-service crashes during video startup. diff --git a/changes/macos-config-window-exit.md b/changes/macos-config-window-exit.md new file mode 100644 index 00000000..3de4ba7c --- /dev/null +++ b/changes/macos-config-window-exit.md @@ -0,0 +1,4 @@ +type: fixed +area: launcher + +- macOS `subminer settings` launches now exit cleanly after the settings window is closed, returning control to the terminal without requiring Ctrl+C. diff --git a/changes/macos-update-dialog-activation.md b/changes/macos-update-dialog-activation.md new file mode 100644 index 00000000..72ea2d64 --- /dev/null +++ b/changes/macos-update-dialog-activation.md @@ -0,0 +1,4 @@ +type: fixed +area: updater + +- macOS update dialogs triggered by `subminer -u` now reliably appear in the foreground. SubMiner now shows the dock icon and activates itself via `osascript` (LaunchServices) before opening the modal alert; `app.focus({ steal: true })` alone was unreliable when SubMiner was reached through single-instance forwarding from the CLI-spawned child, leaving the dialog stranded behind other apps with a bouncing dock icon. diff --git a/changes/macos-updater-curl-fetch.md b/changes/macos-updater-curl-fetch.md new file mode 100644 index 00000000..7981b478 --- /dev/null +++ b/changes/macos-updater-curl-fetch.md @@ -0,0 +1,4 @@ +type: fixed +area: updater + +- Routed macOS supplemental GitHub release lookups through `/usr/bin/curl` instead of Electron `net.fetch`, eliminating the last Electron-networking path from background update checks and avoiding the network-service crashes seen in earlier prereleases. diff --git a/changes/mpv-config-unified.md b/changes/mpv-config-unified.md new file mode 100644 index 00000000..2264b770 --- /dev/null +++ b/changes/mpv-config-unified.md @@ -0,0 +1,4 @@ +type: added +area: launcher + +- Managed bundled mpv plugin startup options from SubMiner config. diff --git a/changes/mpv-session-shortcuts.md b/changes/mpv-session-shortcuts.md new file mode 100644 index 00000000..8fbb6013 --- /dev/null +++ b/changes/mpv-session-shortcuts.md @@ -0,0 +1,4 @@ +type: fixed +area: overlay + +- Wired configured session shortcuts, including `stats.markWatchedKey`, through mpv so custom add/remove changes work while mpv has focus. diff --git a/changes/note-fields-default-note-type.md b/changes/note-fields-default-note-type.md new file mode 100644 index 00000000..9f46ec63 --- /dev/null +++ b/changes/note-fields-default-note-type.md @@ -0,0 +1,4 @@ +type: fixed +area: config + +- Defaulted the note-fields note type picker to the configured Anki deck's note type when available, then exact `Kiku`, then exact `Lapis`, otherwise leaving it blank for manual selection. diff --git a/changes/nplusone-known-word-compat.md b/changes/nplusone-known-word-compat.md new file mode 100644 index 00000000..1dc5c555 --- /dev/null +++ b/changes/nplusone-known-word-compat.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Config: Preserved N+1 subtitle highlighting for existing configs that already enabled known-word highlighting, while keeping N+1 disabled by default for new configs unless `ankiConnect.nPlusOne.enabled` is set. diff --git a/changes/overlay-restart-visible.md b/changes/overlay-restart-visible.md new file mode 100644 index 00000000..e07629b6 --- /dev/null +++ b/changes/overlay-restart-visible.md @@ -0,0 +1,4 @@ +type: fixed +area: overlay + +- Kept the visible overlay and subtitle stream alive after restarting SubMiner from the mpv `y-r` shortcut by transporting Linux AppImage control args safely, restoring mpv subtitle visibility during shutdown, snapshotting subtitles before overlay suppression resumes, reapplying Linux overlay bounds after the restarted window maps, allowing Hyprland to resize the visible overlay window, and preserving user-paused playback while readiness gates clear. diff --git a/changes/plain-websocket-annotations.md b/changes/plain-websocket-annotations.md new file mode 100644 index 00000000..c883df74 --- /dev/null +++ b/changes/plain-websocket-annotations.md @@ -0,0 +1,4 @@ +type: fixed +area: websocket + +- WebSocket: Kept the regular subtitle websocket plain-text only; annotation spans and token metadata now stay on the annotation websocket. diff --git a/changes/rename-config-window-to-settings.md b/changes/rename-config-window-to-settings.md new file mode 100644 index 00000000..2eb171b4 --- /dev/null +++ b/changes/rename-config-window-to-settings.md @@ -0,0 +1,7 @@ +type: changed +area: launcher +breaking: true + +- Renamed the SubMiner Configuration window to the Settings window across the UI, tray menu, docs, and CLI verbiage. +- Replaced the `--config` flag and `subminer config` (no action) entry points with `--settings` and `subminer settings`. The `subminer config` subcommand now only accepts `path` or `show`. +- Removed the `--settings` alias that previously opened the bundled Yomitan settings popup. Use `--yomitan` to open Yomitan settings. diff --git a/changes/settings-modal-layout.md b/changes/settings-modal-layout.md new file mode 100644 index 00000000..8ec6c8b2 --- /dev/null +++ b/changes/settings-modal-layout.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Settings: reorganized playback, shortcut, WebSocket, tracking, Jellyfin, character dictionary, and Discord presence controls in the settings modal. diff --git a/changes/setup-subminer-settings-button.md b/changes/setup-subminer-settings-button.md new file mode 100644 index 00000000..8d0b9195 --- /dev/null +++ b/changes/setup-subminer-settings-button.md @@ -0,0 +1,4 @@ +type: added +area: setup + +- Setup: Added an Open SubMiner Settings button to first-run setup and moved Finish setup to the right-side action slot. diff --git a/changes/subsync-manual-only.md b/changes/subsync-manual-only.md new file mode 100644 index 00000000..41cb3fbd --- /dev/null +++ b/changes/subsync-manual-only.md @@ -0,0 +1,4 @@ +type: changed +area: subtitles + +- Subsync now always opens the manual picker and the `subsync.defaultMode` config/settings option has been removed. diff --git a/changes/subtitle-css-appearance-editor.md b/changes/subtitle-css-appearance-editor.md new file mode 100644 index 00000000..cc4532ae --- /dev/null +++ b/changes/subtitle-css-appearance-editor.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Config: Primary and secondary subtitle appearance now use color controls plus CSS declaration editors, saved as `subtitleStyle.css` and `subtitleStyle.secondary.css`. diff --git a/changes/subtitle-css-hover-migration.md b/changes/subtitle-css-hover-migration.md new file mode 100644 index 00000000..73f47a20 --- /dev/null +++ b/changes/subtitle-css-hover-migration.md @@ -0,0 +1,4 @@ +type: fixed +area: config + +- Migrated legacy subtitle hover token colors into `subtitleStyle.css` instead of leaving `hoverTokenColor` or `hoverTokenBackgroundColor` behind. diff --git a/changes/subtitle-css-legacy-migration.md b/changes/subtitle-css-legacy-migration.md new file mode 100644 index 00000000..be2b42f6 --- /dev/null +++ b/changes/subtitle-css-legacy-migration.md @@ -0,0 +1,4 @@ +type: fixed +area: config + +- Migrated legacy primary and secondary subtitle appearance options into `subtitleStyle.css` automatically when loading config files. diff --git a/changes/subtitle-css-live-settings.md b/changes/subtitle-css-live-settings.md new file mode 100644 index 00000000..132789f8 --- /dev/null +++ b/changes/subtitle-css-live-settings.md @@ -0,0 +1,4 @@ +type: fixed +area: config + +- Fixed live Settings window saves so primary and secondary subtitle CSS declarations apply immediately to open video overlays. diff --git a/changes/subtitle-sidebar-css.md b/changes/subtitle-sidebar-css.md new file mode 100644 index 00000000..28bbaa70 --- /dev/null +++ b/changes/subtitle-sidebar-css.md @@ -0,0 +1,4 @@ +type: changed +area: config + +- Added `subtitleSidebar.css`, migrated legacy sidebar appearance fields into it, and updated subtitle font defaults to `Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP`. diff --git a/changes/yomitan-popup-late-load.md b/changes/yomitan-popup-late-load.md new file mode 100644 index 00000000..65940ab5 --- /dev/null +++ b/changes/yomitan-popup-late-load.md @@ -0,0 +1,4 @@ +type: fixed +area: overlay + +- Fixed Yomitan popups not opening when playback/overlay startup races the Yomitan extension load. diff --git a/config.example.jsonc b/config.example.jsonc index 705fc2aa..393e2f29 100644 --- a/config.example.jsonc +++ b/config.example.jsonc @@ -7,10 +7,11 @@ { // ========================================== - // Overlay Auto-Start - // When overlay connects to mpv, automatically show overlay and hide mpv subtitles. + // Visible Overlay Auto-Start + // Show the visible subtitle overlay automatically after managed mpv playback starts SubMiner. + // SubMiner can still auto-start in the background when this is false. // ========================================== - "auto_start_overlay": false, // Auto-start the subtitle overlay window when SubMiner launches. Values: true | false + "auto_start_overlay": true, // Show the visible subtitle overlay automatically when the bundled mpv plugin starts SubMiner. Values: true | false // ========================================== // Texthooker Server @@ -45,6 +46,7 @@ // Logging // Controls logging verbosity. // Set to debug for full runtime diagnostics. + // Hot-reload: logging.level applies live while SubMiner is running. // ========================================== "logging": { "level": "info" // Minimum log level for runtime logging. Values: debug | info | warn | error @@ -153,7 +155,7 @@ "mecab": true, // Warm up MeCab tokenizer at startup. Values: true | false "yomitanExtension": true, // Warm up Yomitan extension at startup. Values: true | false "subtitleDictionaries": true, // Warm up subtitle dictionaries at startup. Values: true | false - "jellyfinRemoteSession": true // Warm up Jellyfin remote session at startup. Values: true | false + "jellyfinRemoteSession": false // Warm up Jellyfin remote session at startup. Values: true | false }, // Background warmup controls for MeCab, Yomitan, dictionaries, and Jellyfin session. // ========================================== @@ -334,11 +336,11 @@ }, // Dual subtitle track options. // ========================================== - // Auto Subtitle Sync + // Subtitle Sync // Subsync engine and executable paths. + // Hot-reload: subsync changes apply to the next subtitle sync run. // ========================================== "subsync": { - "defaultMode": "auto", // Subsync default mode. Values: auto | manual "alass_path": "", // Optional absolute path to the alass binary used by subsync. Leave empty to auto-discover from PATH. "ffsubsync_path": "", // Optional absolute path to the ffsubsync binary used by subsync. Leave empty to auto-discover from PATH. "ffmpeg_path": "", // Optional absolute path to the ffmpeg binary used by subsync. Leave empty to auto-discover from PATH. @@ -360,29 +362,31 @@ // ========================================== "subtitleStyle": { "primaryDefaultMode": "visible", // Default primary subtitle bar visibility mode. hidden hides it, visible shows it, hover reveals it on hover. Values: hidden | visible | hover + "css": { + "font-family": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", // Font family setting. + "color": "#cad3f5", // Color setting. + "background-color": "transparent", // Background color setting. + "font-size": "35px", // Font size setting. + "font-weight": "600", // Font weight setting. + "font-style": "normal", // Font style setting. + "line-height": "1.35", // Line height setting. + "letter-spacing": "-0.01em", // Letter spacing setting. + "word-spacing": "0", // Word spacing setting. + "font-kerning": "normal", // Font kerning setting. + "text-rendering": "geometricPrecision", // Text rendering setting. + "text-shadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)", // Text shadow setting. + "backdrop-filter": "blur(6px)", // Backdrop filter setting. + "--subtitle-hover-token-color": "#f4dbd6", // Subtitle hover token color setting. + "--subtitle-hover-token-background-color": "transparent" // Subtitle hover token background color setting. + }, // CSS declaration object applied to primary subtitles after normal subtitle style defaults. "enableJlpt": false, // Enable JLPT vocabulary level underlines. When disabled, JLPT tagging lookup and underlines are skipped. Values: true | false "preserveLineBreaks": false, // Preserve line breaks in visible overlay subtitle rendering. When false, line breaks are flattened to spaces for a single-line flow. Values: true | false "autoPauseVideoOnHover": true, // Automatically pause mpv playback while hovering subtitle text, then resume on leave. Values: true | false "autoPauseVideoOnYomitanPopup": true, // Automatically pause mpv playback while Yomitan popup is open, then resume when popup closes. Values: true | false - "hoverTokenColor": "#f4dbd6", // Hex color used for hovered subtitle token highlight in mpv. - "hoverTokenBackgroundColor": "rgba(54, 58, 79, 0.84)", // CSS color used for hovered subtitle token background highlight in mpv. - "nameMatchEnabled": true, // Enable subtitle token coloring for matches from the SubMiner character dictionary. Values: true | false + "nameMatchEnabled": false, // Enable subtitle token coloring for matches from the SubMiner character dictionary. Values: true | false "nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary. - "fontFamily": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", // Font family setting. - "fontSize": 35, // Font size setting. - "fontColor": "#cad3f5", // Font color setting. - "fontWeight": "600", // Font weight setting. - "lineHeight": 1.35, // Line height setting. - "letterSpacing": "-0.01em", // Letter spacing setting. - "wordSpacing": 0, // Word spacing setting. - "fontKerning": "normal", // Font kerning setting. - "textRendering": "geometricPrecision", // Text rendering setting. - "textShadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)", // Text shadow setting. - "fontStyle": "normal", // Font style setting. - "backgroundColor": "transparent", // Background color setting. - "backdropFilter": "blur(6px)", // Backdrop filter setting. - "nPlusOneColor": "#c6a0f6", // N plus one color setting. - "knownWordColor": "#a6da95", // Known word color setting. + "nPlusOneColor": "#c6a0f6", // Color used for the single N+1 target token subtitle highlight. + "knownWordColor": "#a6da95", // Color used for known-word subtitle highlights. "jlptColors": { "N1": "#ed8796", // N1 setting. "N2": "#f5a97f", // N2 setting. @@ -406,19 +410,21 @@ ] // Five colors used for rank bands when mode is `banded` (from most common to least within topX). }, // Frequency dictionary setting. "secondary": { - "fontFamily": "Inter, Noto Sans, Helvetica Neue, sans-serif", // Font family setting. - "fontSize": 24, // Font size setting. - "fontColor": "#cad3f5", // Font color setting. - "lineHeight": 1.35, // Line height setting. - "letterSpacing": "-0.01em", // Letter spacing setting. - "wordSpacing": 0, // Word spacing setting. - "fontKerning": "normal", // Font kerning setting. - "textRendering": "geometricPrecision", // Text rendering setting. - "textShadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)", // Text shadow setting. - "backgroundColor": "transparent", // Background color setting. - "backdropFilter": "blur(6px)", // Backdrop filter setting. - "fontWeight": "600", // Font weight setting. - "fontStyle": "normal" // Font style setting. + "css": { + "font-family": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", // Font family setting. + "color": "#cad3f5", // Color setting. + "background-color": "transparent", // Background color setting. + "font-size": "24px", // Font size setting. + "font-weight": "600", // Font weight setting. + "font-style": "normal", // Font style setting. + "line-height": "1.35", // Line height setting. + "letter-spacing": "-0.01em", // Letter spacing setting. + "word-spacing": "0", // Word spacing setting. + "font-kerning": "normal", // Font kerning setting. + "text-rendering": "geometricPrecision", // Text rendering setting. + "text-shadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)", // Text shadow setting. + "backdrop-filter": "blur(6px)" // Backdrop filter setting. + } // CSS declaration object applied to secondary subtitles after normal subtitle style defaults. } // Secondary setting. }, // Primary and secondary subtitle styling. @@ -432,18 +438,20 @@ "autoOpen": false, // Automatically open the subtitle sidebar once during overlay startup. Values: true | false "layout": "overlay", // Render the subtitle sidebar as a floating overlay or reserve space inside mpv. Values: overlay | embedded "toggleKey": "Backslash", // KeyboardEvent.code used to toggle the subtitle sidebar open and closed. - "pauseVideoOnHover": false, // Pause mpv while hovering the subtitle sidebar, then resume on leave. Values: true | false + "pauseVideoOnHover": true, // Pause mpv while hovering the subtitle sidebar, then resume on leave. Values: true | false "autoScroll": true, // Auto-scroll the active subtitle cue into view while playback advances. Values: true | false - "maxWidth": 420, // Maximum sidebar width in CSS pixels. - "opacity": 0.95, // Base opacity applied to the sidebar shell. - "backgroundColor": "rgba(73, 77, 100, 0.9)", // Background color for the subtitle sidebar shell. - "textColor": "#cad3f5", // Default cue text color in the subtitle sidebar. - "fontFamily": "\"M PLUS 1\", \"Noto Sans CJK JP\", sans-serif", // Font family used for subtitle sidebar cue text. - "fontSize": 16, // Base font size for subtitle sidebar cue text in CSS pixels. - "timestampColor": "#a5adcb", // Timestamp color in the subtitle sidebar. - "activeLineColor": "#f5bde6", // Text color for the active subtitle cue. - "activeLineBackgroundColor": "rgba(138, 173, 244, 0.22)", // Background color for the active subtitle cue. - "hoverLineBackgroundColor": "rgba(54, 58, 79, 0.84)" // Background color for hovered subtitle cues. + "css": { + "font-family": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", // Font family setting. + "color": "#cad3f5", // Color setting. + "background-color": "rgba(73, 77, 100, 0.9)", // Background color setting. + "font-size": "16px", // Font size setting. + "opacity": "0.95", // Opacity setting. + "--subtitle-sidebar-max-width": "420px", // Subtitle sidebar max width setting. + "--subtitle-sidebar-timestamp-color": "#a5adcb", // Subtitle sidebar timestamp color setting. + "--subtitle-sidebar-active-line-color": "#f5bde6", // Subtitle sidebar active line color setting. + "--subtitle-sidebar-active-background-color": "rgba(138, 173, 244, 0.22)", // Subtitle sidebar active background color setting. + "--subtitle-sidebar-hover-background-color": "rgba(54, 58, 79, 0.84)" // Subtitle sidebar hover background color setting. + } // CSS declaration object applied to the subtitle sidebar. Includes color, background-color, and all font properties. }, // Parsed-subtitle sidebar cue list styling, behavior, and toggle key. // ========================================== @@ -463,7 +471,7 @@ // ========================================== // AnkiConnect Integration // Automatic Anki updates and media generation options. - // Hot-reload: ankiConnect.ai.enabled updates live while SubMiner is running. + // Hot-reload: ankiConnect.ai.enabled, knownWords, nPlusOne, fields.word/audio/image/sentence/miscInfo, behavior.autoUpdateNewCards, isLapis.sentenceCardModel, and isKiku.fieldGrouping update live while SubMiner is running. // Shared AI provider transport settings are read from top-level ai and typically require restart. // Most other AnkiConnect settings still require restart. // ========================================== @@ -512,8 +520,7 @@ "refreshMinutes": 1440, // Minutes between known-word cache refreshes. "addMinedWordsImmediately": true, // Immediately append newly mined card words into the known-word cache. Values: true | false "matchMode": "headword", // Known-word matching strategy for subtitle annotations. Values: headword | surface - "decks": {}, // Decks and fields for known-word cache. Object mapping deck names to arrays of field names to extract, e.g. { "Kaishi 1.5k": ["Word", "Word Reading"] }. - "color": "#a6da95" // Color used for known-word highlights. + "decks": {} // Decks and fields for known-word cache. Object mapping deck names to arrays of field names to extract, e.g. { "Kaishi 1.5k": ["Word", "Word Reading"] }. }, // Known words setting. "behavior": { "overwriteAudio": true, // When updating an existing card, overwrite the audio field instead of skipping it. Values: true | false @@ -524,15 +531,15 @@ "autoUpdateNewCards": true // Automatically update newly added cards. Values: true | false }, // Behavior setting. "nPlusOne": { - "minSentenceWords": 3, // Minimum sentence word count required for N+1 targeting (default: 3). - "nPlusOne": "#c6a0f6" // Color used for the single N+1 target token highlight. + "enabled": false, // Enable N+1 subtitle highlighting (highlights the one unknown word in a sentence). Requires known-word cache data. Values: true | false + "minSentenceWords": 3 // Minimum sentence word count required for N+1 targeting (default: 3). }, // N plus one setting. "metadata": { "pattern": "[SubMiner] %f (%t)" // Template used to render the miscInfo field. Placeholders include %f (filename) and %t (timestamp). }, // Metadata setting. "isLapis": { "enabled": false, // Enable Lapis-specific mining behaviors and sentence card model targeting. Values: true | false - "sentenceCardModel": "Japanese sentences" // Note type name used by Lapis sentence cards. + "sentenceCardModel": "Lapis" // Note type name used by Lapis sentence cards. }, // Is lapis setting. "isKiku": { "enabled": false, // Enable Kiku-specific mining behaviors (duplicate handling, field grouping). Values: true | false @@ -544,6 +551,7 @@ // ========================================== // Jimaku // Jimaku API configuration and defaults. + // Hot-reload: Jimaku changes apply to the next Jimaku request. // ========================================== "jimaku": { "apiBaseUrl": "https://jimaku.cc", // Base URL of the Jimaku subtitle search API. @@ -554,6 +562,7 @@ // ========================================== // YouTube Playback Settings // Defaults for managed subtitle language preferences and YouTube subtitle loading. + // Hot-reload: primarySubLanguages applies to the next YouTube subtitle load. // ========================================== "youtube": { "primarySubLanguages": [ @@ -598,14 +607,23 @@ // ========================================== // MPV Launcher - // Optional mpv.exe override for Windows playback entry points. + // SubMiner-managed mpv launch and bundled plugin options. + // Set mpv.socketPath to the IPC socket used by the launcher, Electron app, and bundled plugin. + // autoStartSubMiner starts SubMiner in the background; auto_start_overlay only controls visible overlay display. // Set mpv.launchMode to choose normal, maximized, or fullscreen SubMiner-managed mpv playback. // Leave mpv.executablePath blank to auto-discover mpv.exe from SUBMINER_MPV_PATH or PATH. // ========================================== "mpv": { "executablePath": "", // Optional absolute path to mpv.exe for Windows launch flows. Leave empty to auto-discover from SUBMINER_MPV_PATH or PATH. - "launchMode": "normal" // Default window state for SubMiner-managed mpv launches. Values: normal | maximized | fullscreen - }, // Optional mpv.exe override for Windows playback entry points. + "launchMode": "normal", // Default window state for SubMiner-managed mpv launches. Values: normal | maximized | fullscreen + "socketPath": "/tmp/subminer-socket", // mpv IPC socket path used by SubMiner-managed playback and the bundled mpv plugin. + "backend": "auto", // Window tracking backend passed to the bundled mpv plugin. Auto detects the current platform. Values: auto | hyprland | sway | x11 | macos | windows + "autoStartSubMiner": true, // Start SubMiner in the background when SubMiner-managed mpv loads a file. Values: true | false + "pauseUntilOverlayReady": true, // Pause mpv on visible-overlay auto-start until SubMiner signals subtitle tokenization readiness. Values: true | false + "subminerBinaryPath": "", // Optional SubMiner app binary path passed to the bundled mpv plugin. Leave empty to use the launcher-detected app path. + "aniskipEnabled": true, // Enable AniSkip intro detection and skip markers in the bundled mpv plugin. Values: true | false + "aniskipButtonKey": "TAB" // mpv key used to trigger the AniSkip button while the skip marker is visible. + }, // SubMiner-managed mpv launch and bundled plugin options. // ========================================== // Jellyfin @@ -648,7 +666,7 @@ // ========================================== "discordPresence": { "enabled": true, // Enable optional Discord Rich Presence updates. Values: true | false - "presenceStyle": "default", // Presence card text preset: "default" (clean bilingual), "meme" (Mining and crafting), "japanese" (fully JP), or "minimal". + "presenceStyle": "default", // Presence card text preset: "default" (clean bilingual), "meme" (Mining and crafting), "japanese" (fully JP), or "minimal". Values: default | meme | japanese | minimal "updateIntervalMs": 3000, // Minimum interval between presence payload updates. "debounceMs": 750 // Debounce delay used to collapse bursty presence updates. }, // Optional Discord Rich Presence activity card updates for current playback/study session. diff --git a/docs-site/character-dictionary.md b/docs-site/character-dictionary.md index 82e1857e..9de139a2 100644 --- a/docs-site/character-dictionary.md +++ b/docs-site/character-dictionary.md @@ -88,7 +88,7 @@ Name matching runs inside Yomitan's scanning pipeline during subtitle tokenizati 1. Yomitan receives subtitle text and scans for dictionary matches. 2. Entries from "SubMiner Character Dictionary" are checked with exact primary-source matching — the token must match the entry's `originalText` with `isPrimary: true` and `matchType: 'exact'`. 3. Matched tokens are flagged `isNameMatch: true` and forwarded to the renderer. -4. The renderer applies the name-match highlight color (default: `#f5bde6`). +4. If `subtitleStyle.nameMatchEnabled` is enabled, the renderer applies the name-match highlight color (default: `#f5bde6`). Name matches are visually distinct from [N+1 targeting, frequency highlighting, and JLPT tags](/subtitle-annotations) so you can tell at a glance whether a highlighted word is a character name or a vocabulary target. @@ -96,7 +96,7 @@ Name matches are visually distinct from [N+1 targeting, frequency highlighting, | Option | Default | Description | | -------------------------------- | --------- | ---------------------------------- | -| `subtitleStyle.nameMatchEnabled` | `true` | Toggle character-name highlighting | +| `subtitleStyle.nameMatchEnabled` | `false` | Toggle character-name highlighting | | `subtitleStyle.nameMatchColor` | `#f5bde6` | Highlight color for matched names | ## Dictionary Entries @@ -228,7 +228,7 @@ merged.zip | `anilist.characterDictionary.collapsibleSections.description` | `false` | Start Description section expanded | | `anilist.characterDictionary.collapsibleSections.characterInformation` | `false` | Start Character Information section expanded | | `anilist.characterDictionary.collapsibleSections.voicedBy` | `false` | Start Voiced By section expanded | -| `subtitleStyle.nameMatchEnabled` | `true` | Toggle character-name highlighting in subtitles | +| `subtitleStyle.nameMatchEnabled` | `false` | Toggle character-name highlighting in subtitles | | `subtitleStyle.nameMatchColor` | `#f5bde6` | Highlight color for character-name matches | ## Reference Implementation diff --git a/docs-site/configuration.md b/docs-site/configuration.md index 7c675413..aba9ea72 100644 --- a/docs-site/configuration.md +++ b/docs-site/configuration.md @@ -8,10 +8,6 @@ outline: [2, 3] import { withBase } from 'vitepress'; -Settings are stored in `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc` when `XDG_CONFIG_HOME` is unset). -On Windows, the default path is `%APPDATA%\SubMiner\config.jsonc`. -When both files exist, SubMiner prefers `config.jsonc` over `config.json`. - ## Quick Start For most users, start with this minimal configuration: @@ -39,9 +35,38 @@ For most users, start with this minimal configuration: Then customize as needed using the sections below. +## Settings + +SubMiner includes a dedicated **Settings** window accessible from the tray menu, the app `--settings` flag, or launcher commands such as `subminer --settings` and `subminer settings`. It is the primary way to configure SubMiner — all changes are written directly to `config.jsonc`, so manual file editing is not required for most users. + +The Settings window groups options by workflow instead of mirroring the raw config-file shape: + +- Appearance +- Behavior +- Mining & Anki +- Playback & Sources +- Input +- Integrations +- Tracking & App +- Advanced + +Each field still writes to its current `config.jsonc` path. For example, subtitle hover pause appears under **Behavior** / playback behavior, but saves to `subtitleStyle.autoPauseVideoOnHover`. Anki-aware fields can query AnkiConnect for deck names, note types, and field names, and keybinding fields use click-to-learn controls instead of raw text boxes. + +The Settings window preserves existing JSONC comments, trailing commas, unrelated keys, and unsupported legacy options. Resetting a field removes the explicit config path so the built-in default applies. + +Secret fields do not display stored values. They show whether a value is configured; entering a new value writes it, and reset clears the explicit path. Prefer command-based secret options such as `ai.apiKeyCommand` when available. + +Saving validates the candidate config before writing. Live-reloadable changes are applied immediately; other changes return a restart-required banner in the window. + ## Configuration File -See [config.example.jsonc](/config.example.jsonc) for a comprehensive example configuration file with all available options, default values, and detailed comments. Only include the options you want to customize in your config file. +The Settings window writes to `config.jsonc` directly, so most users do not need to edit the file by hand. The config file and the option reference below are provided for advanced use, scripting, or cases where you prefer editing config directly. + +Settings are stored in `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc` when `XDG_CONFIG_HOME` is unset). +On Windows, the default path is `%APPDATA%\SubMiner\config.jsonc`. +When both files exist, SubMiner prefers `config.jsonc` over `config.json`. + +See [config.example.jsonc](/config.example.jsonc) for a comprehensive example with all available options, default values, and detailed comments. Only include the options you want to customize in your config file. Generate a fresh default config from the centralized config registry: @@ -63,28 +88,6 @@ For valid JSON/JSONC with invalid option values, SubMiner uses warn-and-fallback On macOS, these validation warnings also open a native dialog with full details (desktop notification banners can truncate long messages). -### Configuration Window - -SubMiner also includes a dedicated **Configuration** window from the tray menu, the app `--config` flag, or launcher commands such as `subminer --config` and `subminer config`. It groups settings by workflow instead of mirroring the raw config-file shape: - -- Viewing -- Mining & Anki -- Playback & Sources -- Input -- Integrations -- Tracking & App -- Advanced - -Each field still writes to its current `config.jsonc` path. For example, subtitle hover pause appears under **Viewing** / playback behavior, but saves to `subtitleStyle.autoPauseVideoOnHover`. - -The settings window preserves existing JSONC comments, trailing commas, unrelated keys, and unsupported legacy options. Resetting a field removes the explicit config path so the built-in default applies. - -Secret fields do not display stored values. They show whether a value is configured; entering a new value writes it, and reset clears the explicit path. Prefer command-based secret options such as `ai.apiKeyCommand` when available. - -Some compatibility-only or ignored legacy keys are intentionally hidden from the normal field list, including legacy top-level Anki migration fields, old N+1 aliases, the removed YouTube subtitle-generation primary-language key, `anilist.characterDictionary.refreshTtlHours`, `anilist.characterDictionary.evictionPolicy`, `jellyfin.accessToken`, `jellyfin.userId`, and normal editing for `controller.buttonIndices`. Advanced/raw JSON editing remains the escape hatch for unsupported or legacy keys. - -Saving validates the candidate config before writing. Live-reloadable changes are applied immediately; other changes return a restart-required banner in the window. - ### Hot-Reload Behavior SubMiner watches the active config file (`config.jsonc` or `config.json`) while running and applies supported updates automatically. @@ -96,7 +99,28 @@ Hot-reloadable fields: - `keybindings` - `shortcuts` - `secondarySub.defaultMode` -- `ankiConnect.ai` +- `stats.toggleKey` +- `stats.markWatchedKey` +- `logging.level` +- `youtube.primarySubLanguages` +- `jimaku.*` +- `subsync.*` +- `ankiConnect.ai.enabled` +- `ankiConnect.behavior.autoUpdateNewCards` +- `ankiConnect.knownWords.highlightEnabled` +- `ankiConnect.knownWords.refreshMinutes` +- `ankiConnect.knownWords.addMinedWordsImmediately` +- `ankiConnect.knownWords.matchMode` +- `ankiConnect.knownWords.decks` +- `ankiConnect.nPlusOne.enabled` +- `ankiConnect.nPlusOne.minSentenceWords` +- `ankiConnect.fields.word` +- `ankiConnect.fields.audio` +- `ankiConnect.fields.image` +- `ankiConnect.fields.sentence` +- `ankiConnect.fields.miscInfo` +- `ankiConnect.isLapis.sentenceCardModel` +- `ankiConnect.isKiku.fieldGrouping` When these values change, SubMiner applies them live. Invalid config edits are rejected and the previous valid runtime config remains active. @@ -104,6 +128,7 @@ Restart-required changes: - Any other config sections still require restart. - Shared top-level `ai` provider settings still require restart. +- AnkiConnect transport/proxy/media/deck/tag fields still require restart unless listed above. - SubMiner shows an on-screen/system notification listing restart-required sections when they change. ### Configuration Options Overview @@ -146,7 +171,7 @@ The configuration file includes several main sections: **External Integrations** - [**Jimaku**](#jimaku) - Jimaku API configuration and defaults -- [**Auto Subtitle Sync**](#auto-subtitle-sync) - Sync current subtitle with `alass`/`ffsubsync` +- [**Subtitle Sync**](#subtitle-sync) - Sync current subtitle with `alass`/`ffsubsync` - [**AniList**](#anilist) - Optional post-watch progress updates - [**Yomitan**](#yomitan) - Reuse an external read-only Yomitan profile via `yomitan.externalProfilePath` - [**Jellyfin**](#jellyfin) - Optional Jellyfin auth, library listing, and playback launch @@ -233,7 +258,7 @@ Control which startup warmups run in the background versus deferring to first re "mecab": true, "yomitanExtension": true, "subtitleDictionaries": true, - "jellyfinRemoteSession": true + "jellyfinRemoteSession": false } } ``` @@ -246,11 +271,11 @@ Control which startup warmups run in the background versus deferring to first re | `subtitleDictionaries` | `true`, `false` | Warm up JLPT + frequency dictionaries at startup | | `jellyfinRemoteSession` | `true`, `false` | Warm up Jellyfin remote session at startup (still requires Jellyfin remote auto-connect settings) | -Defaults warm everything (`true` for all toggles, `lowPowerMode: false`). Setting a warmup toggle to `false` defers that work until first usage. +Defaults warm local tokenizer/dictionary work (`true` for `mecab`, `yomitanExtension`, and `subtitleDictionaries`) with `lowPowerMode: false`; Jellyfin remote session warmup is opt-in (`false` by default). Setting a warmup toggle to `false` defers that work until first usage. ### WebSocket Server -The overlay includes a built-in WebSocket server that broadcasts subtitle text to connected clients (such as texthooker-ui) for external processing. +The overlay includes a built-in WebSocket server that broadcasts plain subtitle text to connected clients for external processing. For endpoint details, payload examples, and client patterns, see [WebSocket / Texthooker API & Integration](/websocket-texthooker-api). @@ -323,25 +348,29 @@ See `config.example.jsonc` for detailed configuration options. ```json { "subtitleStyle": { - "fontFamily": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", - "fontSize": 35, "fontColor": "#cad3f5", - "fontWeight": "600", - "lineHeight": 1.35, - "letterSpacing": "-0.01em", - "wordSpacing": 0, - "fontKerning": "normal", - "textRendering": "geometricPrecision", - "textShadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)", - "fontStyle": "normal", "backgroundColor": "transparent", - "backdropFilter": "blur(6px)", + "css": { + "font-family": "Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP", + "font-size": "35px", + "font-weight": "600", + "line-height": "1.35", + "letter-spacing": "-0.01em", + "word-spacing": "0", + "font-kerning": "normal", + "text-rendering": "geometricPrecision", + "text-shadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)", + "font-style": "normal", + "backdrop-filter": "blur(6px)" + }, "secondary": { - "fontFamily": "Inter, Noto Sans, Helvetica Neue, sans-serif", - "fontSize": 24, "fontColor": "#cad3f5", - "textShadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)", - "backgroundColor": "transparent" + "backgroundColor": "transparent", + "css": { + "font-family": "Inter, Noto Sans, Helvetica Neue, sans-serif", + "font-size": "24px", + "text-shadow": "0 2px 6px rgba(0,0,0,0.9), 0 0 12px rgba(0,0,0,0.55)" + } } } } @@ -352,6 +381,7 @@ See `config.example.jsonc` for detailed configuration options. | `fontFamily` | string | CSS font-family value (default: `"Hiragino Sans, M PLUS 1, Source Han Sans JP, Noto Sans CJK JP"`) | | `fontSize` | number (px) | Font size in pixels (default: `35`) | | `fontColor` | string | Any CSS color value (default: `"#cad3f5"`) | +| `css` | object | CSS declarations applied to subtitles after normal style defaults; the settings window writes textbox edits here | | `fontWeight` | string | CSS font-weight, e.g. `"bold"`, `"normal"`, `"600"` (default: `"600"`) | | `fontStyle` | string | `"normal"` or `"italic"` (default: `"normal"`) | | `backgroundColor` | string | Any CSS color, including `"transparent"` (default: `"transparent"`) | @@ -360,9 +390,11 @@ See `config.example.jsonc` for detailed configuration options. | `autoPauseVideoOnHover` | boolean | Pause playback while mouse hovers subtitle text, then resume on leave (`true` by default). | | `autoPauseVideoOnYomitanPopup` | boolean | Pause playback while the Yomitan popup is open, then resume when the popup closes (`true` by default). | | `hoverTokenColor` | string | Hex color used for hovered subtitle token highlight in mpv (default: catppuccin mauve) | -| `hoverTokenBackgroundColor` | string | CSS color used for hovered subtitle token background highlight; `hoverBackground` is accepted as an alias | -| `nameMatchEnabled` | boolean | Enable subtitle token coloring for matches from the SubMiner character dictionary (`true` by default) | +| `hoverTokenBackgroundColor` | string | CSS color used for hovered subtitle token background highlight (default: `"transparent"`); `hoverBackground` is accepted as an alias | +| `nameMatchEnabled` | boolean | Enable subtitle token coloring for matches from the SubMiner character dictionary (`false` by default) | | `nameMatchColor` | string | Hex color used for subtitle tokens matched from the SubMiner character dictionary (default: `#f5bde6`) | +| `knownWordColor` | string | Hex color used for known-word subtitle highlights (default: `#a6da95`) | +| `nPlusOneColor` | string | Hex color used for the single N+1 target subtitle highlight (default: `#c6a0f6`) | | `frequencyDictionary.enabled` | boolean | Enable frequency highlighting from dictionary lookups (`false` by default) | | `frequencyDictionary.sourcePath` | string | Path to a local frequency dictionary root. Leave empty or omit to use installed/default frequency-dictionary search paths. | | `frequencyDictionary.topX` | number | Only color tokens whose frequency rank is `<= topX` (`1000` by default) | @@ -370,10 +402,13 @@ See `config.example.jsonc` for detailed configuration options. | `frequencyDictionary.matchMode` | string | `"headword"` or `"surface"` (`"headword"` by default) | | `frequencyDictionary.singleColor` | string | Color used for all highlighted tokens in single mode | | `frequencyDictionary.bandedColors` | string[] | Array of five hex colors used for ranked bands in banded mode | -| `nPlusOneColor` | string | Existing n+1 highlight color (default: `#c6a0f6`) | -| `knownWordColor` | string | Existing known-word highlight color (default: `#a6da95`) | | `jlptColors` | object | JLPT level underline colors object (`N1`..`N5`) | -| `secondary` | object | Override any of the above for secondary subtitles (optional) | +| `secondary` | object | Override any of the above for secondary subtitles (optional), including `secondary.css` declarations | + +The Settings window keeps subtitle color controls separate, then saves CSS textboxes to +`subtitleStyle.css`, `subtitleStyle.secondary.css`, and `subtitleSidebar.css`. The generated example +uses that same CSS declaration shape; existing top-level style keys such as `fontSize` and +`textShadow` remain supported for hand-written or older configs. Frequency dictionary highlighting uses the same dictionary file format as JLPT bundle lookups (`term_meta_bank_*.json` under discovered dictionary directories). A token is highlighted when it has a positive integer `frequencyRank` (lower is more common) and the rank is within `topX`. @@ -408,7 +443,7 @@ Configure the parsed-subtitle sidebar modal. "autoOpen": false, "layout": "overlay", "toggleKey": "Backslash", - "pauseVideoOnHover": false, + "pauseVideoOnHover": true, "autoScroll": true, "fontFamily": "\"M PLUS 1\", \"Noto Sans CJK JP\", sans-serif", "fontSize": 16 @@ -422,7 +457,7 @@ Configure the parsed-subtitle sidebar modal. | `autoOpen` | boolean | Open sidebar automatically on overlay startup (`false` by default) | | `layout` | string | `"overlay"` floats over mpv; `"embedded"` reserves right-side player space to mimic browser-like layout | | `toggleKey` | string | `KeyboardEvent.code` used to open/close the sidebar (default: `"Backslash"`) | -| `pauseVideoOnHover` | boolean | Pause playback while hovering the sidebar cue list | +| `pauseVideoOnHover` | boolean | Pause playback while hovering the sidebar cue list (`true` by default) | | `autoScroll` | boolean | Keep the active cue in view while playback advances | | `maxWidth` | number | Maximum sidebar width in CSS pixels (default: `420`) | | `opacity` | number | Sidebar opacity between `0` and `1` (default: `0.95`) | @@ -963,11 +998,10 @@ This example is intentionally compact. The option table below documents availabl | `behavior.highlightWord` | `true`, `false` | Highlight the word in sentence context (default: `true`) | | `ankiConnect.knownWords.highlightEnabled` | `true`, `false` | Enable fast local highlighting for words already known in Anki (default: `false`) | | `ankiConnect.knownWords.addMinedWordsImmediately` | `true`, `false` | Add words from successful mines into the local known-word cache immediately (default: `true`) | -| `ankiConnect.knownWords.color` | hex color string | Text color for tokens already found in the local known-word cache (default: `"#a6da95"`). | | `ankiConnect.knownWords.matchMode` | `"headword"`, `"surface"` | Matching strategy for known-word highlighting (default: `"headword"`). `headword` uses token headwords; `surface` uses visible subtitle text. | | `ankiConnect.knownWords.refreshMinutes` | number | Minutes between known-word cache refreshes (default: `1440`) | | `ankiConnect.knownWords.decks` | object | Deck→fields mapping used for known-word cache query scope (e.g. `{ "Kaishi 1.5k": ["Word", "Word Reading"] }`). | -| `ankiConnect.nPlusOne.nPlusOne` | hex color string | Text color for the single target token to study when exactly one unknown candidate exists in a sentence (default: `"#c6a0f6"`). | +| `ankiConnect.nPlusOne.enabled` | `true`, `false` | Enable N+1 subtitle highlighting (highlights the one unknown word in a sentence). Independent from `knownWords.highlightEnabled`. Requires known-word cache data (default: `false`). | | `ankiConnect.nPlusOne.minSentenceWords` | number | Minimum number of words required in a sentence before single unknown-word N+1 highlighting can trigger (default: `3`). | | `behavior.notificationType` | `"osd"`, `"system"`, `"both"`, `"none"` | Notification type on card update (default: `"osd"`) | | `behavior.autoUpdateNewCards` | `true`, `false` | Automatically update cards on creation (default: `true`) | @@ -1009,9 +1043,9 @@ Known-word cache policy: - Initial sync runs when the integration starts if the cache is missing or stale. - `ankiConnect.knownWords.refreshMinutes` controls the minimum time between refreshes; between refreshes, cached words are reused without querying Anki. -- `ankiConnect.nPlusOne.nPlusOne` sets the color for the single target token when exactly one eligible unknown word exists. +- `subtitleStyle.nPlusOneColor` sets the color for the single target token when exactly one eligible unknown word exists. - `ankiConnect.nPlusOne.minSentenceWords` sets the minimum token count required in a sentence for N+1 highlighting (default: `3`). -- `ankiConnect.knownWords.color` sets the known-word highlight color for tokens already in Anki. +- `subtitleStyle.knownWordColor` sets the known-word highlight color for tokens already in Anki. - `ankiConnect.knownWords.decks` accepts an object keyed by deck name. If omitted or empty, it falls back to the legacy `ankiConnect.deck` single-deck scope. - Cache state is persisted to `known-words-cache.json` under the app `userData` directory. - The cache is automatically invalidated when the configured scope changes (for example, when deck changes). @@ -1077,17 +1111,16 @@ Jimaku is rate limited; if you hit a limit, SubMiner will surface the retry dela Set `openBrowser` to `false` to only print the URL without opening a browser. -### Auto Subtitle Sync +### Subtitle Sync -Sync the active subtitle track using `alass` (preferred) or `ffsubsync`. Both are **optional external tools** that must be installed separately and available on your `PATH` (or configured via the path options below). Subtitle syncing is silently skipped if neither is found. +Sync the active subtitle track from the overlay picker using `alass` or `ffsubsync`. Both are **optional external tools** that must be installed separately and available on your `PATH` (or configured via the path options below). - [`alass`](https://github.com/kaegi/alass) — fast, audio-independent sync using a secondary subtitle as reference -- [`ffsubsync`](https://github.com/smacke/ffsubsync) — audio-based sync using the video file as reference (fallback) +- [`ffsubsync`](https://github.com/smacke/ffsubsync) — audio-based sync using the video file as reference ```json { "subsync": { - "defaultMode": "auto", "alass_path": "", "ffsubsync_path": "", "ffmpeg_path": "", @@ -1098,7 +1131,6 @@ Sync the active subtitle track using `alass` (preferred) or `ffsubsync`. Both ar | Option | Values | Description | | ---------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------- | -| `defaultMode` | `"auto"`, `"manual"` | `auto`: try `alass` against secondary subtitle, then fallback to `ffsubsync`; `manual`: open overlay picker | | `alass_path` | string path | Path to `alass` executable. Empty or `null` resolves from `PATH`. `alass` must be installed separately. | | `ffsubsync_path` | string path | Path to `ffsubsync` executable. Empty or `null` resolves from `PATH`. `ffsubsync` must be installed separately. | | `ffmpeg_path` | string path | Path to `ffmpeg` (used for internal subtitle extraction). Empty or `null` falls back to `/usr/bin/ffmpeg`. | @@ -1255,7 +1287,7 @@ Jellyfin integration is optional and disabled by default. When enabled, SubMiner | `directPlayContainers` | string[] | Container allowlist for direct play decisions | | `transcodeVideoCodec` | string | Preferred transcode video codec fallback (default: `h264`) | -Jellyfin auth session (`accessToken` + `userId`) is stored in local encrypted storage after login/setup. The legacy `jellyfin.accessToken` and `jellyfin.userId` config keys are not resolver-backed settings in the current runtime and are hidden from the configuration window. +Jellyfin auth session (`accessToken` + `userId`) is stored in local encrypted storage after login/setup. The legacy `jellyfin.accessToken` and `jellyfin.userId` config keys are not resolver-backed settings in the current runtime. The Settings window also hides low-level client identity and default library fields (`deviceId`, `clientName`, `clientVersion`, and `defaultLibraryId`) so normal setup stays focused on server, auth, playback, and remote-control behavior. - On Linux, token storage defaults to `gnome-libsecret` for `safeStorage`. Override with `--password-store=` on launcher/app invocations when needed. diff --git a/docs-site/demos.md b/docs-site/demos.md index a9f5a1f6..ae034e5c 100644 --- a/docs-site/demos.md +++ b/docs-site/demos.md @@ -25,7 +25,7 @@ Mine vocabulary cards from Yomitan or directly from subtitle lines. SubMiner aut ## Subtitle Download & Sync -Search and download subtitles from Jimaku, then automatically synchronize them with alass or ffsubsync — all from within SubMiner. +Search and download subtitles from Jimaku, then retime them with alass or ffsubsync — all from within SubMiner.