diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bf46008..e5fc002 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -98,6 +98,17 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ - name: Build unversioned AppImage
+ run: |
+ shopt -s nullglob
+ appimages=(release/SubMiner-*.AppImage)
+ if [ "${#appimages[@]}" -eq 0 ]; then
+ echo "No versioned AppImage found to create unversioned artifact."
+ ls -la release
+ exit 1
+ fi
+ cp "${appimages[0]}" release/SubMiner.AppImage
+
- name: Upload AppImage artifact
uses: actions/upload-artifact@v4
with:
@@ -213,8 +224,7 @@ jobs:
- name: Package optional assets bundle
run: |
- VERSION="${GITHUB_REF#refs/tags/}"
- tar -czf "release/subminer-assets-${VERSION}.tar.gz" \
+ tar -czf "release/subminer-assets.tar.gz" \
config.example.jsonc \
plugin/subminer.lua \
plugin/subminer.conf \
@@ -264,8 +274,8 @@ jobs:
### AppImage (Recommended)
1. Download the AppImage below
- 2. Make it executable: `chmod +x SubMiner-*.AppImage`
- 3. Run: `./SubMiner-*.AppImage`
+ 2. Make it executable: `chmod +x SubMiner.AppImage`
+ 3. Run: `./SubMiner.AppImage`
### macOS
1. Download `subminer-*.dmg`
@@ -276,7 +286,7 @@ jobs:
See the [README](https://github.com/${{ github.repository }}#installation) for manual installation instructions.
### Optional Assets (config example + mpv plugin + rofi theme)
- 1. Download `subminer-assets-*.tar.gz`
+ 1. Download `subminer-assets.tar.gz`
2. Extract and copy `config.example.jsonc` to `~/.config/SubMiner/config.jsonc`
3. Copy `plugin/subminer.lua` to `~/.config/mpv/scripts/`
4. Copy `plugin/subminer.conf` to `~/.config/mpv/script-opts/`
diff --git a/README.md b/README.md
index 8b12165..82ba7af 100644
--- a/README.md
+++ b/README.md
@@ -27,9 +27,11 @@ SubMiner is an Electron overlay that sits on top of mpv. It turns your video pla
- **Hover to look up** — Yomitan dictionary popups directly on subtitles
- **One-key mining** — Creates Anki cards with sentence, audio, screenshot, and translation
- **N+1 highlighting** — Marks known words from your Anki deck so unknown ones jump out
-- **Subtitle tools** — Download from Jimaku, sync with alass/ffsubsync, all in-player
+- **Subtitle tools** — Download from Jimaku, sync with alass/ffsubsync
- **Immersion tracking** — SQLite-powered stats on your watch time and mining activity
-- **Texthooker page built in** — WebSocket streaming to external tools, no extra setup
+- **Custom texthooker page** — Built-in custom texthooker page and websocket, no extra setup
+- **Jellyfin integration** — Remote playback setup, cast device mode, and direct playback launch
+- **AniList progress** — Track episode completion and push watching progress automatically
## Quick start
@@ -38,10 +40,11 @@ SubMiner is an Electron overlay that sits on top of mpv. It turns your video pla
**Linux (AppImage):**
```bash
-wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner-0.1.0.AppImage -O ~/.local/bin/SubMiner.AppImage
+wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.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
+
```
> [!NOTE]
@@ -52,13 +55,14 @@ chmod +x ~/.local/bin/subminer
### 2. Install the mpv plugin and configuration file
```bash
-wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets-0.1.0.tar.gz -O /tmp/subminer-assets.tar.gz
+wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets.tar.gz -O /tmp/subminer-assets.tar.gz
tar -xzf /tmp/subminer-assets.tar.gz -C /tmp
cp /tmp/plugin/subminer.lua ~/.config/mpv/scripts/
cp /tmp/plugin/subminer.conf ~/.config/mpv/script-opts/
mkdir -p ~/.config/SubMiner && cp /tmp/config.example.jsonc ~/.config/SubMiner/config.jsonc
```
+
### 3. Set up Yomitan Dictionaries
```bash
@@ -69,7 +73,7 @@ subminer app --start --yomitan
```bash
subminer app --start --background
-subminer video.mkv
+subminer video.mkv # toggle invisible overlay with y-i and visible overlay with y-t
```
## Requirements
@@ -89,7 +93,7 @@ For full guides on configuration, Anki, Jellyfin, and more, see [docs.subminer.m
## Acknowledgments
-Built on the shoulders of [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [mpvacious](https://github.com/Ajatt-Tools/mpvacious), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv). Subtitles powered by [Jimaku.cc](https://jimaku.cc). Dictionary lookups via [Yomitan](https://github.com/yomidevs/yomitan).
+Built on the shoulders of [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner), [texthooker-ui](https://github.com/Renji-XD/texthooker-ui), [mpvacious](https://github.com/Ajatt-Tools/mpvacious), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), and [autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv). Subtitles powered by [Jimaku.cc](https://jimaku.cc). Dictionary lookups via [Yomitan](https://github.com/yomidevs/yomitan).
## License
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 84f2c71..5143b98 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -87,6 +87,7 @@ export default {
items: [
{ text: 'Building & Testing', link: '/development' },
{ text: 'Architecture', link: '/architecture' },
+ { text: 'IPC + Runtime Contracts', link: '/ipc-contracts' },
],
},
],
diff --git a/docs/README.md b/docs/README.md
index 34845a0..8b8809b 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -25,6 +25,7 @@ make docs-preview # Preview built site at http://localhost:4173
- [Anki Integration](/anki-integration) — AnkiConnect setup, field mapping, media generation, field grouping
- [Jellyfin Integration](/jellyfin-integration) — Optional Jellyfin auth, cast discovery, remote control, and playback launch
- [Immersion Tracking](/immersion-tracking) — SQLite schema, retention/rollup policies, query templates, and extension points
+- [Performance & Tuning](/troubleshooting#performance-and-resource-impact) — Resource usage and practical low-impact profile
- [JLPT Vocabulary](/jlpt-vocab-bundle) — Bundled term-meta bank for JLPT level underlining and frequency highlighting
- [MPV Plugin](/mpv-plugin) — Chord keybindings, subminer.conf options, script messages
- [Troubleshooting](/troubleshooting) — Common issues and solutions by category
@@ -33,3 +34,4 @@ make docs-preview # Preview built site at http://localhost:4173
- [Building & Testing](/development) — Build commands, test suites, contributor notes, environment variables
- [Architecture](/architecture) — Service-oriented design, composition model, renderer module layout
+- [IPC + Runtime Contracts](/ipc-contracts) — Main/renderer IPC contracts and contributor onboarding
diff --git a/docs/architecture.md b/docs/architecture.md
index 90084da..257f45b 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -310,14 +310,18 @@ flowchart LR
Warmups["Background
warmups"]:::phase
- Warmups --> W1["MeCab"]:::warmup
- Warmups --> W2["Yomitan"]:::warmup
- Warmups --> W3["JLPT + freq
dictionaries"]:::warmup
- Warmups --> W4["Jellyfin"]:::warmup
- Warmups --> W5["Discord"]:::warmup
- Warmups --> W6["AniList"]:::warmup
+ subgraph WarmupGroup[" "]
+ direction TB
+ W1["MeCab"]:::warmup
+ W2["Yomitan"]:::warmup
+ W3["JLPT + freq
dictionaries"]:::warmup
+ W4["Jellyfin"]:::warmup
+ W5["Discord"]:::warmup
+ W6["AniList"]:::warmup
+ W1 ~~~ W2 ~~~ W3 ~~~ W4 ~~~ W5 ~~~ W6
+ end
- W1 & W2 & W3 & W4 & W5 & W6 --> Loop
+ Warmups --> WarmupGroup
subgraph Loop["Runtime — event-driven"]
direction TB
@@ -329,6 +333,10 @@ flowchart LR
Process --> Broadcast["Update AppState
broadcast to windows"]:::runtime
end
+ WarmupGroup --> Loop
+
+ style WarmupGroup fill:transparent,stroke:none
+
Loop -->|"quit signal"| Quit["will-quit"]:::shutdown
Quit --> T1["Tray · config watcher
global shortcuts"]:::shutdown
@@ -354,6 +362,7 @@ flowchart LR
- Add behavior to an existing service in `src/core/services/*` or create a focused runtime module under `src/main/runtime/*`; avoid ad-hoc logic in `main.ts`.
- Add new cross-process channels in `src/shared/ipc/contracts.ts` first, validate payloads in `src/shared/ipc/validators.ts`, then wire handlers in IPC runtime modules.
+- See also the contributor IPC onboarding page: [IPC + Runtime Contracts](/ipc-contracts).
- If change spans startup/overlay/mpv/integration wiring, prefer composing through `src/main/runtime/domains/*` + `src/main/runtime/composers/*` rather than direct wiring in `main.ts`.
- Keep service APIs explicit and narrowly scoped, and preserve existing CLI flag / IPC channel behavior unless the change is intentionally breaking.
- Add or update focused tests (including malformed-payload IPC tests) when runtime boundaries or contracts change.
diff --git a/docs/configuration.md b/docs/configuration.md
index 5d031cb..3bdb7ba 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -1,3 +1,7 @@
+---
+outline: [2, 3]
+---
+
# Configuration
Settings are stored in `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc` when `XDG_CONFIG_HOME` is unset).
diff --git a/docs/installation.md b/docs/installation.md
index 7061095..18728c4 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -43,12 +43,13 @@ Download the latest AppImage from [GitHub Releases](https://github.com/ksyasuda/
```bash
# Download and install AppImage
-wget https://github.com/ksyasuda/SubMiner/releases/download/v0.1.0/SubMiner-0.1.0.AppImage -O ~/.local/bin/SubMiner.AppImage
+wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage
chmod +x ~/.local/bin/SubMiner.AppImage
# Download subminer wrapper script
-wget https://github.com/ksyasuda/SubMiner/releases/download/v0.1.0/subminer -O ~/.local/bin/subminer
+wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer -O ~/.local/bin/subminer
chmod +x ~/.local/bin/subminer
+
```
The `subminer` wrapper uses a Bun shebang (`#!/usr/bin/env bun`), so [Bun](https://bun.sh) must be installed and available on `PATH`.
@@ -135,22 +136,7 @@ Ensure `mecab` is available on your PATH when launching SubMiner.
binary_path=/Applications/SubMiner.app/Contents/MacOS/subminer
```
-## Windows
-
-Windows support is available through the mpv plugin. Set the binary and socket path in `subminer.conf`:
-
-```ini
-binary_path=C:\\Program Files\\subminer\\subminer.exe
-socket_path=\\\\.\\pipe\\subminer-socket
-```
-
-Launch mpv with:
-
-```bash
-mpv --input-ipc-server=\\\\.\\pipe\\subminer-socket video.mkv
-```
-
-## MPV Plugin (Optional)
+## MPV Plugin (Recommended)
The Lua plugin provides in-player keybindings to control the overlay from mpv. It communicates with SubMiner by invoking the binary with CLI flags.
@@ -160,7 +146,7 @@ mpv must be launched with `--input-ipc-server=/tmp/subminer-socket` for SubMiner
```bash
# Option 1: install from release assets bundle
-wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets-0.1.0.tar.gz -O /tmp/subminer-assets.tar.gz
+wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets.tar.gz -O /tmp/subminer-assets.tar.gz
tar -xzf /tmp/subminer-assets.tar.gz -C /tmp
mkdir -p ~/.config/SubMiner
cp /tmp/config.example.jsonc ~/.config/SubMiner/config.jsonc
@@ -171,7 +157,7 @@ cp /tmp/plugin/subminer.conf ~/.config/mpv/script-opts/
# make install-plugin
```
-## Rofi Theme (Optional)
+## Rofi Theme (Linux Only)
SubMiner ships a default rofi theme at `assets/themes/subminer.rasi`.
diff --git a/docs/ipc-contracts.md b/docs/ipc-contracts.md
new file mode 100644
index 0000000..d8cc0b0
--- /dev/null
+++ b/docs/ipc-contracts.md
@@ -0,0 +1,47 @@
+# IPC + Runtime Contracts
+
+## Core Surfaces
+
+- `src/shared/ipc/contracts.ts`: canonical channel names and payload contracts
+- `src/shared/ipc/validators.ts`: runtime payload validation/parsing
+- `src/preload.ts`: renderer bridge to approved IPC endpoints
+- `src/main/ipc-runtime.ts`: main-process registration and handler routing
+- `src/core/services/ipc.ts`: service-level invoke handling and guardrails
+- `src/core/services/anki-jimaku-ipc.ts`: integration-specific IPC boundary
+- `src/main/cli-runtime.ts`: CLI/runtime command boundary
+
+## Contract Rules
+
+- Use shared contract constants; avoid ad-hoc literal channel strings.
+- Validate `invoke` payloads before domain/service logic.
+- Return structured failures (`{ ok: false, error }`) where possible.
+- Keep payloads narrow and explicit.
+- Update contracts, validators, preload types, and handlers in the same change when shape changes.
+
+## Add a New IPC Action
+
+1. Add channel in `src/shared/ipc/contracts.ts`.
+2. Add or extend validator in `src/shared/ipc/validators.ts`.
+3. Expose typed bridge method in `src/preload.ts`.
+4. Register handler in `src/main/ipc-runtime.ts` (or relevant domain runtime module).
+5. Add tests for valid and malformed payload cases in `src/core/services/*`.
+6. Update renderer tests when behavior or state transitions change.
+
+## Runtime State Notes
+
+- Prefer runtime/domain composition via `src/main/runtime/composers/*` and `src/main/runtime/domains/*`.
+- Route shared mutable state updates through transition helpers in `src/main/state.ts` for migrated domains.
+- Keep IPC handlers thin; avoid embedding business logic in transport wiring.
+
+## Troubleshooting
+
+- Unknown payload in handler: confirm validator is applied before handler/service call.
+- Renderer invoke fails: verify preload bridge method and channel registration.
+- Contract drift: compare shared contract, validator, preload bridge, and main handler signatures together.
+
+## Related Docs
+
+- [Architecture](/architecture)
+- [Development](/development)
+- [Configuration](/configuration)
+- [Troubleshooting](/troubleshooting)
diff --git a/docs/mining-workflow.md b/docs/mining-workflow.md
index b156dc2..0e3f540 100644
--- a/docs/mining-workflow.md
+++ b/docs/mining-workflow.md
@@ -15,7 +15,7 @@ Watch video → See subtitle → Click word → Yomitan lookup → Add to Anki
## Subtitle Delivery Path (Startup + Runtime)
-SubMiner now prioritizes subtitle responsiveness over heavy initialization:
+SubMiner prioritizes subtitle responsiveness over heavy initialization:
1. The first subtitle render is **plain text first** (no tokenization wait).
2. Tokenized enrichment (word spans, known-word flags, JLPT/frequency metadata) is applied right after parsing completes.
diff --git a/docs/mpv-plugin.md b/docs/mpv-plugin.md
index bb37208..72be4cb 100644
--- a/docs/mpv-plugin.md
+++ b/docs/mpv-plugin.md
@@ -6,12 +6,13 @@ The SubMiner mpv plugin (`subminer.lua`) provides in-player keybindings to contr
```bash
# From release bundle:
-wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets-0.1.0.tar.gz -O /tmp/subminer-assets.tar.gz
+wget https://github.com/ksyasuda/SubMiner/releases/latest/download/subminer-assets.tar.gz -O /tmp/subminer-assets.tar.gz
tar -xzf /tmp/subminer-assets.tar.gz -C /tmp
mkdir -p ~/.config/SubMiner
cp /tmp/config.example.jsonc ~/.config/SubMiner/config.jsonc
cp /tmp/plugin/subminer.lua ~/.config/mpv/scripts/
cp /tmp/plugin/subminer.conf ~/.config/mpv/script-opts/
+
# Or from source checkout: make install-plugin
```
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
index 39ce9ca..94c049e 100644
--- a/docs/troubleshooting.md
+++ b/docs/troubleshooting.md
@@ -21,6 +21,81 @@ SubMiner retries the connection automatically with increasing delays (200 ms, 50
- Use `--dev`/`--debug` only to force app/dev mode (for example to get dev behavior from the overlay/app); they do not change log verbosity.
- You can combine both, for example `SubMiner.AppImage --start --dev --log-level debug`, when you need maximum diagnostics.
+## Performance and Resource Impact
+
+### At a glance
+
+- Baseline: `SubMiner --start` is usually lightweight for normal playback.
+- Common spikes come from:
+ - first subtitle parse/tokenization bursts
+ - media generation (`ffmpeg` audio/image and AVIF paths)
+ - media sync and subtitle tooling (`alass`, `ffsubsync`, `whisper` fallback path)
+ - `ankiConnect` enrichment and frequent polling
+
+### If playback feels sluggish
+
+1. Reduce overlay workload:
+
+- set secondary subtitles hidden:
+ - `secondarySub.defaultMode: "hidden"`
+- disable optional enrichment:
+ - `subtitleStyle.enableJlpt: false`
+ - `subtitleStyle.frequencyDictionary.enabled: false`
+
+2. Reduce rendering pressure:
+
+- lower `subtitleStyle.fontSize`
+- keep overlay complexity minimal during heavy CPU periods
+
+3. Reduce media overhead:
+
+- keep `ankiConnect.media.imageType` set to `static` (avoid animated AVIF unless needed)
+- lower `ankiConnect.media.imageQuality`
+- reduce `ankiConnect.media.maxMediaDuration`
+
+4. Lower integration cost:
+
+- disable AI translation when not needed (`ankiConnect.ai.enabled: false`)
+- if needed, run immersion telemetry with lower duration expectations (`immersionTracking.enabled: false` for constrained sessions)
+- prefer YouTube `--mode automatic` over `preprocess` on low-resource systems
+
+### Practical low-impact profile
+
+```json
+{
+ "subtitleStyle": {
+ "fontSize": 30,
+ "enableJlpt": false,
+ "frequencyDictionary": {
+ "enabled": false
+ }
+ },
+ "secondarySub": {
+ "defaultMode": "hidden"
+ },
+ "ankiConnect": {
+ "media": {
+ "imageType": "static",
+ "imageQuality": 80,
+ "maxMediaDuration": 12
+ },
+ "ai": {
+ "enabled": false
+ }
+ },
+ "immersionTracking": {
+ "enabled": false
+ }
+}
+```
+
+### If usage is still high
+
+- Confirm only one SubMiner instance is running.
+- Check whether bottlenecks are `ffmpeg`, `yt-dlp`, or sync tooling in system monitor.
+- Use `info` logs by default; keep `debug` for targeted diagnosis.
+- Reproduce once with `SubMiner.AppImage --start --log-level debug` and open DevTools (`y` then `d`) if freezes recur.
+
**"Failed to parse MPV message"**
Logged when a malformed JSON line arrives from the mpv socket. Usually harmless — SubMiner skips the bad line and continues. If it happens constantly, check that nothing else is writing to the same socket path.