sudacode 2924f04316 refactor(main): move domains into per-domain assemblies (#309)
* refactor(main): move the YouTube domain into an assembly

createYoutubeAssembly owns the YouTube flow, playback, media cache,
whisper subtitles, primary-subtitle notifications, and the in-app
browser, plus the browser quit-on-close state. main.ts builds it once
where the first YouTube runtime used to be and calls through the result.
Siblings built later are passed as getters or callbacks; tests pin that
construction calls none of them.

* docs(architecture): describe per-domain assemblies

* test(e2e): blank the line before mining the sub-delay cue

The previous test leaves the same cue on screen, so the overlay check
passed before the seek landed and --mine-sentence could read mpv's empty
mid-seek sub-text and skip the card.

* refactor(main): move the Jellyfin domain into an assembly

createJellyfinAssembly owns the Jellyfin composer call, its playback and
remote-progress state, the subtitle cache IO, mpv defaults, and the tray
discovery deps. main.ts builds it where the composer call used to be and
reads state through getActivePlayback/getQuitOnDisconnectArmed.

* refactor(main): move the AniList domain into an assembly

createAnilistAssembly owns the AniList setup and tracking composer calls,
the setup window, the state runtime, and the token, media-guess, in-flight
and attempted-update state they share. main.ts builds it where the setup
composer call used to be and keeps the rate limiter, which the stats
cover-art fetcher also uses.

* refactor(main): move config hot-reload, settings, and sync UI into assemblies

createSettingsAssembly owns config hot-reload and the settings window,
which share the hot-reload-applied handler; main.ts still supplies that
handler's cross-domain deps and the Yomitan deck-name helper.
createSyncAssembly owns the sync window, sync IPC runtime, and auto-sync
scheduler. Both are built where their blocks were; main.ts keeps the
registerHandlers()/start() calls at the same points.

* refactor(main): move dictionary runtimes into assemblies

createDictionariesAssembly owns the JLPT and frequency lookups and their
dictionary-root builders; main.ts still resolves the root paths at the
same point. createCharacterDictionaryAssembly owns the character
dictionary runtime, Yomitan auto-sync, and the portrait and name-candidate
lookups. Both are built where their blocks were; later siblings (Yomitan
profile policy, notifications, OSD, subtitle refresh) come in as getters
or callbacks.

* refactor(main): move overlay modal openers into an assembly

createOverlayModalsAssembly owns the shared open-with-notification helper
and the runtime options, Jimaku, TsukiHime, subtitle generation, session
help, changelog, character dictionary manager, controller, and playlist
browser openers. main.ts builds it where the openers used to live.

* refactor(logger): export the Logger type for assembly deps

The build emits declarations, and the Jellyfin assembly returns the logger
in its tray discovery deps, so tsc needs a nameable Logger type. Assemblies
now Pick from it directly.
2026-10-31 19:39:27 -07:00
2026-10-26 23:27:24 -07:00
2026-10-26 23:27:24 -07:00

SubMiner logo

SubMiner

Look up words with Yomitan or Hachidori, mine them to Anki, and track your immersion without leaving mpv

Installation · Requirements · Usage · Documentation

Downloads Release AUR Platform License TypeScript

SubMiner demo

Features

Dictionary Lookups

Hover over any word in the subtitles to open the full Yomitan popup with definitions, pitch accent, and frequency data. SubMiner bundles its own Yomitan, separate from any browser install.

Yomitan remains the default. Select the bundled Hachidori backend with dictionaryBackend: "hachidori" and restart SubMiner. The tray opens the selected backend's settings. See dictionary setup for importing dictionaries, linking an external Hachidori host, and configuring Anki.

Yomitan dictionary popup over annotated subtitles in mpv

Instant Anki Mining

Create an Anki card from the exact playback moment with one key press, click, or controller input. SubMiner fills in the sentence, an audio clip, and a screenshot or animated image.

Yomitan lookup over an anime scene with Anki mining controls

Reading Annotations

Subtitles are annotated as they play with frequency highlighting, JLPT tags, N+1 targeting, and character names from a generated dictionary. Particles and grammar-only tokens stay plain so the words worth learning stand out.

Annotated subtitles with frequency colors, character portraits, and JLPT underlines

Immersion Dashboard

A stats dashboard tracks watch time, vocabulary growth, mining throughput, session history, and trends. Everything stays on your machine, with no third-party tracking.

Stats dashboard showing watch time, cards mined, streaks, and tracking data

Integrations

YouTube Play YouTube URLs with subtitle tracks picked by your language priorities, or choose tracks yourself in the overlay picker (Ctrl+Alt+C). Requires yt-dlp
AniList Automatic episode tracking and progress sync
Jellyfin Browse your Jellyfin library, or cast to SubMiner from any Jellyfin client. Setup and discovery live in the tray menu
Jimaku Search and download Japanese subtitles. Requires a free Jimaku API key
TsukiHime Search and download subtitles extracted from anime releases, with Japanese and secondary-language tabs (Ctrl+Shift+T). Requires xz on your PATH
Subtitle generation Transcribe a video's audio into Japanese subtitles locally from the generation modal (Ctrl+Shift+G), the subtitle sidebar, or the launcher. SubMiner can download models for you; optional Silero speech detection helps focus on dialogue. Requires whisper.cpp and FFmpeg. Setup guide
AniSkip Automatic intro detection with chapter markers and a one-key skip (TAB by default)
alass / ffsubsync Retime a subtitle against the audio or another subtitle track (Ctrl+Alt+S). Requires alass or ffsubsync; set subsync.alass_path or subsync.ffsubsync_path if they are not in /usr/bin
WebSocket Plain subtitle feed plus a dedicated annotated feed for texthooker pages and custom tools
Plugins Load your own JavaScript at startup to react to playback, run timers, add key-bound actions, and show results in an overlay panel. The bundled example explains the current line with Codex. Plugin guide


Requirements

SubMiner runs on Linux, macOS 11+, and Windows 10+. Only mpv is required to run it (plus fuse2 for the Linux AppImage). Mining cards also needs Anki with the AnkiConnect add-on. Everything else is optional.

Dependency Status What it does
mpv Required The video player SubMiner draws over
fuse2 Required (Linux) Running the AppImage
Anki + AnkiConnect Required to mine Card creation from the Yomitan popup
ffmpeg Recommended Audio clips and screenshots on cards
MeCab + mecab-ipadic Recommended More accurate N+1, JLPT, and frequency highlighting
xdotool + xwininfo Required (X11) Window tracking on desktops other than Hyprland or Sway
yt-dlp Optional YouTube playback
xz Optional TsukiHime subtitle downloads (most Linux distros already have it)
alass / ffsubsync Optional Subtitle sync
whisper.cpp Optional Subtitle generation
guessit Optional Better title, season, and episode detection for AniSkip and AniList
fzf / rofi Optional Video picker in the subminer launcher (rofi is Linux only)
chafa, ffmpegthumbnailer Optional Thumbnail previews in the launcher pickers
Platform-specific install commands

Arch Linux:

sudo pacman -S --needed mpv ffmpeg
paru -S --needed mecab-git mecab-ipadic   # MeCab is only in the AUR

On desktops other than Hyprland or Sway, also install xdotool and xorg-xwininfo.

macOS:

brew install mpv ffmpeg mecab mecab-ipadic

Windows:

winget install shinchiro.mpv
winget install Gyan.FFmpeg

Then reopen your terminal and check mpv --version and ffmpeg -version. ffmpeg must be on PATH; mpv does not have to be. If mpv is not found, either add its folder (usually %LOCALAPPDATA%\Programs\mpv) to PATH or enter the full path to mpv.exe during first-run setup.

Scoop is the alternative if you want one package manager for everything. It is the only one that also carries xz:

scoop bucket add extras
scoop install extras/mpv main/ffmpeg main/yt-dlp main/xz

See the installation guide for Ubuntu, Debian, and Fedora commands and the full optional package lists.


Quick Start

1. Install SubMiner

Arch Linux (AUR)
paru -S subminer-bin

Includes the AppImage and the subminer command.

Linux (AppImage)
mkdir -p ~/.local/bin
wget https://github.com/ksyasuda/SubMiner/releases/latest/download/SubMiner.AppImage -O ~/.local/bin/SubMiner.AppImage \
 && chmod +x ~/.local/bin/SubMiner.AppImage

The AppImage is all you need. First-run setup can install the optional subminer command, which runs on a copy of Bun bundled with the app, so you do not need Bun installed.

macOS (DMG)

Download the latest DMG from GitHub Releases and drag SubMiner.app into /Applications.

Then enable SubMiner under System Settings > Privacy & Security > Accessibility (named Device Control and Data Access on macOS 27) so the overlay can track the mpv window. Without it, SubMiner falls back to a basic window lookup that cannot tell when mpv is minimized. If macOS blocks the first launch, right-click the app and choose Open.

Windows

Download and run the latest installer (SubMiner-<version>.exe) from GitHub Releases. A portable .zip is also available.

From source

See the build-from-source guide.

2. Launch & Set Up

Start SubMiner and the setup window opens on first launch. It creates your config file, imports Yomitan dictionaries (you need at least one for lookups), and can install the subminer command. On Windows it also creates a SubMiner mpv shortcut.

subminer app --setup                     # AUR
~/.local/bin/SubMiner.AppImage --setup   # AppImage

On macOS, open SubMiner.app from /Applications. On Windows, run SubMiner from the Start menu. To reopen setup later, run subminer app --setup.

For card creation, keep Anki open with AnkiConnect installed. SubMiner connects to it at its default address with no extra setup.

3. Mine

subminer video.mkv          # play a video with SubMiner
subminer /path/to/dir       # pick a file with fzf
subminer -R /path/to/dir    # pick a file with rofi (Linux only)
subminer -H                 # watch history: replay, next, or previous episode
subminer doctor             # check your setup

On Windows, double-click the SubMiner mpv shortcut or drag a video file onto it.

Starting mpv some other way? See Launching mpv yourself for the IPC socket option the overlay needs.

Documentation

Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more: docs.subminer.moe


Acknowledgments

SubMiner builds on the work of these open-source projects:

Project Role
ani-skip AniSkip API client for anime intro/outro skip timestamps
Anacreon-Script Inspiration for the mining workflow
asbplayer Inspiration for subtitle sidebar and logic for YouTube subtitle parsing
Bee's Character Dictionary Character name recognition in subtitles
Bun Bundled runtime for the subminer command-line launcher
GameSentenceMiner Inspiration for Electron overlay with Yomitan integration
jellyfin-mpv-shim Jellyfin integration
Jimaku.cc Japanese subtitle search and downloads
Renji's Texthooker Page Base for the WebSocket texthooker integration
Yomitan Default dictionary engine and morphological parser
Hachidori Alternative dictionary backend, powered by HoshiDicts
yomitan-jlpt-vocab JLPT level tags for vocabulary

License

SubMiner is released under the GNU General Public License v3.0.

Release packages also bundle an unmodified copy of Bun, which is MIT licensed and statically links JavaScriptCore (LGPL 2.0) and TinyCC (LGPL 2.1). Its license texts and third-party notices ship inside the app under resources/bun/licenses, and each release publishes bun-v1.3.5-source.tar.gz with the corresponding source. See Bundled Bun runtime.

Languages
TypeScript 95.5%
Lua 1.6%
CSS 1.2%
JavaScript 0.9%
HTML 0.4%
Other 0.2%