mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-29 18:12:06 -07:00
* chore(backlog): add mining workflow milestone and tasks
* refactor: split character dictionary runtime modules
* refactor: split shared type entrypoints
* refactor: use bun serve for stats server
* feat: add repo-local subminer workflow plugin
* fix: add stats server node fallback
* refactor: split immersion tracker query modules
* chore: update backlog task records
* refactor: migrate shared type imports
* refactor: compose startup and setup window wiring
* Add backlog tasks and launcher time helper tests
- Track follow-up cleanup work in Backlog.md
- Replace Date.now usage with shared nowMs helper
- Add launcher args/parser and core regression tests
* test: increase launcher test timeout for CI stability
* fix: address CodeRabbit review feedback
* refactor(main): extract remaining inline runtime logic from main
* chore(backlog): update task notes and changelog fragment
* refactor: split main boot phases
* test: stabilize bun coverage reporting
* Switch plausible endpoint and harden coverage lane parsing
- update docs-site tracking to use the Plausible capture endpoint
- tighten coverage lane argument and LCOV parsing checks
- make script entrypoint use CommonJS main guard
* Restrict docs analytics and build coverage input
- limit Plausible init to docs.subminer.moe
- build Yomitan before src coverage lane
* fix(ci): normalize Windows shortcut paths for cross-platform tests
* Fix verification and immersion-tracker grouping
- isolate verifier artifacts and lease handling
- switch weekly/monthly tracker cutoffs to calendar boundaries
- tighten boot lifecycle and zip writer tests
* fix: resolve CI type failures in boot and immersion query tests
* fix: remove strict spread usage in Date mocks
* fix: use explicit super args for MockDate constructors
* Factor out mock date helper in tracker tests
- reuse a shared `withMockDate` helper for date-sensitive query tests
- make monthly rollup assertions key off `videoId` instead of row order
* fix: use variadic array type for MockDate constructor args
TS2367: fixed-length tuple made args.length === 0 unreachable.
* refactor: remove unused createMainBootRuntimes/Handlers aggregate functions
These functions were never called by production code — main.ts imports
the individual composeBoot* re-exports directly.
* refactor: remove boot re-export alias layer
main.ts now imports directly from the runtime/composers and runtime/domains
modules, eliminating the intermediate boot/ indirection.
* refactor: consolidate 3 near-identical setup window factories
Extract shared createSetupWindowHandler with a config parameter.
Public API unchanged.
* refactor: parameterize duplicated getAffected*Ids query helpers
Four structurally identical functions collapsed into two parameterized
helpers while preserving the existing public API.
* refactor: inline identity composers (stats-startup, overlay-window)
composeStatsStartupRuntime was a no-op that returned its input.
composeOverlayWindowHandlers was a 1-line delegation.
Both removed in favor of direct usage.
* chore: remove unused token/queue file path constants from main.ts
* fix: replace any types in boot services with proper signatures
* refactor: deduplicate ensureDir into shared/fs-utils
5 copies of mkdir-p-if-not-exists consolidated into one shared module
with ensureDir (directory path) and ensureDirForFile (file path) variants.
* fix: tighten type safety in boot services
- Add AppLifecycleShape and OverlayModalInputStateShape constraints
so TAppLifecycleApp and TOverlayModalInputState generics are bounded
- Remove unsafe `as { handleModalInputStateChange? }` cast — now
directly callable via the constraint
- Use `satisfies AppLifecycleShape` for structural validation on the
appLifecycleApp object literal
- Document Electron App.on incompatibility with simple signatures
* refactor: inline subtitle-prefetch-runtime-composer
The composer was a pure pass-through that destructured an object and
reassembled it with the same fields. Inlined at the call site.
* chore: consolidate duplicate import paths in main.ts
* test: extract mpv composer test fixture factory to reduce duplication
* test: add behavioral assertions to composer tests
Upgrade 8 composer test files from shape-only typeof checks to behavioral
assertions that invoke returned handlers and verify injected dependencies are
actually called, following the mpv-runtime-composer pattern.
* refactor: normalize import extensions in query modules
* refactor: consolidate toDbMs into query-shared.ts
* refactor: remove Node.js fallback from stats-server, use Bun only
* Fix monthly rollup test expectations
- Preserve multi-arg Date construction in mock helper
- Align rollup assertions with the correct videoId
* fix: address PR 36 CodeRabbit follow-ups
* fix: harden coverage lane cleanup
* fix(stats): fallback to node server when Bun.serve unavailable
* fix(ci): restore coverage lane compatibility
* chore(backlog): close TASK-242
* fix: address latest CodeRabbit review round
* fix: guard disabled immersion retention windows
* fix: migrate discord rpc wrapper
* fix(ci): add changelog fragment for PR 36
* fix: stabilize macOS visible overlay toggle
* fix: pin installed mpv plugin to current binary
* fix: strip inline subtitle markup from sidebar cues
* fix(renderer): restore subtitle sidebar mpv passthrough
* feat(discord): add configurable presence style presets
Replace the hardcoded "Mining and crafting (Anki cards)" meme message
with a preset system. New `discordPresence.presenceStyle` option
supports four presets: "default" (clean bilingual), "meme" (the OG
Minecraft joke), "japanese" (fully JP), and "minimal". The default
preset shows "Sentence Mining" with 日本語学習中 as the small image
tooltip. Existing users can set presenceStyle to "meme" to keep the
old behavior.
* fix: finalize v0.10.0 release prep
* docs: add subtitle sidebar guide and release note
* chore(backlog): mark docs task done
* fix: lazily resolve youtube playback socket path
* chore(release): build v0.10.0 changelog
* Revert "chore(release): build v0.10.0 changelog"
This reverts commit 9741c0f020.
550 lines
36 KiB
JSON
550 lines
36 KiB
JSON
/**
|
|
* SubMiner Example Configuration File
|
|
*
|
|
* This file is auto-generated from src/config/definitions.ts.
|
|
* Copy to %APPDATA%/SubMiner/config.jsonc on Windows, or $XDG_CONFIG_HOME/SubMiner/config.jsonc (or ~/.config/SubMiner/config.jsonc) on Linux/macOS.
|
|
*/
|
|
{
|
|
|
|
// ==========================================
|
|
// Overlay Auto-Start
|
|
// When overlay connects to mpv, automatically show overlay and hide mpv subtitles.
|
|
// ==========================================
|
|
"auto_start_overlay": false, // When overlay connects to mpv, automatically show overlay and hide mpv subtitles. Values: true | false
|
|
|
|
// ==========================================
|
|
// Texthooker Server
|
|
// Configure texthooker startup launch and browser opening behavior.
|
|
// ==========================================
|
|
"texthooker": {
|
|
"launchAtStartup": true, // Launch texthooker server automatically when SubMiner starts. Values: true | false
|
|
"openBrowser": true // Open browser setting. Values: true | false
|
|
}, // Configure texthooker startup launch and browser opening behavior.
|
|
|
|
// ==========================================
|
|
// WebSocket Server
|
|
// Built-in WebSocket server broadcasts subtitle text to connected clients.
|
|
// Auto mode disables built-in server if mpv_websocket is detected.
|
|
// ==========================================
|
|
"websocket": {
|
|
"enabled": "auto", // Built-in subtitle websocket server mode. Values: auto | true | false
|
|
"port": 6677 // Built-in subtitle websocket server port.
|
|
}, // Built-in WebSocket server broadcasts subtitle text to connected clients.
|
|
|
|
// ==========================================
|
|
// Annotation WebSocket
|
|
// Dedicated annotated subtitle websocket for bundled texthooker and token-aware clients.
|
|
// Independent from websocket.auto and defaults to port 6678.
|
|
// ==========================================
|
|
"annotationWebsocket": {
|
|
"enabled": true, // Annotated subtitle websocket server enabled state. Values: true | false
|
|
"port": 6678 // Annotated subtitle websocket server port.
|
|
}, // Dedicated annotated subtitle websocket for bundled texthooker and token-aware clients.
|
|
|
|
// ==========================================
|
|
// Logging
|
|
// Controls logging verbosity.
|
|
// Set to debug for full runtime diagnostics.
|
|
// ==========================================
|
|
"logging": {
|
|
"level": "info" // Minimum log level for runtime logging. Values: debug | info | warn | error
|
|
}, // Controls logging verbosity.
|
|
|
|
// ==========================================
|
|
// Controller Support
|
|
// Gamepad support for the visible overlay while keyboard-only mode is active.
|
|
// Use Alt+C to pick a preferred controller and remap actions inline with learn mode.
|
|
// Trigger input mode can be auto, digital-only, or analog-thresholded depending on the controller.
|
|
// Override controller.buttonIndices when your pad reports non-standard raw button numbers.
|
|
// ==========================================
|
|
"controller": {
|
|
"enabled": true, // Enable overlay controller support through the Chrome Gamepad API. Values: true | false
|
|
"preferredGamepadId": "", // Preferred controller id saved from the controller config modal.
|
|
"preferredGamepadLabel": "", // Preferred controller display label saved for diagnostics.
|
|
"smoothScroll": true, // Use smooth scrolling for controller-driven popup scroll input. Values: true | false
|
|
"scrollPixelsPerSecond": 900, // Base popup scroll speed for controller stick input.
|
|
"horizontalJumpPixels": 160, // Popup page-jump distance for controller jump input.
|
|
"stickDeadzone": 0.2, // Deadzone applied to controller stick axes.
|
|
"triggerInputMode": "auto", // How controller triggers are interpreted: auto, pressed-only, or thresholded analog. Values: auto | digital | analog
|
|
"triggerDeadzone": 0.5, // Minimum analog trigger value required when trigger input uses auto or analog mode.
|
|
"repeatDelayMs": 320, // Delay before repeating held controller actions.
|
|
"repeatIntervalMs": 120, // Repeat interval for held controller actions.
|
|
"buttonIndices": {
|
|
"select": 6, // Raw button index used for the controller select/minus/back button.
|
|
"buttonSouth": 0, // Raw button index used for controller south/A button input.
|
|
"buttonEast": 1, // Raw button index used for controller east/B button input.
|
|
"buttonWest": 2, // Raw button index used for controller west/X button input.
|
|
"buttonNorth": 3, // Raw button index used for controller north/Y button input.
|
|
"leftShoulder": 4, // Raw button index used for controller left shoulder input.
|
|
"rightShoulder": 5, // Raw button index used for controller right shoulder input.
|
|
"leftStickPress": 9, // Raw button index used for controller L3 input.
|
|
"rightStickPress": 10, // Raw button index used for controller R3 input.
|
|
"leftTrigger": 6, // Raw button index used for controller L2 input.
|
|
"rightTrigger": 7 // Raw button index used for controller R2 input.
|
|
}, // Semantic button-name reference mapping used for legacy configs and debug output. Updating it does not rewrite existing raw binding descriptors.
|
|
"bindings": {
|
|
"toggleLookup": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 0 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for toggling lookup. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"closeLookup": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 1 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for closing lookup. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"toggleKeyboardOnlyMode": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 3 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for toggling keyboard-only mode. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"mineCard": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 2 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for mining the active card. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"quitMpv": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 6 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for quitting mpv. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"previousAudio": {
|
|
"kind": "none" // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
}, // Controller binding descriptor for previous Yomitan audio. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"nextAudio": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 5 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for next Yomitan audio. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"playCurrentAudio": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 4 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for playing the current Yomitan audio. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"toggleMpvPause": {
|
|
"kind": "button", // Discrete binding input source kind. When kind is "axis", set both axisIndex and direction. Values: none | button | axis
|
|
"buttonIndex": 9 // Raw button index captured for this discrete controller action.
|
|
}, // Controller binding descriptor for toggling mpv play/pause. Use Alt+C learn mode or set a raw button/axis descriptor manually. If kind is "axis", direction is required.
|
|
"leftStickHorizontal": {
|
|
"kind": "axis", // Analog binding input source kind. Values: none | axis
|
|
"axisIndex": 0, // Raw axis index captured for this analog controller action.
|
|
"dpadFallback": "horizontal" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
|
|
}, // Axis binding descriptor used for left/right token selection. Use Alt+C learn mode or set a raw axis descriptor manually.
|
|
"leftStickVertical": {
|
|
"kind": "axis", // Analog binding input source kind. Values: none | axis
|
|
"axisIndex": 1, // Raw axis index captured for this analog controller action.
|
|
"dpadFallback": "vertical" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
|
|
}, // Axis binding descriptor used for primary popup scrolling. Use Alt+C learn mode or set a raw axis descriptor manually.
|
|
"rightStickHorizontal": {
|
|
"kind": "axis", // Analog binding input source kind. Values: none | axis
|
|
"axisIndex": 3, // Raw axis index captured for this analog controller action.
|
|
"dpadFallback": "none" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
|
|
}, // Axis binding descriptor reserved for alternate right-stick mappings. Use Alt+C learn mode or set a raw axis descriptor manually.
|
|
"rightStickVertical": {
|
|
"kind": "axis", // Analog binding input source kind. Values: none | axis
|
|
"axisIndex": 4, // Raw axis index captured for this analog controller action.
|
|
"dpadFallback": "none" // Optional D-pad fallback used when this analog controller action should also read D-pad input. Values: none | horizontal | vertical
|
|
} // Axis binding descriptor used for popup page jumps. Use Alt+C learn mode or set a raw axis descriptor manually.
|
|
} // Raw controller binding descriptors saved by Alt+C learn mode. For discrete axis bindings, kind "axis" requires axisIndex and direction.
|
|
}, // Gamepad support for the visible overlay while keyboard-only mode is active.
|
|
|
|
// ==========================================
|
|
// Startup Warmups
|
|
// Background warmup controls for MeCab, Yomitan, dictionaries, and Jellyfin session.
|
|
// Disable individual warmups to defer load until first real usage.
|
|
// lowPowerMode defers all warmups except Yomitan extension.
|
|
// ==========================================
|
|
"startupWarmups": {
|
|
"lowPowerMode": false, // Defer startup warmups except Yomitan extension. Values: true | false
|
|
"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
|
|
}, // Background warmup controls for MeCab, Yomitan, dictionaries, and Jellyfin session.
|
|
|
|
// ==========================================
|
|
// Keyboard Shortcuts
|
|
// Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
|
// Hot-reload: shortcut changes apply live and update the session help modal on reopen.
|
|
// ==========================================
|
|
"shortcuts": {
|
|
"toggleVisibleOverlayGlobal": "Alt+Shift+O", // Toggle visible overlay global setting.
|
|
"copySubtitle": "CommandOrControl+C", // Copy subtitle setting.
|
|
"copySubtitleMultiple": "CommandOrControl+Shift+C", // Copy subtitle multiple setting.
|
|
"updateLastCardFromClipboard": "CommandOrControl+V", // Update last card from clipboard setting.
|
|
"triggerFieldGrouping": "CommandOrControl+G", // Trigger field grouping setting.
|
|
"triggerSubsync": "Ctrl+Alt+S", // Trigger subsync setting.
|
|
"mineSentence": "CommandOrControl+S", // Mine sentence setting.
|
|
"mineSentenceMultiple": "CommandOrControl+Shift+S", // Mine sentence multiple setting.
|
|
"multiCopyTimeoutMs": 3000, // Timeout for multi-copy/mine modes.
|
|
"toggleSecondarySub": "CommandOrControl+Shift+V", // Toggle secondary sub setting.
|
|
"markAudioCard": "CommandOrControl+Shift+A", // Mark audio card setting.
|
|
"openRuntimeOptions": "CommandOrControl+Shift+O", // Open runtime options setting.
|
|
"openJimaku": "Ctrl+Shift+J" // Open jimaku setting.
|
|
}, // Overlay keyboard shortcuts. Set a shortcut to null to disable.
|
|
|
|
// ==========================================
|
|
// Keybindings (MPV Commands)
|
|
// Extra keybindings that are merged with built-in defaults.
|
|
// Set command to null to disable a default keybinding.
|
|
// Hot-reload: keybinding changes apply live and update the session help modal on reopen.
|
|
// ==========================================
|
|
"keybindings": [], // Extra keybindings that are merged with built-in defaults.
|
|
|
|
// ==========================================
|
|
// Secondary Subtitles
|
|
// Dual subtitle track options.
|
|
// Used by the YouTube subtitle loading flow as secondary language preferences.
|
|
// Hot-reload: defaultMode updates live while SubMiner is running.
|
|
// ==========================================
|
|
"secondarySub": {
|
|
"secondarySubLanguages": [], // Secondary sub languages setting.
|
|
"autoLoadSecondarySub": false, // Auto load secondary sub setting. Values: true | false
|
|
"defaultMode": "hover" // Default mode setting.
|
|
}, // Dual subtitle track options.
|
|
|
|
// ==========================================
|
|
// Auto Subtitle Sync
|
|
// Subsync engine and executable paths.
|
|
// ==========================================
|
|
"subsync": {
|
|
"defaultMode": "auto", // Subsync default mode. Values: auto | manual
|
|
"alass_path": "", // Alass path setting.
|
|
"ffsubsync_path": "", // Ffsubsync path setting.
|
|
"ffmpeg_path": "", // Ffmpeg path setting.
|
|
"replace": true // Replace the active subtitle file when sync completes. Values: true | false
|
|
}, // Subsync engine and executable paths.
|
|
|
|
// ==========================================
|
|
// Subtitle Position
|
|
// Initial vertical subtitle position from the bottom.
|
|
// ==========================================
|
|
"subtitlePosition": {
|
|
"yPercent": 10 // Y percent setting.
|
|
}, // Initial vertical subtitle position from the bottom.
|
|
|
|
// ==========================================
|
|
// Subtitle Appearance
|
|
// Primary and secondary subtitle styling.
|
|
// Hot-reload: subtitle style changes apply live without restarting SubMiner.
|
|
// ==========================================
|
|
"subtitleStyle": {
|
|
"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": false, // 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
|
|
"nameMatchColor": "#f5bde6", // Hex color used when a subtitle token matches an entry from the SubMiner character dictionary.
|
|
"fontFamily": "M PLUS 1 Medium, 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 3px 10px rgba(0,0,0,0.69)", // Text shadow setting.
|
|
"fontStyle": "normal", // Font style setting.
|
|
"backgroundColor": "rgb(30, 32, 48, 0.88)", // Background color setting.
|
|
"backdropFilter": "blur(6px)", // Backdrop filter setting.
|
|
"nPlusOneColor": "#c6a0f6", // N plus one color setting.
|
|
"knownWordColor": "#a6da95", // Known word color setting.
|
|
"jlptColors": {
|
|
"N1": "#ed8796", // N1 setting.
|
|
"N2": "#f5a97f", // N2 setting.
|
|
"N3": "#f9e2af", // N3 setting.
|
|
"N4": "#a6e3a1", // N4 setting.
|
|
"N5": "#8aadf4" // N5 setting.
|
|
}, // Jlpt colors setting.
|
|
"frequencyDictionary": {
|
|
"enabled": false, // Enable frequency-dictionary-based highlighting based on token rank. Values: true | false
|
|
"sourcePath": "", // Optional absolute path to a frequency dictionary directory. If empty, built-in discovery search paths are used.
|
|
"topX": 1000, // Only color tokens with frequency rank <= topX (default: 1000).
|
|
"mode": "single", // single: use one color for all matching tokens. banded: use color ramp by frequency band. Values: single | banded
|
|
"matchMode": "headword", // headword: frequency lookup uses dictionary form. surface: lookup uses subtitle-visible token text. Values: headword | surface
|
|
"singleColor": "#f5a97f", // Color used when frequencyDictionary.mode is `single`.
|
|
"bandedColors": [
|
|
"#ed8796",
|
|
"#f5a97f",
|
|
"#f9e2af",
|
|
"#8bd5ca",
|
|
"#8aadf4"
|
|
] // 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 4px rgba(0,0,0,0.95), 0 0 8px rgba(0,0,0,0.8), 0 0 16px rgba(0,0,0,0.55)", // Text shadow setting.
|
|
"backgroundColor": "rgba(20, 22, 34, 0.78)", // Background color setting.
|
|
"backdropFilter": "blur(6px)", // Backdrop filter setting.
|
|
"fontWeight": "600", // Font weight setting.
|
|
"fontStyle": "normal" // Font style setting.
|
|
} // Secondary setting.
|
|
}, // Primary and secondary subtitle styling.
|
|
|
|
// ==========================================
|
|
// Subtitle Sidebar
|
|
// Parsed-subtitle sidebar cue list styling, behavior, and toggle key.
|
|
// Hot-reload: subtitle sidebar changes apply live without restarting SubMiner.
|
|
// ==========================================
|
|
"subtitleSidebar": {
|
|
"enabled": false, // Enable the subtitle sidebar feature for parsed subtitle sources. Values: true | false
|
|
"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
|
|
"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.
|
|
}, // Parsed-subtitle sidebar cue list styling, behavior, and toggle key.
|
|
|
|
// ==========================================
|
|
// Shared AI Provider
|
|
// Canonical OpenAI-compatible provider transport settings shared by Anki and YouTube subtitle fixing.
|
|
// ==========================================
|
|
"ai": {
|
|
"enabled": false, // Enable shared OpenAI-compatible AI provider features. Values: true | false
|
|
"apiKey": "", // Static API key for the shared OpenAI-compatible AI provider.
|
|
"apiKeyCommand": "", // Shell command used to resolve the shared AI provider API key.
|
|
"model": "openai/gpt-4o-mini", // Model setting.
|
|
"baseUrl": "https://openrouter.ai/api", // Base URL for the shared OpenAI-compatible AI provider.
|
|
"systemPrompt": "You are a translation engine. Return only the translated text with no explanations.", // System prompt setting.
|
|
"requestTimeoutMs": 15000 // Timeout in milliseconds for shared AI provider requests.
|
|
}, // Canonical OpenAI-compatible provider transport settings shared by Anki and YouTube subtitle fixing.
|
|
|
|
// ==========================================
|
|
// AnkiConnect Integration
|
|
// Automatic Anki updates and media generation options.
|
|
// Hot-reload: ankiConnect.ai.enabled updates 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.
|
|
// ==========================================
|
|
"ankiConnect": {
|
|
"enabled": false, // Enable AnkiConnect integration. Values: true | false
|
|
"url": "http://127.0.0.1:8765", // Url setting.
|
|
"pollingRate": 3000, // Polling interval in milliseconds.
|
|
"proxy": {
|
|
"enabled": true, // Enable local AnkiConnect-compatible proxy for push-based auto-enrichment. Values: true | false
|
|
"host": "127.0.0.1", // Bind host for local AnkiConnect proxy.
|
|
"port": 8766, // Bind port for local AnkiConnect proxy.
|
|
"upstreamUrl": "http://127.0.0.1:8765" // Upstream AnkiConnect URL proxied by local AnkiConnect proxy.
|
|
}, // Proxy setting.
|
|
"tags": [
|
|
"SubMiner"
|
|
], // Tags to add to cards mined or updated by SubMiner. Provide an empty array to disable automatic tagging.
|
|
"fields": {
|
|
"word": "Expression", // Card field for the mined word or expression text.
|
|
"audio": "ExpressionAudio", // Audio setting.
|
|
"image": "Picture", // Image setting.
|
|
"sentence": "Sentence", // Sentence setting.
|
|
"miscInfo": "MiscInfo", // Misc info setting.
|
|
"translation": "SelectionText" // Translation setting.
|
|
}, // Fields setting.
|
|
"ai": {
|
|
"enabled": false, // Enable AI provider usage for Anki translation/enrichment flows. Values: true | false
|
|
"model": "", // Optional model override for Anki AI translation/enrichment flows.
|
|
"systemPrompt": "" // Optional system prompt override for Anki AI translation/enrichment flows.
|
|
}, // Ai setting.
|
|
"media": {
|
|
"generateAudio": true, // Generate audio setting. Values: true | false
|
|
"generateImage": true, // Generate image setting. Values: true | false
|
|
"imageType": "static", // Image type setting.
|
|
"imageFormat": "jpg", // Image format setting.
|
|
"imageQuality": 92, // Image quality setting.
|
|
"animatedFps": 10, // Animated fps setting.
|
|
"animatedMaxWidth": 640, // Animated max width setting.
|
|
"animatedCrf": 35, // Animated crf setting.
|
|
"syncAnimatedImageToWordAudio": true, // For animated AVIF images, prepend a frozen first frame matching the existing word-audio duration so motion starts with sentence audio. Values: true | false
|
|
"audioPadding": 0.5, // Audio padding setting.
|
|
"fallbackDuration": 3, // Fallback duration setting.
|
|
"maxMediaDuration": 30 // Max media duration setting.
|
|
}, // Media setting.
|
|
"knownWords": {
|
|
"highlightEnabled": false, // Enable fast local highlighting for words already known in Anki. Values: true | false
|
|
"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.
|
|
}, // Known words setting.
|
|
"behavior": {
|
|
"overwriteAudio": true, // Overwrite audio setting. Values: true | false
|
|
"overwriteImage": true, // Overwrite image setting. Values: true | false
|
|
"mediaInsertMode": "append", // Media insert mode setting.
|
|
"highlightWord": true, // Highlight word setting. Values: true | false
|
|
"notificationType": "osd", // Notification type setting.
|
|
"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.
|
|
}, // N plus one setting.
|
|
"metadata": {
|
|
"pattern": "[SubMiner] %f (%t)" // Pattern setting.
|
|
}, // Metadata setting.
|
|
"isLapis": {
|
|
"enabled": false, // Enabled setting. Values: true | false
|
|
"sentenceCardModel": "Japanese sentences" // Sentence card model setting.
|
|
}, // Is lapis setting.
|
|
"isKiku": {
|
|
"enabled": false, // Enabled setting. Values: true | false
|
|
"fieldGrouping": "disabled", // Kiku duplicate-card field grouping mode. Values: auto | manual | disabled
|
|
"deleteDuplicateInAuto": true // Delete duplicate in auto setting. Values: true | false
|
|
} // Is kiku setting.
|
|
}, // Automatic Anki updates and media generation options.
|
|
|
|
// ==========================================
|
|
// Jimaku
|
|
// Jimaku API configuration and defaults.
|
|
// ==========================================
|
|
"jimaku": {
|
|
"apiBaseUrl": "https://jimaku.cc", // Api base url setting.
|
|
"languagePreference": "ja", // Preferred language used in Jimaku search. Values: ja | en | none
|
|
"maxEntryResults": 10 // Maximum Jimaku search results returned.
|
|
}, // Jimaku API configuration and defaults.
|
|
|
|
// ==========================================
|
|
// YouTube Playback Settings
|
|
// Defaults for SubMiner YouTube subtitle loading and languages.
|
|
// ==========================================
|
|
"youtube": {
|
|
"primarySubLanguages": [
|
|
"ja",
|
|
"jpn"
|
|
] // Comma-separated primary subtitle language priority for YouTube auto-loading.
|
|
}, // Defaults for SubMiner YouTube subtitle loading and languages.
|
|
|
|
// ==========================================
|
|
// Anilist
|
|
// Anilist API credentials and update behavior.
|
|
// Includes optional auto-sync for a merged MRU-based character dictionary in bundled Yomitan.
|
|
// Character dictionaries are keyed by AniList media ID (no season/franchise merge).
|
|
// ==========================================
|
|
"anilist": {
|
|
"enabled": false, // Enable AniList post-watch progress updates. Values: true | false
|
|
"accessToken": "", // Optional explicit AniList access token override; leave empty to use locally stored token from setup.
|
|
"characterDictionary": {
|
|
"enabled": false, // Enable automatic Yomitan character dictionary sync for currently watched AniList media. Values: true | false
|
|
"refreshTtlHours": 168, // Legacy setting; merged character dictionary retention is now usage-based and this value is ignored.
|
|
"maxLoaded": 3, // Maximum number of most-recently-used anime snapshots included in the merged Yomitan character dictionary.
|
|
"evictionPolicy": "delete", // Legacy setting; merged character dictionary eviction is usage-based and this value is ignored. Values: disable | delete
|
|
"profileScope": "all", // Yomitan profile scope for dictionary enable/disable updates. Values: all | active
|
|
"collapsibleSections": {
|
|
"description": false, // Open the Description section by default in character dictionary glossary entries. Values: true | false
|
|
"characterInformation": false, // Open the Character Information section by default in character dictionary glossary entries. Values: true | false
|
|
"voicedBy": false // Open the Voiced by section by default in character dictionary glossary entries. Values: true | false
|
|
} // Collapsible sections setting.
|
|
} // Character dictionary setting.
|
|
}, // Anilist API credentials and update behavior.
|
|
|
|
// ==========================================
|
|
// Yomitan
|
|
// Optional external Yomitan profile integration.
|
|
// Setting yomitan.externalProfilePath switches SubMiner to read-only external-profile mode.
|
|
// For GameSentenceMiner on Linux, the default overlay profile is usually ~/.config/gsm_overlay.
|
|
// In external-profile mode SubMiner will not import, delete, or modify Yomitan dictionaries/settings.
|
|
// ==========================================
|
|
"yomitan": {
|
|
"externalProfilePath": "" // Optional external Yomitan Electron profile path to use in read-only mode for shared dictionaries/settings. Example: ~/.config/gsm_overlay
|
|
}, // Optional external Yomitan profile integration.
|
|
|
|
// ==========================================
|
|
// Jellyfin
|
|
// Optional Jellyfin integration for auth, browsing, and playback launch.
|
|
// Access token is stored in local encrypted token storage after login/setup.
|
|
// jellyfin.accessToken remains an optional explicit override in config.
|
|
// ==========================================
|
|
"jellyfin": {
|
|
"enabled": false, // Enable optional Jellyfin integration and CLI control commands. Values: true | false
|
|
"serverUrl": "", // Base Jellyfin server URL (for example: http://localhost:8096).
|
|
"username": "", // Default Jellyfin username used during CLI login.
|
|
"deviceId": "subminer", // Device id setting.
|
|
"clientName": "SubMiner", // Client name setting.
|
|
"clientVersion": "0.1.0", // Client version setting.
|
|
"defaultLibraryId": "", // Optional default Jellyfin library ID for item listing.
|
|
"remoteControlEnabled": true, // Enable Jellyfin remote cast control mode. Values: true | false
|
|
"remoteControlAutoConnect": true, // Auto-connect to the configured remote control target. Values: true | false
|
|
"autoAnnounce": false, // When enabled, automatically trigger remote announce/visibility check on websocket connect. Values: true | false
|
|
"remoteControlDeviceName": "SubMiner", // Device name reported for Jellyfin remote control sessions.
|
|
"pullPictures": false, // Enable Jellyfin poster/icon fetching for launcher menus. Values: true | false
|
|
"iconCacheDir": "/tmp/subminer-jellyfin-icons", // Directory used by launcher for cached Jellyfin poster icons.
|
|
"directPlayPreferred": true, // Try direct play before server-managed transcoding when possible. Values: true | false
|
|
"directPlayContainers": [
|
|
"mkv",
|
|
"mp4",
|
|
"webm",
|
|
"mov",
|
|
"flac",
|
|
"mp3",
|
|
"aac"
|
|
], // Container allowlist for direct play decisions.
|
|
"transcodeVideoCodec": "h264" // Preferred transcode video codec when direct play is unavailable.
|
|
}, // Optional Jellyfin integration for auth, browsing, and playback launch.
|
|
|
|
// ==========================================
|
|
// Discord Rich Presence
|
|
// Optional Discord Rich Presence activity card updates for current playback/study session.
|
|
// Uses official SubMiner Discord app assets for polished card visuals.
|
|
// ==========================================
|
|
"discordPresence": {
|
|
"enabled": false, // 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".
|
|
"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.
|
|
|
|
// ==========================================
|
|
// Immersion Tracking
|
|
// Enable/disable immersion tracking.
|
|
// Set dbPath to override the default sqlite database location.
|
|
// Policy tuning is available for queue, flush, and retention values.
|
|
// ==========================================
|
|
"immersionTracking": {
|
|
"enabled": true, // Enable immersion tracking for mined subtitle metadata. Values: true | false
|
|
"dbPath": "", // Optional SQLite database path for immersion tracking. Empty value uses the default app data path.
|
|
"batchSize": 25, // Buffered telemetry/event writes per SQLite transaction.
|
|
"flushIntervalMs": 500, // Max delay before queue flush in milliseconds.
|
|
"queueCap": 1000, // In-memory write queue cap before overflow policy applies.
|
|
"payloadCapBytes": 256, // Max JSON payload size per event before truncation.
|
|
"maintenanceIntervalMs": 86400000, // Maintenance cadence (prune + rollup + vacuum checks).
|
|
"retentionMode": "preset", // Retention mode (`preset` uses preset values, `advanced` uses explicit values). Values: preset | advanced
|
|
"retentionPreset": "balanced", // Retention preset when `retentionMode` is `preset`. Values: minimal | balanced | deep-history
|
|
"retention": {
|
|
"eventsDays": 0, // Raw event retention window in days. Use 0 to keep all.
|
|
"telemetryDays": 0, // Telemetry retention window in days. Use 0 to keep all.
|
|
"sessionsDays": 0, // Session retention window in days. Use 0 to keep all.
|
|
"dailyRollupsDays": 0, // Daily rollup retention window in days. Use 0 to keep all.
|
|
"monthlyRollupsDays": 0, // Monthly rollup retention window in days. Use 0 to keep all.
|
|
"vacuumIntervalDays": 0 // Minimum days between VACUUM runs. Use 0 to disable.
|
|
}, // Retention setting.
|
|
"lifetimeSummaries": {
|
|
"global": true, // Maintain global lifetime stats rows. Values: true | false
|
|
"anime": true, // Maintain per-anime lifetime stats rows. Values: true | false
|
|
"media": true // Maintain per-media lifetime stats rows. Values: true | false
|
|
} // Lifetime summaries setting.
|
|
}, // Enable/disable immersion tracking.
|
|
|
|
// ==========================================
|
|
// Stats Dashboard
|
|
// Local immersion stats dashboard served on localhost and available as an in-app overlay.
|
|
// Uses the immersion tracking database for overview, trends, sessions, and vocabulary views.
|
|
// ==========================================
|
|
"stats": {
|
|
"toggleKey": "Backquote", // Key code to toggle the stats overlay.
|
|
"markWatchedKey": "KeyW", // Key code to mark the current video as watched and advance to the next playlist entry.
|
|
"serverPort": 6969, // Port for the stats HTTP server.
|
|
"autoStartServer": true, // Automatically start the stats server on launch. Values: true | false
|
|
"autoOpenBrowser": true // Automatically open the stats dashboard in a browser when the server starts. Values: true | false
|
|
} // Local immersion stats dashboard served on localhost and available as an in-app overlay.
|
|
}
|