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.