Files
SubMiner/docs-site/mining-workflow.md
T
sudacode 430373f010 feat(tokenizer): use Yomitan word classes for subtitle POS filtering (#57)
* feat(tokenizer): use Yomitan word classes for subtitle POS filtering

- Carry matched headword wordClasses from termsFind into YomitanScanToken
- Map recognized Yomitan wordClasses to SubMiner coarse POS before annotation
- MeCab enrichment now fills only missing POS fields, preserving existing coarse pos1
- Exclude standalone grammar particles, して helper fragments, and single-kana surfaces from annotations
- Respect source-text punctuation gaps when counting N+1 sentence words
- Preserve known-word highlight on excluded kanji-containing tokens
- Add backlog tasks 304 (N+1 boundary bug) and 305 (wordClasses POS, done)

* fix(tokenizer): preserve annotation and enrichment behavior

* fix: restore jlpt subtitle underlines

* fix: exclude kana-only n+1 targets

* fix: refresh overlay on Hyprland fullscreen

* fix: address fullscreen and n-plus-one review notes

* fix: address CodeRabbit review comments

* fix: accept modified digits for multi-line sentence mining

* Cancel pending Linux MPV fullscreen overlay refresh bursts

- return a cancel handle from the Linux refresh burst scheduler
- clear pending refresh bursts when overlays hide or windows close
- tighten the burst test polling to wait for the async refresh

* fix: suppress N+1 for kana-only candidates and fix minSentenceWords coun

- Treat kana-only tokens with surrounding subtitle punctuation (…, ―, etc.) as kana-only so they are not promoted to N+1 targets
- Exclude unknown tokens filtered from N+1 targeting from the minSentenceWords count so filtered kana-only unknowns cannot satisfy sentence length threshold
- Add regression tests for kana-only candidate suppression and filtered-unknown padding cases

* Suppress subtitle annotations for grammar fragments

- Hide annotation metadata for auxiliary inflection and ja-nai endings
- Preserve lexical `くれる` forms and add regression coverage

* Fix kana-only N+1 tokenizer regression test

- Use a pure-kana fixture for the subtitle token N+1 case
- Update task notes for the latest CodeRabbit follow-up

* Fix managed playback exit and tokenizer grammar splits

- Ignore background stats daemons during regular app startup
- Split standalone grammar endings before applying annotations
- Clear helper-span annotations for auxiliary-only tokens

* fix: refresh current subtitle after known-word mining

* fix: suppress sigh interjection annotations

* fix: preserve jlpt underline color after lookup

* Replace grammar-ending permutations with shared matcher; preserve word a

- Extract `grammar-ending.ts` with `isStandaloneGrammarEndingText` / `isSubtitleGrammarEndingText` pattern matchers
- Replace `STANDALONE_GRAMMAR_ENDINGS` set in parser-selection-stage with shared matcher
- Replace generated phrase sets in subtitle-annotation-filter with shared matcher
- Remove stale duplicate subtitle-exclusion constants and helpers from annotation-stage
- Manual clipboard card updates now write only to the sentence audio field, leaving word/expression audio untouched

* fix: CI changelog, annotation options threading, and Jellyfin quit

- Add `type: fixed` / `area:` frontmatter to `changes/319` to pass `changelog:lint`
- Thread `TokenizerAnnotationOptions` through `stripSubtitleAnnotationMetadata` so `sourceText` is honored
- Include `jellyfinPlay` in `shouldQuitOnDisconnectWhenOverlayRuntimeInitialized` predicate
- Make mouse test `elementFromPoint` stubs coordinate-sensitive
- Make Lua test `.tmp` mkdir portable on Windows

* Preserve overlay across macOS flaps and mpv playlist changes

- keep visible overlays alive during transient macOS tracker loss
- reuse the running mpv overlay path on playlist navigation
- update regression coverage and changelog fragments

* fix: restore stats daemon deferral

* fix: keep subtitle prefetch alive after cache hits

* Fix JLPT underline color drift and AniList skipped-threshold sync

- Replace JLPT `text-decoration` underlines with `border-bottom` so Chromium selection/hover cannot repaint them to another annotation's color
- Lock JLPT underline color for combined annotation selectors (known, n+1, frequency) and character hover/selection states
- Trigger AniList post-watch check on every mpv time-position update to catch skipped completion thresholds
- Fall back to filename-parser season/episode when guessit omits them

* fix: address coderabbit feedback

* fix: sync AniList after seeked completion

* fix: preserve ordinal frequency annotations

* fix: preserve known highlighting for filtered tokens

* fix: address PR #57 CodeRabbit feedback

- Acquire AniList post-watch in-flight lock before async gating to prevent duplicate writes
- Isolate manual watched mark result from AniList post-watch callback failures
- Report known-word cache clears as mutations during immediate append when state existed
- Add regression tests for each fix

* fix: stop AniList setup reopening on Linux when keyring token exists

- Gate setup success on token persistence: `saveToken` now returns `boolean`; on failure, keeps the setup window open instead of reporting success
- Config reload passes `allowSetupPrompt: false` so playback reloads don't re-open the setup window
- Add regression test for persistence-failure path

* fix: suppress known highlights for subtitle particles

* fix: retry transient AniList safeStorage failures

* fix: hide overlay focus ring

* fix: align Hyprland fullscreen overlays

* fix: restore subtitle playback keybindings

* fix: align Hyprland overlay windows to mpv and stop pinning them

- Force-apply exact Hyprland move/resize/setprop dispatches when bounds are provided
- Stop pinning overlay windows; toggle pin off when Hyprland reports pinned=true
- Compensate stats overlay outer placement for Electron/Wayland content insets
- Make stats overlay window and page opaque so mpv cannot show through transparent insets
- Constrain stats app to h-screen with internal scroll so content covers mpv from y=0
- Lock overlay/stats window titles against page-title-updated events
- Add regression coverage for placement dispatches, inset compensation, and CSS overlay mode

* fix: retain frequency rank for honorific prefix-noun tokens

- Add `shouldAllowHonorificPrefixNounFrequency` to exempt お/ご/御 + noun merged tokens from frequency exclusion
- Add regression test for `ご機嫌` asserting rank 5484 is preserved after MeCab enrichment and annotation
- Close TASK-341

* fix: map openCharacterDictionary session action to --open-character-dict

- Add missing Lua CLI dispatch entry for openCharacterDictionary
- Add regression test for Alt+Meta+A binding and CLI flag forwarding

* fix: keep macOS overlay interactive while mpv remains active

- Overlay no longer hides or becomes click-through during tracker refreshes when mpv is the focused window
- Preserve already-visible overlay when tracker is temporarily not ready but mpv target signal is active
- Add regression tests for active-mpv tracker refresh and transient tracker-not-ready paths

* fix: address coderabbit subtitle follow-ups

* fix: resolve media detail from sessions when lifetime summary is absent

- Change `getMediaDetail` JOIN to LEFT JOIN on `imm_lifetime_media` and fall back to aggregated session metrics when no lifetime row exists
- Add filter `AND (lm.video_id IS NOT NULL OR s.session_id IS NOT NULL)` to keep results valid
- Add regression test covering the session-visible / media-detail-missing mismatch

* fix: address PR-57 CodeRabbit findings and CI failures

- use filtered word counts in media detail session token aggregation
- cancel fullscreen refresh burst on exit via updateLinuxMpvFullscreenOverlayRefreshBurst
- guard Hyprland JSON.parse in try/catch; exclude windowtitle from geometry events
- narrow focus suppression from :focus to :focus-visible
- apply JLPT lock selectors to word-name-match tokens (N1–N5)

* fix: macOS overlay z-order and Yomitan compound token known highlighting

- Release always-on-top when tracked mpv loses foreground on macOS
- Skip visible overlay blur restacking on macOS to avoid covering unrelated windows
- Prefer Yomitan internal parse tokens over fragmented scanner output for known-word decisions
- Add regression tests for both behaviors

* fix: macOS visible-overlay blur no longer invokes Windows-only blur call

- Split win32/darwin branches in handleOverlayWindowBlurred so darwin visible blur returns early without calling onWindowsVisibleOverlayBlur
- Add regression test asserting Windows callback stays inactive on macOS visible overlay blur
- Close TASK-347
2026-05-12 12:08:09 -07:00

13 KiB
Raw Blame History

Mining Workflow

This guide walks through the sentence mining loop — from watching a video to creating Anki cards with audio, screenshots, and context.

Overview

SubMiner runs as a transparent overlay on top of mpv. As subtitles play, the overlay displays them as interactive text. You hover a word, trigger Yomitan lookup with your configured lookup key/modifier, then create an Anki card with a single action. SubMiner automatically attaches the sentence, audio clip, and screenshot.

Subtitle Delivery Path (Startup + Runtime)

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.
  3. Under rapid subtitle churn, SubMiner uses a latest-only tokenization queue so stale lines are dropped instead of building lag.
  4. MeCab, Yomitan extension load, and dictionary prewarm run as background warmups after overlay initialization (configurable via startupWarmups, including low-power mode).

This keeps early playback snappy and avoids mpv-side sluggishness while startup work completes.

Overlay Model

SubMiner uses one overlay window with modal surfaces.

Primary Subtitle Layer

The visible overlay renders subtitles as tokenized hoverable word spans. Each word is a separate element with reading and headword data attached. This plane is styled independently from mpv subtitles and supports:

  • Word-level hover targets for Yomitan lookup
  • Auto pause/resume on subtitle hover (enabled by default via subtitleStyle.autoPauseVideoOnHover)
  • Auto pause/resume while the Yomitan popup is open (enabled by default via subtitleStyle.autoPauseVideoOnYomitanPopup)
  • Right-click to pause/resume
  • Right-click + drag to reposition subtitles
  • Modal dialogs for Jimaku search, field grouping, subsync, and runtime options
  • Reading annotations — known words, N+1 targets, character-name matches, JLPT levels, and frequency hits can all be visually highlighted

Toggle visibility with Alt+Shift+O (global) or y-t (mpv plugin).

Secondary Subtitle Bar

The secondary subtitle bar is a compact top-strip region in the same overlay window for translation/context visibility while keeping primary reading flow below. It mirrors your configured secondary subtitle preference and can be independently shown or hidden.

It is controlled by secondarySub configuration and shares lifecycle with the main overlay window.

Modal Surfaces

Jimaku search, field-grouping, runtime options, and manual subsync open as modal surfaces on top of the same overlay window.

Looking Up Words

  1. Hover over the subtitle area — the overlay activates pointer events.
  2. Hover the word you want. SubMiner keeps per-token boundaries so Yomitan can target that token cleanly.
  3. Trigger Yomitan lookup with your configured lookup key/modifier (for example Shift if that is how your Yomitan profile is set up).
  4. Yomitan opens its lookup popup for the hovered token.
  5. From the popup, add the word to Anki.

Controller Workflow

With a gamepad connected and keyboard-only mode enabled, the full mining loop works without a mouse or keyboard:

  1. Navigate — push the left stick left/right to move the token highlight across subtitle words.
  2. Look up — press A to trigger Yomitan lookup on the highlighted word.
  3. Browse the popup — push the left stick up/down to smooth-scroll through the Yomitan popup, or use the right stick for larger jumps.
  4. Cycle audio — press R1 to move to the next dictionary audio entry, L1 to play the current one.
  5. Mine — press X to create an Anki card for the current sentence (same as Ctrl+S).
  6. Close — press B to dismiss the Yomitan popup and return to subtitle navigation.
  7. Pause/resume — press L3 (left stick click) to toggle mpv pause at any time.

The controller and keyboard can be used interchangeably — switching mid-session is seamless. Toggle keyboard-only mode on or off with Y on the controller.

See Usage — Controller Support for setup details and Configuration — Controller Support for the full mapping and tuning options.

Creating Anki Cards

There are three ways to create cards, depending on your workflow.

1. Auto-Update from Yomitan

This is the most common flow. Yomitan creates a card in Anki, and SubMiner enriches it automatically.

  1. Hover a word, then trigger Yomitan lookup → Yomitan popup appears.
  2. Click the Anki icon in Yomitan to add the word.
  3. SubMiner receives or detects the new card:
    • Proxy mode (ankiConnect.proxy.enabled: true): immediate enrich after successful addNote / addNotes.
    • Polling mode (default): detects via AnkiConnect polling (ankiConnect.pollingRate, default 3 seconds).
  4. SubMiner updates the card with:
    • Sentence: The current subtitle line.
    • Audio: Extracted from the video using the subtitle's start/end timing (plus configurable padding).
    • Image: A screenshot or animated clip from the current playback position.
    • Translation: From the secondary subtitle track, or generated via AI if configured.
    • MiscInfo: Metadata like filename and timestamp.

Configure which fields to fill in ankiConnect.fields. See Anki Integration for details.

2. Manual Update from Clipboard

If you prefer a hands-on approach (animecards-style), you can copy the current subtitle to the clipboard and then paste it onto the last-added Anki card:

  1. Add a word via Yomitan as usual.
  2. Press Ctrl/Cmd+C to copy the current subtitle line to the clipboard.
    • For multiple lines: press Ctrl/Cmd+Shift+C, then a digit 19 to select how many recent subtitle lines to combine. The combined text is copied to the clipboard.
  3. Press Ctrl/Cmd+V to update the last-added card with the clipboard contents plus audio, image, and translation — the same fields auto-update would fill.

Manual clipboard updates always replace generated sentence audio, even when ankiConnect.behavior.overwriteAudio is disabled. The word audio field is left unchanged because the word itself does not change in this flow.

This is useful when auto-update is disabled or when you want explicit control over which subtitle line gets attached to the card.

Shortcut Action Config key
Ctrl/Cmd+C Copy current subtitle shortcuts.copySubtitle
Ctrl/Cmd+Shift+C + digit Copy multiple recent lines shortcuts.copySubtitleMultiple
Ctrl/Cmd+V Update last card from clipboard shortcuts.updateLastCardFromClipboard

3. Mine Sentence (Hotkey)

Create a standalone sentence card without going through Yomitan:

  • Mine current sentence: Ctrl/Cmd+S (configurable via shortcuts.mineSentence)
  • Mine multiple lines: Ctrl/Cmd+Shift+S followed by a digit 19 to select how many recent subtitle lines to combine.

The sentence card uses the note type configured in isLapis.sentenceCardModel and always maps sentence/audio to Sentence and SentenceAudio.

::: warning Requires Lapis/Kiku note type Sentence card creation requires a Lapis or Kiku compatible note type and ankiConnect.isLapis.enabled: true in your config. See Anki Integration — Sentence Cards for setup. :::

4. Mark as Audio Card

After adding a word via Yomitan, press the audio card shortcut to overwrite the audio with a longer clip spanning the full subtitle timing.

::: warning Requires Lapis/Kiku note type Audio card marking requires a Lapis or Kiku compatible note type and ankiConnect.isLapis.enabled: true in your config. See Anki Integration — Sentence Cards for setup. :::

Secondary Subtitles

SubMiner can display a secondary subtitle track (typically English) alongside the primary Japanese subtitles. This is useful for:

  • Quick comprehension checks without leaving the mining flow.
  • Auto-populating the translation field on mined cards.

Display Modes

Cycle through modes with the configured shortcut:

  • Hidden: Secondary subtitle not shown.
  • Visible: Always displayed below the primary subtitle.
  • Hover: Only shown when you hover over the primary subtitle.

When a card is created, SubMiner uses the secondary subtitle text as the translation field value (unless AI translation is configured to override it).

Field Grouping (Kiku)

If you mine the same word from different sentences, SubMiner can merge the cards instead of creating duplicates. This feature is designed for use with Kiku and similar note types that support grouped fields.

How It Works

  1. You add a word via Yomitan.
  2. SubMiner detects the new card and checks if a card with the same expression already exists.
  3. If a duplicate is found:
    • Auto mode (fieldGrouping: "auto"): Merges automatically. Both sentences, audio clips, and images are combined into the existing card. The duplicate is optionally deleted.
    • Manual mode (fieldGrouping: "manual"): A modal appears showing both cards side by side. You choose which card to keep and preview the merged result before confirming.

See Anki Integration — Field Grouping for configuration options, merge behavior, and modal keyboard shortcuts.

SubMiner integrates with Jimaku to search and download subtitle files for anime directly from the overlay.

  1. Open the Jimaku modal via the configured shortcut (Ctrl+Shift+J by default).
  2. SubMiner auto-fills the search from the current video filename (title, season, episode).
  3. Browse matching entries and select a subtitle file to download.
  4. The subtitle is loaded into mpv as a new track.

Requires an internet connection. An API key (jimaku.apiKey) is optional but recommended for higher rate limits.

Texthooker

SubMiner runs a local HTTP server at http://127.0.0.1:5174 (configurable port) that serves a texthooker UI. This allows external tools — such as a browser-based Yomitan instance — to receive subtitle text in real time.

The texthooker page displays the current subtitle and updates as new lines arrive. This is useful if you prefer to do lookups in a browser rather than through the overlay's built-in Yomitan.

If you want to build your own browser client, websocket consumer, or automation relay, see WebSocket / Texthooker API & Integration.

Subtitle Sync (Subsync)

If your subtitle file is out of sync with the audio, SubMiner can resynchronize it using alass or ffsubsync.

  1. Open the subsync modal from the overlay.
  2. Select the sync engine (alass or ffsubsync).
  3. For alass, select a reference subtitle track from the video.
  4. SubMiner runs the sync and reloads the corrected subtitle.

Install the sync tools separately — see Troubleshooting if the tools are not found.

N+1 Word Highlighting

When enabled, SubMiner cross-references your Anki decks to highlight known words in the overlay, making true N+1 sentences (exactly one unknown word) easy to spot during immersion.

See Subtitle Annotations — N+1 for configuration options and color settings.

Immersion Tracking

SubMiner can log your watching and mining activity to a local SQLite database and expose it in the built-in stats dashboard — session times, words seen, cards mined, and daily/monthly rollups.

Enable it in your config:

"immersionTracking": {
  "enabled": true,
  "dbPath": ""  // leave empty to use the default location
}

Open the dashboard in the overlay with stats.toggleKey (default: `), launch it in a browser with subminer stats, keep a dedicated background server alive with subminer stats -b, stop that background server with subminer stats -s, or visit http://127.0.0.1:6969 directly if the local stats server is already running. The dashboard covers overview totals, anime progress, session detail, and vocabulary drill-down from the same local immersion database.

See Immersion Tracking for dashboard details, schema, and retention settings.

Next: Anki Integration — field mapping, media generation, and card enrichment configuration.