- Whisper subtitle generation now works for Jellyfin playback. It reads the item's original file through toJellyfinOriginalStreamUrl and writes to a reused cache folder. For transcoded playback it uses Jellyfin's audio index
- generateJapaneseSubtitles accepts http(s) streams when an output path is set; subprocess errors redact URL tokens
- Add widenHalfwidthKana/narrowFullwidthKana and widen subtitle text at ingest (mpv sub-text, parsed cues, live primary text), so halfwidth and fullwidth duplicates merge into one cue
- Stats queries widen older stored lines, sentence search matches katakana in either width, and stats mining widens sentence/word
- Update Jellyfin, subtitle generation, and architecture docs; add changelog fragments
* fix(subtitles): widen halfwidth katakana where subtitle text enters the app
Broadcast-derived Japanese subs write katakana halfwidth (アンタ, ダキニ), so
overlay lines mixed squashed and normal-width text. Normalize live mpv
sub-text and parsed cue text to fullwidth so display, lookups, mining,
and stats all see one form. The tokenizer reuses the shared helper.
* fix(subtitles): fold stray voiced marks and widen before cue dedup
- A lone halfwidth ゙ after fullwidth kana (ガ) now composes to ガ
instead of leaving a combining mark that broke name matching.
- Parsed cue text is widened before duplicate merging so ガク and ガク
on the same span collapse into one sidebar row.
- The YouTube track-load refresh widens the sub-text it reads directly.
* fix(stats): widen halfwidth katakana in stored lines and mined cards (#296)
Lines recorded before ingest widening still hold halfwidth katakana.
Occurrence and sentence-search queries widen line text on read, and the
mine-card route widens the sentence and word it receives, so the
dashboard and cards mined from history match newer rows. Word identity
columns are left as stored since the dashboard queries by them.
* fix(stats): match katakana in either width in sentence search
- Add narrowFullwidthKana, derived from widenHalfwidthKana so the two stay in sync
- Match subtitle text against the original, widened, and narrowed forms of each term so a search finds older lines stored as halfwidth
* feat(subtitles): generate Japanese subtitles during Jellyfin playback
- Read the item's original file via toJellyfinOriginalStreamUrl and write output to a temp dir
- Transcoded playback uses Jellyfin's audio index and only external references
- Show Jellyfin media by title and redact URL tokens in subprocess errors
- Update overlay copy, docs, and changelog fragment
* fix(subtitles): redact full stream tokens and reuse one Jellyfin output folder
* test(subtitles): make Jellyfin output path assertions platform-neutral
- Compare dirname/basename via node:path instead of POSIX-only regexes
- Assert the full sub-add command uses the generated output path
Drop 16 overlay preload methods with no caller (onSubtitleAss,
getCurrentSubtitleAss, onVisibility, getOverlayVisibility,
openYomitanSettings, quitApp, toggleOverlay, getMecabStatus,
setMecabEnabled, getKeybindings, saveControllerPreference,
getAnkiConnectStatus, setAnkiConnectEnabled, clearAnkiConnectHistory,
cycleRuntimeOption, appendClipboardVideoToQueue) along with their
IPC_CHANNELS keys, handlers, validators and dep plumbing.
With both ASS consumers gone, stop observing sub-text/ass in mpv and
remove the subtitle-ass-change event chain and appState.currentSubAssText.
The main-process functions these wrapped stay; tray, CLI, shortcuts and
session actions still use them.
* refactor: remove dead code and enable noUnusedLocals
- delete launcher/jimaku.ts and the launcher Args/config jimaku* fields
that only fed it (the SUBMINER_JIMAKU_* env vars had no effect)
- delete plugin/subminer/hover.lua; nothing has sent subminer-hover-token
since Overlay 2.0
- drop never-read anilist.characterDictionary.refreshTtlHours and
evictionPolicy (still hidden in the settings UI for old configs)
- drop unused root deps: eslint, @fontsource-variable/geist{,-mono}
(stats/ declares its own)
- delete the committed draft renderer-performance plan doc
- turn on noUnusedLocals for typecheck and remove what it surfaced,
including Windows focus-handoff values computed but never consumed
* docs(changes): add docs fragment and note retired hover messages
* test: add headless end-to-end harness and CI job
Boot the built app against a real mpv, a synthetic clip, a fixture Yomitan
dictionary, and a fake AnkiConnect inside a private Xvfb display, then assert
on the overlay DOM and the AnkiConnect requests. Adds the test:e2e lane, an
e2e session CLI for exploratory checks, and a three-platform CI job.
* ci: set e2e temp dir from a step
* test: make e2e session startup robust on slow machines
Bun caps node:test hooks at 5s, so boot the session inside the first test.
Keep pausing until the app's resume retries stop, read cue tokens in one
round trip, and resolve Electron through its package entry so CI downloads
the binary.
* test: harden e2e harness for CI runners
Fail fast when mpv cannot be spawned, read every Yomitan popup frame instead
of trusting target order, and put the chocolatey mpv directory on PATH.
* test: add e2e failure diagnostics and mark Windows job experimental
Report what the overlay showed when a cue never renders, dispose the session
when the overlay cannot be opened, skip popup frames that vanish mid-poll,
and turn off AniSkip lookups in the harness config.
* test: read the lookup popup entry from textContent
* test: capture renderer console and main-side subtitle state in e2e failures
* test: expose the Electron main-process inspector to the e2e harness
Adds session.mainProcess() and 'e2e eval main', and reports main's window
list alongside the overlay state when a cue never renders.
* test: tap subtitle sends and receipts in e2e failure diagnostics
* test: probe alternate main-to-renderer paths in e2e failure diagnostics
* test: select the visible overlay layer, not the primed modal window
Windows and macOS pre-create a hidden modal window that loads the same
renderer document, and the harness could attach to it instead of the
visible overlay. Drops the delivery taps that found this.
* ci: run the Windows e2e job as a normal matrix entry
* test: address review on e2e harness
Slice the app log at a byte offset before decoding, create the session state
file owner-only since it carries the inspector URL, and keep the cue failure
when inspector diagnostics are unavailable.
* test: create the e2e session state file exclusively
Every supported host now takes dictionary ZIPs over the link, so remove
the port 8780 upload, its hachidori.externalHostManagementUrl override and
the hachidori config section. A linked host without linked-import-v1 now
reports the update or manual-import fix.
Hachidori 0.2.3 accepts dictionary ZIPs as chunked upload requests: an
unlinked install imports them itself and a linked one forwards them to its
host. The parser bridge now imports through that path with replace, retrying
a busy commit with the same upload. Linked hosts without linked-import-v1
(hachidori-docker, older Hachidori) keep the management API upload.
- Rewrite polish prompt to default to inline items, nesting only for several distinct changes
- Add length/density example and drop internal details from release bodies
- Tell prerelease mode to shorten long baseline bullets
- Update changes/README.md and prompt assertions in tests
- Accept WHEEL_UP/DOWN/LEFT/RIGHT (with modifiers) in keybindings, the plugin, and the settings key editor
- Forward mpv's MBTN_* and WHEEL_* bindings from the overlay so double-click fullscreen, wheel volume, and similar inputs work on Hyprland; SubMiner bindings and right-click pause still win
- Notify the overlay via mpv-input-bindings:changed when mpv's key set changes, fixing dead imported keys when the overlay loads before mpv connects
- Sync README requirements/quick start with the installation guide and fix the Arch MeCab install command (AUR mecab-git)
- Install MeCab from the AUR (mecab-git) on Arch, since it is not in the official repos
- Rewrite the README feature blurbs, requirements table, and quick start so they match docs-site/installation.md
- Add fuse2, X11 window tools, whisper.cpp, and the thumbnail tools to the README requirements
- Drop the playlist browser and texthooker sections and the standalone launcher download steps
- Mention AniList as a guessit consumer in the installation guide
* fix(overlay): reload imported mpv keys after mpv connects
The overlay fetched mpv's input-bindings before SubMiner connected to mpv,
cached an empty list, and never refetched because the connect-time refresh
only broadcast session bindings when their compiled signature changed. Keys
from input.conf and mpv defaults (9/0 volume, m mute) then did nothing while
the overlay had focus, most often in mpv.backend x11 mode.
Main now emits mpv-input-bindings:changed whenever the discovered mpv key
set or client changes, and the renderer refreshes its imported keys on it.
* fix(overlay): subscribe to mpv key changes before initial discovery
The renderer registered the mpv-input-bindings:changed listener only after awaiting setupMpvInputForwarding and several other startup calls. If mpv connected in that window, the event was dropped and the imported mpv keys stayed empty.
- Stop stripping HYPRLAND_INSTANCE_SIGNATURE in applyX11EnvOverrides so the overlay can use hyprctl to lift Hyprland's fullscreen input block
- Fixes the overlay becoming unclickable over fullscreen mpv with mpv.backend: x11
- Update launcher and backend tests, add changelog fragment
- Pass --audio-format=float to the preview mpv on darwin so coreaudio accepts Opus output on Bluetooth devices like AirPods instead of falling back to avfoundation
- Fixes the stuttering preview cursor that jumped to the end partway through the selection
- Thread the platform into buildMediaTimingPreviewArgs and add a test covering darwin vs linux args
- Default character dictionary uploads to the linked host on port 8780; externalHostManagementUrl is now an optional override
- Warn when the override points at a different machine than the link
- Browser/app hosts report where to import the merged ZIP manually
- Name the management origin when the upload request cannot connect
- Update config descriptions, generated examples, docs, and changelog fragment
- Add `subminer youtube`/`yt` and tray "Browse YouTube" window that plays or queues picked videos in mpv
- Add `youtube.subtitleSource` (`youtube` | `whisper`) to transcribe YouTube audio via subtitle generation
- Allow the subtitle generation modal on YouTube videos
- Pick the real Japanese track over machine-translated ones and fall back to yt-dlp on HTTP 429
- Remove legacy launcher youtubeSubgen/whisper/AI args and `--youtube-mode`
- Fix EPIPE crash when the launching terminal is closed with Ctrl+C
- Thin media timing review handles with orange original-boundary bars
- Update docs and change fragments