Skip to content
Closed
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
ec48f90
feat(anime): add anime browser powered by Aniyomi extensions
ksyasuda Aug 1, 2026
a612d44
fix(anime): harden the extension bridge against untrusted repos and h…
ksyasuda Aug 1, 2026
c201520
fix(anime): honor the sidecar readiness deadline and confirm shutdown
ksyasuda Aug 1, 2026
7220f6f
feat(anime): add language filter to extensions available list
ksyasuda Aug 1, 2026
f8a8235
fix(anime): pin linux-x64 bridge checksum and fetch release by tag
ksyasuda Aug 1, 2026
7643855
fix(anime): strip disguised HLS segments and report real playback errors
ksyasuda Aug 1, 2026
ec9da6b
fix(anime): retry cold upstream fetches in the stream strip proxy
ksyasuda Aug 1, 2026
ec66f59
fix(anime): never forward Range to the bridge so 206 replies cannot s…
ksyasuda Aug 1, 2026
4739c3d
fix(anime): restart the bridge on the next request after it dies
ksyasuda Aug 1, 2026
891c4a1
fix(overlay): fall back to did-finish-load when ready-to-show never f…
ksyasuda Aug 1, 2026
2e54a3a
style(animeui): tighten source picker and cue rail layout
ksyasuda Aug 1, 2026
2fbf27f
fix(anime): report a failed bridge kill instead of a generic timeout
ksyasuda Aug 1, 2026
d1d128b
feat(anime): cache stream subtitles to disk for the alass reference
ksyasuda Aug 1, 2026
feb8d5a
fix(anime): cap outcome polling and upstream stalls by wall clock
ksyasuda Aug 1, 2026
4abbe8d
fix(subsync): sync subtitles from stream URLs, auto-discover tool paths
ksyasuda Aug 2, 2026
035ff8e
fix(subsync): resolve ffmpeg only when a track needs extraction
ksyasuda Aug 2, 2026
c948305
fix(anime): thread episode metadata through stream playback and fix seas
ksyasuda Aug 2, 2026
f5369b8
docs: catch up docs for anilist, anime browser, and subsync fixes
ksyasuda Aug 2, 2026
289c74d
fix(anime): scope preferences, paginate sources, harden installs
ksyasuda Aug 2, 2026
8aff2ae
fix(anime): guard playback races and harden install/extract tests
ksyasuda Aug 2, 2026
3dd0750
fix(anime): stop superseded playEpisode calls from touching mpv
ksyasuda Aug 2, 2026
8d55d64
feat(anime): filter episodes and mark them watched by hand
ksyasuda Aug 3, 2026
b22001f
fix(anime): isolate mark-watched requests and batch marks atomically
ksyasuda Aug 3, 2026
8c22567
feat(anime): show extension and repo icons in extensions panel
ksyasuda Aug 7, 2026
51fc903
fix(subsync): decode file:// tracks mpv reports for dropped subtitles
ksyasuda Aug 7, 2026
3ca7dcd
feat(anime): queue episodes to play next across anime
ksyasuda Aug 7, 2026
183560d
fix(macos): bring app forward when opening settings/sync/anime windows
ksyasuda Aug 11, 2026
935e4c1
feat(anime): append queued episodes to mpv playlist
ksyasuda Aug 15, 2026
333ee5e
fix(anime): clean up discarded queued subtitles in background
ksyasuda Aug 15, 2026
cc43d30
fix(anime): alias disguised HLS segments for ffmpeg
ksyasuda Aug 15, 2026
7a1450d
feat(anime): open Anime Browser in player modal
ksyasuda Aug 16, 2026
877f350
fix(anime): keep browser state in sync and alias HLS segments
ksyasuda Aug 16, 2026
4300517
fix(anime): harden browser playback and modal lifecycle
ksyasuda Aug 16, 2026
f6993ae
fix(anime): harden stream proxy and session lifecycle
ksyasuda Aug 16, 2026
1d1575f
fix(anime): preserve HLS playlist rewriting and fetch timeouts
ksyasuda Aug 16, 2026
2bcb6c7
feat(anime): support shared bridge installs and updates
ksyasuda Sep 2, 2026
835a09f
fix(anime): harden bridge updates and startup lifecycle
ksyasuda Sep 2, 2026
9a1b6e6
fix(anime): restore bridge install after validation failure
ksyasuda Sep 2, 2026
b01f0bc
feat(release): prepare v0.19.5 with subtitle pipeline improvements
ksyasuda Sep 2, 2026
0d66747
fix(immersion): await lexical rollup shutdown during app quit
ksyasuda Sep 2, 2026
484a9e0
fix(immersion): drain writes after finalizing tracker shutdown
ksyasuda Sep 2, 2026
18beac1
feat(anime): add version-aware extension updates
ksyasuda Sep 3, 2026
516fadd
feat(anime): auto-open Jimaku for Anime Browser playback
ksyasuda Sep 3, 2026
68dd789
feat(anime): remember the default Anime Browser source
ksyasuda Sep 3, 2026
7bc9a07
fix(anime): rescan runtime after preference cleanup failures
ksyasuda Sep 3, 2026
89479f7
feat(anime): set default source from Extensions tab
ksyasuda Sep 3, 2026
cd7a65e
fix(anime): wait for mpv window before opening Jimaku
ksyasuda Sep 3, 2026
01fcacb
test(anime): synchronize stale Jimaku open test
ksyasuda Sep 3, 2026
93fb4ef
feat: add subtitle generation and bundle Bun launcher runtime
ksyasuda Sep 16, 2026
ca40cd6
fix(anime): clarify bridge failures and update guidance
ksyasuda Sep 17, 2026
d1aa279
feat: expand subtitle and media tracking workflows
ksyasuda Sep 21, 2026
625f1ad
feat(stats): add live-action library support and harden stats runtime
ksyasuda Sep 21, 2026
2c89bc1
feat(subtitles): generate subtitles from anime streams
ksyasuda Sep 22, 2026
4209134
feat(subtitles): optimize anime stream audio extraction
ksyasuda Sep 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 27 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ Browse sibling episode files and the active mpv queue in one overlay modal. Open
<td><b>Jellyfin</b></td>
<td>Browse, launch, and cast media from your Jellyfin server with setup and discovery controls in the app tray</td>
</tr>
<tr>
<td><b>Anime Browser</b></td>
<td>Search anime sources you supply as <a href="https://github.com/aniyomiorg/aniyomi">Aniyomi</a> extension APKs and play an episode in mpv with the overlay attached (<code>subminer anime</code>); SubMiner ships no repositories and bundles no sources</td>
</tr>
<tr>
<td><b>Jimaku</b></td>
<td>Search and download Japanese subtitles</td>
Expand Down Expand Up @@ -178,6 +182,8 @@ See the [full requirements list](https://docs.subminer.moe/installation#_1-insta

```bash
paru -S subminer-bin
# optional: the anime browser bridge, shared with Mangatan and updated by pacman
paru -S mangatan-extension-server
```

</details>
Expand Down Expand Up @@ -256,19 +262,28 @@ Full guides on configuration, Anki setup, Jellyfin, immersion tracking, and more

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

| Project | Role |
| ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| [ani-skip](https://github.com/synacktraa/ani-skip) | AniSkip API client for anime intro/outro skip timestamps |
| [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script) | Inspiration for the mining workflow |
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
| [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui) | Base for the WebSocket texthooker integration |
| [Yomitan](https://github.com/yomidevs/yomitan) | Dictionary engine powering all lookups and the morphological parser |
| [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) | JLPT level tags for vocabulary |
| Project | Role |
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [ani-skip](https://github.com/synacktraa/ani-skip) | AniSkip API client for anime intro/outro skip timestamps |
| [Anacreon-Script](https://github.com/friedrich-de/Anacreon-Script) | Inspiration for the mining workflow |
| [Aniyomi](https://github.com/aniyomiorg/aniyomi) | Anime extension API and data model the anime browser targets |
| [asbplayer](https://github.com/killergerbah/asbplayer) | Inspiration for subtitle sidebar and logic for YouTube subtitle parsing |
| [Bee's Character Dictionary](https://github.com/bee-san/Japanese_Character_Name_Dictionary) | Character name recognition in subtitles |
| [GameSentenceMiner](https://github.com/bpwhelan/GameSentenceMiner) | Inspiration for Electron overlay with Yomitan integration |
| [jellyfin-mpv-shim](https://github.com/jellyfin/jellyfin-mpv-shim) | Jellyfin integration |
| [Jimaku.cc](https://jimaku.cc) | Japanese subtitle search and downloads |
| [M-Extension-Server](https://github.com/1Selxo/M-Extension-Server) | Runs Aniyomi extension APKs off Android; the bridge the anime browser drives |
| [Mangatan](https://github.com/1Selxo/Mangatan) | Reference client for the bridge protocol the anime browser speaks |
| [Renji's Texthooker Page](https://github.com/Renji-XD/texthooker-ui) | Base for the WebSocket texthooker integration |
| [Yomitan](https://github.com/yomidevs/yomitan) | Dictionary engine powering all lookups and the morphological parser |
| [yomitan-jlpt-vocab](https://github.com/stephenmk/yomitan-jlpt-vocab) | JLPT level tags for vocabulary |

## License

[GNU General Public License v3.0](LICENSE)

The anime browser drives [M-Extension-Server](https://github.com/1Selxo/M-Extension-Server),
downloaded from its upstream releases at runtime rather than
bundled or redistributed here; its bundles carry their own dependencies, including a JRE and
GPL-3.0 NewPipe Extractor. SubMiner includes none of them and talks to the bridge
over its own HTTP protocol. It ships no extensions or repositories by default.
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions changes/anilist-season-resolution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type: fixed
area: anilist

- Resolving "season N" against AniList no longer lands one season short for franchises whose broadcast seasons are listed as several entries. AniList records the back half of a split cour ("… Season 2 Part 2", "… Cour 2", "第2クール") as its own sequel, and the resolver counted each as a season — so Mushoku Tensei season 3 resolved to the season 2 entry and Re:ZERO season 4 to season 3, which then drove the character dictionary and AniList progress updates to the wrong series. A sequel carrying a part or cour marker that matches the season it continues is now followed without advancing the season count, in both the relation walk and the air-order fallback. Titles are compared ignoring punctuation, and the marker is looked for in every title and synonym rather than just the display title. A wrong match cached before this fix is remembered in `character-dictionaries/anilist-resolution-cache.json` and needs removing (or overriding from the character dictionary picker) for the affected series.
- A special or OVA sitting in a franchise's sequel chain is walked through without counting as a season. Dr. STONE links STONE WARS to New World through a one-episode special, which made New World resolve as season 4 and every later season shift with it.
- An AniList rate limit or network failure while walking sequel relations is reported so the caller can retry, instead of falling through to the air-date fallback. That fallback is for a franchise with missing relation edges; running it after a failed lookup turned a transient 429 into a confidently wrong season.
- The shared title parser recognizes spelled-out episode labels (`Episode 4`, `第4話`) and a season named at the end of the title (`… Season 3`, `… 3rd Season`, `… S3`), which now fill the Season field instead of being searched for as part of the series name. The TsukiHime modal gained a Season field to match Jimaku, and seasons after the first are included in its search query so a later season's releases are actually found.
33 changes: 33 additions & 0 deletions changes/anime-browser.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
type: added
area: anime

- Added an anime browser window that searches Aniyomi extension sources, shows cover art and episode lists, and plays an episode in mpv so the overlay and mining tools attach as usual.
- Added `subminer anime` and the `--anime` flag to open the browser, plus a "Browse Anime" tray entry.
- Anime extensions are read from `<userData>/anime-extensions`; drop Aniyomi `.apk` files there to add sources.
- Added a source settings tab so extensions that need configuration (server address, credentials, quality) can be set up from the browser; values persist per extension and source, and updated extension schemas replace stale saved field definitions without losing values. Older unscoped preferences are discarded once because their package ownership cannot be proven safely.
- Added an Extensions tab for adding repository URLs and installing, updating, or removing extensions in place; extensions that fail to load are listed with the reason. Repository requests time out instead of hanging, and APK updates are staged before replacing the installed copy.
- Browse, Extensions, and Source settings are tabs, so each one gets the full window instead of sharing it with the search results.
- Repository URLs only need to be an https URL to a `.json` index; the file name is not restricted to `index.min.json`.
- The source picker offers "All sources", which searches every installed source at once. Results stream in as each source answers, with per-source progress in the status bar. Results are tagged with their source, failures do not blank the grid, and **Load more** appends later pages without duplicating streamed entries.
- The Extensions tab opens with an Installed section listing every extension on disk with Remove — including ones added by hand or whose repository has since been removed — and Update where a configured repository still carries it.
- Added `anime.repos`, `anime.extensionsDir`, and `anime.preferredQuality` config keys. SubMiner ships no extension repositories and performs no discovery.
- Anime playback targets Japanese audio: dub-labelled entries are skipped when the source offers an alternative, `alang` prefers Japanese, and the source's own audio and subtitle tracks are loaded into mpv (Japanese selected) instead of being discarded, so all of them can be switched from mpv's track menu.
- The primary subtitle slot stays reserved for Japanese: a source that only has, say, English subtitles gets them added with a normalized language tag (`English` → `en`) but not selected, so the regular `secondarySub` auto-load can route them to the secondary slot instead.
- HLS streams pass through a local strip proxy that removes fake image headers some hosts glue onto their video segments and gives disguised segment URLs (`.image`, `.jpg`, `.css`, and other rotating fake extensions) a media-safe local alias, so those streams play in mpv and support Anki audio and image extraction with current ffmpeg releases. Upstream responses use identity encoding so playlists remain readable for URL rewriting.
- The strip proxy retries a failed segment fetch once after a short pause and logs upstream error statuses; a host that errors on the very first fetches right after an episode resolves no longer kills the whole playback, disconnecting clients release their active upstream fetch instead of consuming the socket and bandwidth in the background, stalled response bodies retain the upstream inactivity timeout, and only origin-form requests can reach the configured local bridge.
- The strip proxy no longer forwards `Range` headers to the bridge: ffmpeg opens every HLS segment with `Range: bytes=0-`, the bridge answers some of those with 206, and a partial response bypassed the disguise strip, so whether an episode played depended on the bridge's cache state.
- A bridge that dies out from under the app (killed, crashed, or stopped mid-operation) no longer leaves the browser failing every request until an app restart: the exit is detected, surfaced in the status bar, and the bridge restarts on the next request.
- "Playing" is only reported once mpv actually configures a video output; when a stream fails to decode, the browser shows mpv's error instead of claiming playback started while no window ever appeared.
- The bridge bundle is downloaded from the newest upstream release that ships a bundle for the platform (skipping releases older than the oldest server SubMiner is known to work with), the same way Mangatan fetches it, so all four published bundles (macOS arm64 and x64, Linux x64, Windows x64) work without a maintainer hashing each one first.
- The Extensions tab's available list has a language chip row: pick one or more languages to narrow a repository index that otherwise lists every language it knows, or "All" to clear the filter. Rows name the language ("Japanese" instead of `ja`) and the Available heading counts what the filter leaves.
- A stream's subtitle tracks are downloaded to a temp directory and loaded into mpv as files rather than streamed from the source URL, so they can serve as the alass reference in Subsync the way Jellyfin subtitles do. The format is detected from the file's own content, a track that fails to download falls back to its URL so the episode still plays, and the directory is removed when the next episode starts or the app exits.
- An episode launched from the browser carries its series, season and episode number through the app instead of a single joined string: stats group by series (every stream previously landed in one entry named `m3u8`), rewatching reuses the existing entry, the Jimaku and TsukiHime modals prefill Title/Season/Episode from the source's own listing, AniList updates use those fields directly, and the mpv title reads `Series S03E04 - Episode Name`.
- Opening the browser shows a tray icon on every platform and — on macOS — puts the app in the Cmd+Tab switcher and Dock, so you can switch between it and mpv; the Dock icon is released again when the window closes during playback. Launching a video starts a regular SubMiner session, and in `subminer anime` standalone mode closing the window during playback no longer quits the app and kills the stream.
- The episode list has a filter box: type an episode number (`12`), a range (`12-18`), or part of an episode name to narrow a long list, with a `6 of 25` counter while it is applied. Escape inside the box clears the filter instead of leaving the detail page.
- Episodes already watched are dimmed and marked in the episode list, with a watched count in the header. The marks come from the stats history playback already writes (an episode is marked once a session passes the completion threshold), so they match the stats window and survive the stream URL changing between playbacks. They refresh when the browser window comes back to the front, and stay empty when immersion tracking is disabled.
- Right-clicking an episode opens a menu for marking it watched or unwatched by hand, plus "Mark this and N below watched/unwatched" for the episode and every episode listed under it. Sources list newest first, so a span covers the back catalogue, which is how a series watched elsewhere gets caught up. A filter never narrows what a span covers, and the status bar reports how many episodes were touched.
- Marking an episode that was never played creates its stats row, carrying the same series, season and episode fields playback would have recorded. Both stats library views join the lifetime tables, so a manual mark does not show up there as watch time nobody spent, and clearing a mark creates nothing.
- Episodes can be queued instead of replacing what is playing. Every episode row has **Play** and **Queue** buttons (clicking the row still plays now), the right-click menu offers the same two, and a queued episode shows its place in line ("next up", "#2 in queue") with a queue count and **Clear queue** in the episode header. The queue spans anime, resolves and appends each episode to mpv's real playlist as soon as it is queued while subtitle tracks cache in the background, so next/previous navigation works immediately and the next episode starts without a resolution pause when the current one ends. Queueing with nothing playing just plays.
- Added an in-player Anime Browser modal on `Ctrl+Alt+A`. The shortcut toggles it without losing its page or scroll position. It stays within the player bounds and shares the active episode, playback queue, source configuration, and watch history with the standalone browser, while each surface keeps independent search and navigation state. The modal validates its embedded page before changing overlay state, so a load setup failure leaves it closed rather than revealing a broken modal.
- The bridge is reused from a package-manager install when one exists: on Arch the AUR `mangatan-extension-server` package (shared with Mangatan) is picked up from `/usr/share/mangatan/extension_server`, so nothing is downloaded and pacman keeps it current. `anime.bridgeDir` points SubMiner at a bundle anywhere else. `subminer-bin` lists the package as an optional dependency.
- SubMiner records which bridge release it installed and, once the bridge is running, checks GitHub for a newer one; when there is, the banner offers an **Update to vX** button. The new release is downloaded beside the running bridge, then the bridge restarts on it. The updater waits for a bridge still starting to stop and keeps the previous bundle until the replacement is active. The Extensions tab shows the bridge version, where it lives, and who updates it without describing an unchecked install as current.
4 changes: 4 additions & 0 deletions changes/linux-overlay-content-ready-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type: fixed
area: overlay

- Fixed the overlay never loading (stuck on the "Overlay loading" OSD spinner) when the Yomitan content-script reload raced overlay window creation at startup, most visible when playing from the anime browser on Linux/Wayland: a hidden window stops painting after that reload, so the ready-to-show signal that gates showing the overlay never fired. Content-ready now falls back to did-finish-load after a short grace period.
4 changes: 4 additions & 0 deletions changes/macos-window-activation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
type: fixed
area: macos

- `subminer anime` (and `--settings` / `--sync`) now bring their window to the front on macOS. `show()`/`focus()` only reorder windows inside the app that is already active, so the window opened behind the terminal that launched it; SubMiner now activates itself when opening one. The anime browser also restores its Dock icon before showing rather than after, because the overlay's fullscreen transform leaves the app as an accessory process that macOS refuses to bring forward at all.
Loading
Loading