mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
Update docs and gitignore changes
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -29,7 +29,7 @@ environment.toml
|
||||
.vitepress/dist/
|
||||
docs/.vitepress/cache/
|
||||
docs/.vitepress/dist/
|
||||
test/*
|
||||
tests/*
|
||||
.worktrees/
|
||||
.codex/*
|
||||
.agents/*
|
||||
|
||||
129
README.md
129
README.md
@@ -1,64 +1,37 @@
|
||||
<div align="center">
|
||||
<img src="assets/SubMiner.png" width="169" alt="SubMiner logo">
|
||||
<h1>SubMiner</h1>
|
||||
<p>Immersion mining overlay for mpv — look up words, mine to Anki, and enrich cards with context without leaving the video.</p>
|
||||
</div>
|
||||
|
||||
An all-in-one immersion mining overlay for MPV with AnkiConnect and dictionary (Yomitan) integration.
|
||||
---
|
||||
|
||||
## What This Project Is For
|
||||
|
||||
SubMiner is for Japanese learners who watch subtitled content in mpv and want a low-friction mining loop:
|
||||
|
||||
- stay inside the video while doing lookups
|
||||
- mine to Anki quickly without manual copy/paste workflows
|
||||
- preserve card context (sentence, audio, screenshot, translation, metadata)
|
||||
- reduce tool switching between player, dictionary, and card workflow
|
||||
|
||||
## Project Goals
|
||||
|
||||
1. Keep immersion continuous by making lookup and mining happen over mpv subtitles.
|
||||
2. Preserve card quality with context-rich media and subtitle timing.
|
||||
3. Support real daily workflows (subtitle management, sync, known-word awareness, keyboard-first controls).
|
||||
4. Stay configurable with sensible defaults and advanced customization.
|
||||
5. Evolve quickly and safely with a TypeScript codebase and automated tests that make refactors easier to ship.
|
||||
|
||||
## Who It's For
|
||||
|
||||
- learners using mpv as their primary immersion player
|
||||
- users already working with Yomitan and AnkiConnect
|
||||
- miners who care about long-term card quality, not just quick word capture
|
||||
|
||||
SubMiner is likely overkill if you only want lightweight dictionary lookup without card enrichment or integrated workflow tools.
|
||||
[](https://github.com/user-attachments/assets/9235a554-ea51-4284-b14b-7bbf3defaf58)
|
||||
|
||||
## Features
|
||||
|
||||
- Real-time subtitle display from MPV via IPC socket
|
||||
- Yomitan integration for fast, on-screen lookups
|
||||
- Japanese text tokenization using MeCab with smart word boundary detection
|
||||
- Integrated texthooker-ui server for use with Yomitan
|
||||
- Integrated WebSocket server (if [mpv_websocket](https://github.com/kuroahna/mpv_websocket) is not found) to send lines to the texthooker
|
||||
- AnkiConnect integration for automatic card creation with media (audio/image)
|
||||
- Invisible subtitle position edit mode (`Ctrl/Cmd+Shift+P`, arrow keys to adjust, `Enter`/`Ctrl+S` save, `Esc` cancel)
|
||||
|
||||
## Demo
|
||||
|
||||
[](https://github.com/user-attachments/assets/9235a554-ea51-4284-b14b-7bbf3defaf58)
|
||||
- **Yomitan Integration** — Hover subtitle words to trigger dictionary lookups in the player
|
||||
- **Anki Card Enrichment** — Fills sentence, audio, screenshot, and translation on new cards automatically
|
||||
- **Dual-Layer Subtitles** — Interactive visible overlay + invisible click-through layer aligned with mpv rendering
|
||||
- **N+1 Highlighting** — Marks known vocabulary from your Anki deck so you can spot new words at a glance
|
||||
- **Texthooker & WebSocket** — Built-in texthooker page with WebSocket streaming for external tools
|
||||
- **Subtitle Download & Sync** — Search Jimaku, sync with alass or ffsubsync — all from the player
|
||||
- **Keyboard-Driven** — Mine, copy, cycle display modes, and navigate from configurable shortcuts
|
||||
- **Japanese Tokenization** — MeCab-powered word boundary detection with smart grouping
|
||||
|
||||
## Requirements
|
||||
|
||||
- `mpv` with IPC socket support
|
||||
- `mecab` and `mecab-ipadic` (recommended for Japanese tokenization)
|
||||
- `mecab` and `mecab-ipadic`
|
||||
- Linux: Hyprland (`hyprctl`) or X11 (`xdotool` + `xwininfo`)
|
||||
- macOS: Accessibility permission for window tracking
|
||||
|
||||
Optional but recommended: `yt-dlp`, `fzf`, `rofi`, `chafa`, `ffmpegthumbnailer`.
|
||||
Optional: `yt-dlp`, `fzf`, `rofi`, `chafa`, `ffmpegthumbnailer`
|
||||
|
||||
## Install
|
||||
|
||||
### Linux (AppImage)
|
||||
|
||||
Download the latest release from [GitHub Releases](https://github.com/ksyasuda/SubMiner/releases/latest):
|
||||
|
||||
```bash
|
||||
wget https://github.com/ksyasuda/SubMiner/releases/download/v0.1.0/SubMiner-0.1.0.AppImage -O ~/.local/bin/SubMiner.AppImage
|
||||
chmod +x ~/.local/bin/SubMiner.AppImage
|
||||
@@ -73,49 +46,34 @@ The `subminer` wrapper uses a [Bun](https://bun.sh) shebang, so `bun` must be on
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/ksyasuda/SubMiner.git
|
||||
cd SubMiner
|
||||
make build
|
||||
make install
|
||||
make build && make install
|
||||
```
|
||||
|
||||
If you already cloned without submodules:
|
||||
|
||||
```bash
|
||||
cd SubMiner
|
||||
git submodule update --init --recursive
|
||||
```
|
||||
|
||||
For macOS builds, signing, and platform-specific details, see [docs/installation.md](docs/installation.md).
|
||||
For macOS builds and platform details, see the [installation docs](docs/installation.md).
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Copy and customize [`config.example.jsonc`](config.example.jsonc) to `$XDG_CONFIG_HOME/SubMiner/config.jsonc` (or `~/.config/SubMiner/config.jsonc`).
|
||||
2. Start mpv with IPC enabled:
|
||||
|
||||
1. Copy [`config.example.jsonc`](config.example.jsonc) to `~/.config/SubMiner/config.jsonc`
|
||||
2. Start mpv with IPC:
|
||||
```bash
|
||||
mpv --input-ipc-server=/tmp/subminer-socket video.mkv
|
||||
```
|
||||
|
||||
3. Launch SubMiner:
|
||||
|
||||
```bash
|
||||
subminer video.mkv
|
||||
# or
|
||||
subminer https://youtu.be/...
|
||||
```
|
||||
|
||||
## Common Commands
|
||||
|
||||
```bash
|
||||
subminer # pick video from current dir (fzf)
|
||||
subminer -R # use rofi picker
|
||||
subminer # pick video from cwd (fzf)
|
||||
subminer -R # rofi picker
|
||||
subminer -d ~/Videos # set source directory
|
||||
subminer -r -d ~/Anime # recursive search
|
||||
subminer video.mkv # launch with default mpv profile (subminer)
|
||||
subminer -p gpu-hq video.mkv # override mpv profile
|
||||
subminer -T video.mkv # disable texthooker
|
||||
subminer https://youtu.be/... # YouTube playback
|
||||
```
|
||||
|
||||
## MPV Plugin (Optional)
|
||||
## MPV Plugin
|
||||
|
||||
```bash
|
||||
cp plugin/subminer.lua ~/.config/mpv/scripts/
|
||||
@@ -123,46 +81,23 @@ cp plugin/subminer.conf ~/.config/mpv/script-opts/
|
||||
# or: make install-plugin
|
||||
```
|
||||
|
||||
Requires mpv IPC: `--input-ipc-server=/tmp/subminer-socket`
|
||||
|
||||
Default chord prefix: `y` (`y-y` menu, `y-s` start, `y-S` stop, `y-t` toggle visible layer).
|
||||
Overlay Jimaku shortcut default: `Ctrl+Shift+J` (`shortcuts.openJimaku`).
|
||||
Default chord prefix: `y` (`y-y` menu, `y-s` start, `y-S` stop, `y-t` toggle overlay).
|
||||
Jimaku shortcut: `Ctrl+Shift+J`.
|
||||
|
||||
## Documentation
|
||||
|
||||
Detailed guides live in [`docs/`](docs/README.md):
|
||||
Full guides at [**docs/**](docs/README.md):
|
||||
[Installation](docs/installation.md) · [Usage](docs/usage.md) · [Mining Workflow](docs/mining-workflow.md) · [Configuration](docs/configuration.md) · [Anki Integration](docs/anki-integration.md) · [MPV Plugin](docs/mpv-plugin.md) · [Troubleshooting](docs/troubleshooting.md) · [Architecture](docs/architecture.md)
|
||||
|
||||
- [Installation](docs/installation.md) — Platform requirements, AppImage/macOS/source installs, mpv plugin
|
||||
- [Usage](docs/usage.md) — Script vs plugin workflow, keybindings, YouTube playback
|
||||
- [Mining Workflow](docs/mining-workflow.md) — End-to-end mining guide, overlay layers, card creation
|
||||
- [Configuration](docs/configuration.md) — Full config reference and option details
|
||||
- [Anki Integration](docs/anki-integration.md) — AnkiConnect setup, field mapping, media generation
|
||||
- [MPV Plugin](docs/mpv-plugin.md) — Chord keybindings, subminer.conf options, script messages
|
||||
- [Troubleshooting](docs/troubleshooting.md) — Common issues and solutions
|
||||
- [Development](docs/development.md) — Building, testing, contributing
|
||||
- [Architecture](docs/architecture.md) — Service-oriented design, composition model, and modular renderer layout (`src/renderer/{modals,handlers,utils,...}`)
|
||||
## Acknowledgments
|
||||
|
||||
### Third-Party Components
|
||||
|
||||
This project includes the following third-party components:
|
||||
|
||||
- **[Yomitan](https://github.com/yomidevs/yomitan)** - Pop-up dictionary
|
||||
- **[texthooker-ui](https://github.com/ksyasuda/texthooker-ui/tree/subminer)** - Texthooker Page
|
||||
- **[yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab)** - JLPT Yomitan Dictionary
|
||||
- **[Jiten Frequency Dictionary](https://jiten.moe/)** - Frequency Dictionary
|
||||
|
||||
### Acknowledgments
|
||||
|
||||
- **[GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner)** — Inspiration for the subtitle overlay and Yomitan integration
|
||||
- **[Jimaku.cc](https://jimaku.cc)** — Japanese subtitle provider
|
||||
|
||||
This project cherry-picks features from the following MPV scripts, ported to TypeScript:
|
||||
|
||||
- **[mpvacious](https://github.com/Ajatt-Tools/mpvacious)** — Sentence mining, screenshotting, and card updating logic
|
||||
- **[Anacreon-Script (animecards)](https://github.com/friedrich-de/Anacreon-Script)** — Copy/paste to card update flow
|
||||
- **[autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv)** — Subtitle synchronization
|
||||
- [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) — Inspiration for the overlay and Yomitan integration
|
||||
- [Jimaku.cc](https://jimaku.cc) — Japanese subtitle provider
|
||||
- [mpvacious](https://github.com/Ajatt-Tools/mpvacious), [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script), [autosubsync-mpv](https://github.com/joaquintorres/autosubsync-mpv) — Mining and sync logic ported to TypeScript
|
||||
|
||||
**Third-party:**
|
||||
[Yomitan](https://github.com/yomidevs/yomitan) · [texthooker-ui](https://github.com/ksyasuda/texthooker-ui/tree/subminer) · [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) · [Jiten Frequency Dictionary](https://jiten.moe/)
|
||||
|
||||
## License
|
||||
|
||||
GNU General Public License v3.0. See [LICENSE](LICENSE).
|
||||
[GNU General Public License v3.0](LICENSE)
|
||||
|
||||
@@ -84,53 +84,63 @@ src/renderer/
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
The main process has three layers: `main.ts` delegates to composition modules that wire together domain services. The renderer runs in a separate Electron process, connected through `preload.ts`.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
classDef root fill:#c6a0f6,stroke:#24273a,color:#24273a,stroke-width:2px
|
||||
classDef comp fill:#b7bdf8,stroke:#24273a,color:#24273a,stroke-width:1.5px
|
||||
classDef svc fill:#8aadf4,stroke:#24273a,color:#24273a,stroke-width:1.5px
|
||||
classDef ext fill:#a6da95,stroke:#24273a,color:#24273a,stroke-width:1.5px
|
||||
classDef entry fill:#c6a0f6,stroke:#363a4f,color:#24273a,stroke-width:2px
|
||||
classDef comp fill:#b7bdf8,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef svc fill:#8aadf4,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef bridge fill:#f5a97f,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef rend fill:#8bd5ca,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef ext fill:#a6da95,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
|
||||
Main["src/main.ts"]:::root
|
||||
Main["main.ts"]:::entry
|
||||
|
||||
subgraph Composition["Composition Modules"]
|
||||
Startup["Startup & Lifecycle"]:::comp
|
||||
IpcCli["IPC & CLI Wiring"]:::comp
|
||||
Overlay["Overlay & Shortcuts"]:::comp
|
||||
Subsync["Subsync"]:::comp
|
||||
subgraph Comp["Composition — src/main/"]
|
||||
Startup["Startup & Lifecycle<br/>startup · app-lifecycle<br/>startup-lifecycle · state"]:::comp
|
||||
Wiring["Runtime Wiring<br/>ipc-runtime · cli-runtime<br/>overlay-runtime · subsync-runtime"]:::comp
|
||||
end
|
||||
|
||||
subgraph Services["Domain Services"]
|
||||
OverlaySvc["Overlay Services"]:::svc
|
||||
MpvSvc["MPV Stack"]:::svc
|
||||
MiningSvc["Mining & Subtitles"]:::svc
|
||||
ShortcutIpc["Shortcuts & IPC"]:::svc
|
||||
subgraph Svc["Services — src/core/services/"]
|
||||
Mpv["MPV Stack<br/>transport · protocol<br/>state · properties"]:::svc
|
||||
Overlay["Overlay<br/>manager · window<br/>visibility · bridge"]:::svc
|
||||
Mining["Mining & Subtitles<br/>mining · field-grouping<br/>subtitle-ws · tokenizer"]:::svc
|
||||
Integrations["Integrations<br/>jimaku · subsync<br/>texthooker · yomitan"]:::svc
|
||||
end
|
||||
|
||||
subgraph External["External Boundaries"]
|
||||
Config["Config & CLI"]:::ext
|
||||
Trackers["Window Trackers"]:::ext
|
||||
Integrations["Jimaku & Subsync"]:::ext
|
||||
Bridge(["preload.ts — Electron IPC"]):::bridge
|
||||
|
||||
subgraph Rend["Renderer — src/renderer/"]
|
||||
Orchestration["renderer.ts<br/>orchestration · IPC wiring"]:::rend
|
||||
UI["subtitle-render · positioning<br/>handlers · modals"]:::rend
|
||||
end
|
||||
|
||||
Main --> Startup
|
||||
Main --> IpcCli
|
||||
Main --> Overlay
|
||||
Main --> Subsync
|
||||
subgraph Ext["External Systems"]
|
||||
mpv["mpv"]:::ext
|
||||
Anki["AnkiConnect"]:::ext
|
||||
Jimaku["Jimaku API"]:::ext
|
||||
Tracker["Window Tracker"]:::ext
|
||||
end
|
||||
|
||||
Startup --> OverlaySvc
|
||||
IpcCli --> ShortcutIpc
|
||||
Overlay --> OverlaySvc
|
||||
Overlay --> MpvSvc
|
||||
Subsync --> Integrations
|
||||
Main -->|delegates| Comp
|
||||
Startup -->|initializes| Svc
|
||||
Wiring -->|dispatches to| Svc
|
||||
|
||||
OverlaySvc --> Trackers
|
||||
MpvSvc --> MiningSvc
|
||||
ShortcutIpc --> Config
|
||||
Overlay <--> Bridge
|
||||
Mining <--> Bridge
|
||||
Bridge <--> Orchestration
|
||||
Orchestration --> UI
|
||||
|
||||
style Composition fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
style Services fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
style External fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
Mpv <-->|JSON socket| mpv
|
||||
Mining -->|HTTP| Anki
|
||||
Integrations -->|HTTP| Jimaku
|
||||
Overlay --> Tracker
|
||||
|
||||
style Comp fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
style Svc fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
style Rend fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
style Ext fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
```
|
||||
|
||||
## Composition Pattern
|
||||
@@ -154,43 +164,53 @@ The composition root (`src/main.ts`) delegates to focused modules in `src/main/`
|
||||
|
||||
This keeps side effects explicit and makes behavior easy to unit-test with fakes.
|
||||
|
||||
## Lifecycle Model
|
||||
## Program Lifecycle
|
||||
|
||||
- **Startup:**
|
||||
- `src/main/startup.ts` (`startup-service`) handles initial argv/env/backend setup and decides generate-config flow vs app lifecycle start.
|
||||
- `src/main/app-lifecycle.ts` (`app-lifecycle-service`) handles Electron single-instance + lifecycle event registration.
|
||||
- `src/main/startup-lifecycle.ts` performs ready-time initialization (config load, websocket policy, tokenizer/tracker setup, overlay auto-init decisions).
|
||||
- **Runtime:**
|
||||
- CLI/shortcut/IPC events map to service calls through `src/main/cli-runtime.ts`, `src/main/ipc-runtime.ts`, and `src/main/overlay-runtime.ts`.
|
||||
- Overlay and MPV state sync through dedicated services.
|
||||
- Runtime options and mining flows are coordinated via service boundaries.
|
||||
- **Shutdown:**
|
||||
- `app-lifecycle-service` registers cleanup hooks (`will-quit`) while teardown behavior stays delegated to focused services from `src/main/*` composition modules.
|
||||
- **Startup:** `startup.ts` parses CLI args and detects the compositor backend. If `--generate-config` is passed, it writes the template and exits. Otherwise `app-lifecycle.ts` acquires the single-instance lock and registers Electron lifecycle hooks.
|
||||
- **Initialization:** Once `app.whenReady()` fires, `startup-lifecycle.ts` loads config, resolves keybindings, creates the mpv client, initializes the MeCab tokenizer, starts the window tracker, and applies WebSocket policy — then creates the overlay window and establishes the IPC bridge.
|
||||
- **Runtime:** Event-driven. mpv property changes, IPC messages, CLI commands, and keyboard shortcuts all route through the composition layer to domain services, which update state and broadcast to the renderer.
|
||||
- **Shutdown:** Electron's `will-quit` triggers service teardown — closes the mpv socket, unregisters shortcuts, stops WebSocket and texthooker servers, destroys the window tracker, and cleans up Anki state.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
classDef phase fill:#b7bdf8,stroke:#24273a,color:#24273a,stroke-width:1.5px
|
||||
classDef decision fill:#f5a97f,stroke:#24273a,color:#24273a,stroke-width:1.5px
|
||||
classDef runtime fill:#8aadf4,stroke:#24273a,color:#24273a,stroke-width:1.5px
|
||||
classDef shutdown fill:#a6da95,stroke:#24273a,color:#24273a,stroke-width:1.5px
|
||||
classDef start fill:#c6a0f6,stroke:#363a4f,color:#24273a,stroke-width:2px
|
||||
classDef phase fill:#b7bdf8,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef decision fill:#f5a97f,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef init fill:#8aadf4,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef runtime fill:#8bd5ca,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
classDef shutdown fill:#ed8796,stroke:#363a4f,color:#24273a,stroke-width:1.5px
|
||||
|
||||
Args["CLI args / env"]:::phase --> Startup["src/main/startup.ts"]:::phase
|
||||
CLI["CLI args & environment"]:::start
|
||||
CLI --> Parse["startup.ts<br/>Parse argv · detect backend · resolve config"]:::phase
|
||||
Parse --> GenCheck{"--generate-config?"}:::decision
|
||||
GenCheck -->|yes| GenExit["Write config template & exit"]:::phase
|
||||
GenCheck -->|no| Lifecycle["app-lifecycle.ts<br/>Acquire single-instance lock<br/>Register Electron lifecycle hooks"]:::phase
|
||||
Lifecycle -->|"app.whenReady()"| Ready["startup-lifecycle.ts"]:::phase
|
||||
|
||||
Startup --> Decision{"generate-config?"}:::decision
|
||||
Ready --> Init
|
||||
subgraph Init["Initialization"]
|
||||
direction LR
|
||||
Config["Load config<br/>resolve keybindings"]:::init
|
||||
Runtime["Create mpv client<br/>init MeCab tokenizer"]:::init
|
||||
Platform["Start window tracker<br/>WebSocket policy"]:::init
|
||||
end
|
||||
|
||||
Decision -->|yes| WriteConfig["Write config + exit"]:::phase
|
||||
Decision -->|no| AppLifecycle["src/main/app-lifecycle.ts"]:::phase
|
||||
Init --> Create["Create overlay window<br/>Establish IPC bridge<br/>Load Yomitan extension"]:::phase
|
||||
|
||||
AppLifecycle --> Ready["src/main/startup-lifecycle.ts\nConfig · WebSocket · Tracker · Tokenizer · State"]:::phase
|
||||
Create --> Loop
|
||||
subgraph Loop["Runtime — event-driven"]
|
||||
direction LR
|
||||
Events["mpv · IPC · CLI<br/>shortcut events"]:::runtime
|
||||
Dispatch["Route to service<br/>via composition layer"]:::runtime
|
||||
State["Update state<br/>broadcast to renderer"]:::runtime
|
||||
Events --> Dispatch --> State
|
||||
end
|
||||
|
||||
Ready --> Runtime["Runtime Modules\nipc · cli · overlay · subsync"]:::runtime
|
||||
Loop -->|"app close"| Quit["Electron will-quit"]:::shutdown
|
||||
Quit --> Teardown["Close mpv socket · unregister shortcuts<br/>Stop WebSocket & texthooker<br/>Destroy tracker · clean Anki state"]:::shutdown
|
||||
|
||||
Runtime --> Overlay["Overlay & Mining"]:::runtime
|
||||
Runtime --> Subtitle["Subtitle Processing"]:::runtime
|
||||
Runtime --> SubsyncInt["Subsync & Jimaku"]:::runtime
|
||||
|
||||
Runtime --> WillQuit["Electron will-quit"]:::shutdown
|
||||
WillQuit --> Cleanup["Service Teardown"]:::shutdown
|
||||
style Init fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
style Loop fill:#363a4f,stroke:#494d64,color:#cad3f5
|
||||
```
|
||||
|
||||
## Why This Design
|
||||
|
||||
153
docs/index.md
153
docs/index.md
@@ -6,8 +6,8 @@ titleTemplate: Immersion Mining Workflow for MPV
|
||||
|
||||
hero:
|
||||
name: SubMiner
|
||||
text: Built for Immersion Mining
|
||||
tagline: A self-contained MPV overlay for Japanese study. Look up words, mine cards, and enrich Anki without breaking playback flow.
|
||||
text: Immersion Mining for MPV
|
||||
tagline: Look up words, mine to Anki, and enrich cards with context — all without leaving the video.
|
||||
image:
|
||||
src: /assets/SubMiner.png
|
||||
alt: SubMiner logo
|
||||
@@ -18,80 +18,80 @@ hero:
|
||||
- theme: alt
|
||||
text: Mining Workflow
|
||||
link: /mining-workflow
|
||||
- theme: alt
|
||||
text: Is This For Me?
|
||||
link: "#who-this-is-for"
|
||||
|
||||
features:
|
||||
- icon:
|
||||
src: /assets/mpv.svg
|
||||
alt: mpv icon
|
||||
title: Built for mpv
|
||||
details: Connects directly to mpv over IPC — tracks subtitles in real time, observes playback properties, and renders a self-contained overlay with everything bundled in a single application.
|
||||
details: Connects via IPC to track subtitles in real time and render a self-contained overlay — everything bundled in a single application.
|
||||
- icon:
|
||||
src: /assets/yomitan-icon.svg
|
||||
alt: Yomitan logo
|
||||
title: Yomitan Integration
|
||||
details: Hover over any word in the subtitles to trigger Yomitan dictionary lookups — get instant definitions without leaving the video player.
|
||||
details: Hover over any word in the subtitle overlay to trigger dictionary lookups — instant definitions without leaving the player.
|
||||
- icon:
|
||||
src: /assets/anki-card.svg
|
||||
alt: Anki card icon
|
||||
title: Anki Card Enrichment
|
||||
details: Add a word from Yomitan and SubMiner automatically updates the card with the sentence, audio clip, screenshot, and translation — no extra steps needed.
|
||||
details: Add a word from Yomitan and SubMiner fills in the sentence, audio clip, screenshot, and translation automatically.
|
||||
- icon:
|
||||
src: /assets/dual-layer.svg
|
||||
alt: Dual layer icon
|
||||
title: Dual-Layer Subtitle System
|
||||
details: Visible overlay with styled, interactive subtitles — plus an invisible layer that aligns with mpv's own subtitle rendering for seamless click-through lookup.
|
||||
title: Dual-Layer Subtitles
|
||||
details: Interactive visible overlay plus an invisible layer aligned with mpv's own rendering for seamless click-through lookup.
|
||||
- icon:
|
||||
src: /assets/highlight.svg
|
||||
alt: Highlight icon
|
||||
title: N+1 Word Highlighting
|
||||
details: Highlights words you already know from your Anki deck, making it easy to spot new vocabulary and identify true N+1 sentences during immersion.
|
||||
title: N+1 Highlighting
|
||||
details: Marks words you already know from your Anki deck so you can spot new vocabulary and identify N+1 sentences at a glance.
|
||||
- icon:
|
||||
src: /assets/texthooker.svg
|
||||
alt: Texthooker icon
|
||||
title: Texthooker & WebSocket
|
||||
details: Built-in texthooker page that receives subtitles over WebSocket — use it as a clipboard inserter for Yomitan or connect external tools for real-time subtitle streaming.
|
||||
details: Built-in texthooker page that receives subtitles over WebSocket — use it as a clipboard inserter or connect external tools.
|
||||
- icon:
|
||||
src: /assets/subtitle-download.svg
|
||||
alt: Subtitle download icon
|
||||
title: Subtitle Download & Sync
|
||||
details: Search and download Japanese subtitles from Jimaku, then sync them to the audio with alass or ffsubsync — all from within the player.
|
||||
details: Search and download Japanese subtitles from Jimaku, then sync to audio with alass or ffsubsync — all from within the player.
|
||||
- icon:
|
||||
src: /assets/keyboard.svg
|
||||
alt: Keyboard icon
|
||||
title: Keyboard-Driven Workflow
|
||||
details: Mine sentences, copy subtitles, cycle display modes, and trigger field grouping — all from configurable keyboard shortcuts without touching the mouse.
|
||||
title: Keyboard-Driven
|
||||
details: Mine sentences, copy subtitles, cycle display modes, and trigger field grouping — all from configurable shortcuts.
|
||||
---
|
||||
|
||||
<style>
|
||||
.demo-section {
|
||||
max-width: 960px;
|
||||
margin: 2rem auto 0;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.demo-section h2 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.demo-section p {
|
||||
color: var(--vp-c-text-2);
|
||||
margin-bottom: 1rem;
|
||||
margin-bottom: 1.25rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.demo-section video {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.workflow-section {
|
||||
max-width: 960px;
|
||||
margin: 3rem auto 0;
|
||||
margin: 4rem auto 0;
|
||||
padding: 0 24px 3rem;
|
||||
}
|
||||
|
||||
@@ -99,106 +99,52 @@ features:
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1.5rem;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.workflow-steps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 1rem;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1px;
|
||||
background: var(--vp-c-divider);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.workflow-steps {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
.workflow-step {
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--vp-c-divider);
|
||||
padding: 1.25rem 1.5rem;
|
||||
background: var(--vp-c-bg-soft);
|
||||
}
|
||||
|
||||
.workflow-step .step-number {
|
||||
font-size: 0.8rem;
|
||||
display: inline-block;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--vp-c-brand-1);
|
||||
margin-bottom: 0.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.workflow-step .step-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 0.25rem;
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.workflow-step .step-desc {
|
||||
font-size: 0.875rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--vp-c-text-2);
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="demo-section">
|
||||
|
||||
## What SubMiner Is For
|
||||
|
||||
SubMiner is for people who learn Japanese by watching subtitled content in mpv and want a low-friction mining loop:
|
||||
|
||||
- stay inside the video while looking up words
|
||||
- send mined content to Anki quickly
|
||||
- keep media context (audio, screenshot, timestamp, subtitle context) attached to each card
|
||||
- reduce tool switching between player, dictionary, and card workflow
|
||||
|
||||
</div>
|
||||
|
||||
<div class="workflow-section">
|
||||
|
||||
## Project Goals
|
||||
|
||||
<div class="workflow-steps">
|
||||
<div class="workflow-step">
|
||||
<div class="step-title">1. Keep Immersion Continuous</div>
|
||||
<div class="step-desc">Minimize context switching by making lookup and mining happen directly over mpv subtitles.</div>
|
||||
</div>
|
||||
<div class="workflow-step">
|
||||
<div class="step-title">2. Preserve Card Quality</div>
|
||||
<div class="step-desc">Attach sentence context, audio, image, and translation so mined cards stay reviewable and useful long-term.</div>
|
||||
</div>
|
||||
<div class="workflow-step">
|
||||
<div class="step-title">3. Support Real Workflows</div>
|
||||
<div class="step-desc">Handle day-to-day immersion needs: subtitle management, syncing, known-word awareness, and keyboard-first controls.</div>
|
||||
</div>
|
||||
<div class="workflow-step">
|
||||
<div class="step-title">4. Stay Configurable</div>
|
||||
<div class="step-desc">Offer defaults that work out of the box, while still letting advanced users shape behavior around their note type and setup.</div>
|
||||
</div>
|
||||
<div class="workflow-step">
|
||||
<div class="step-title">5. Evolve Safely</div>
|
||||
<div class="step-desc">Use a modular TypeScript codebase and automated tests so features can ship faster without breaking core mining behavior.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
|
||||
## See It in Action
|
||||
|
||||
SubMiner sits as a transparent overlay on top of mpv. Subtitles appear as interactive, clickable text — click a word to look it up with Yomitan, then add it to Anki with one click.
|
||||
|
||||
<video controls playsinline preload="metadata" poster="/assets/demo-poster.jpg">
|
||||
<source :src="'/assets/card-mine.webm'" type="video/webm" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="workflow-section">
|
||||
|
||||
## Who This Is For
|
||||
|
||||
- learners using mpv as their main immersion player
|
||||
- users who already rely on Yomitan + AnkiConnect
|
||||
- miners who care about preserving context on cards, not just raw words
|
||||
|
||||
SubMiner is likely overkill if you only want lightweight lookup without card enrichment, overlay controls, or integrated workflow tooling.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="workflow-section">
|
||||
|
||||
## How It Works
|
||||
@@ -212,7 +158,7 @@ SubMiner is likely overkill if you only want lightweight lookup without card enr
|
||||
<div class="workflow-step">
|
||||
<div class="step-number">02</div>
|
||||
<div class="step-title">Look Up</div>
|
||||
<div class="step-desc">Hover over a word in the subtitle overlay and hold Shift to trigger a Yomitan dictionary lookup.</div>
|
||||
<div class="step-desc">Hover over a word in the subtitle overlay and hold Shift to trigger a Yomitan lookup.</div>
|
||||
</div>
|
||||
<div class="workflow-step">
|
||||
<div class="step-number">03</div>
|
||||
@@ -227,3 +173,14 @@ SubMiner is likely overkill if you only want lightweight lookup without card enr
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="demo-section">
|
||||
|
||||
## See It in Action
|
||||
|
||||
<video controls playsinline preload="metadata" poster="/assets/demo-poster.jpg">
|
||||
<source :src="'/assets/card-mine.webm'" type="video/webm" />
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user