Commit Graph

395 Commits

Author SHA1 Message Date
sudacode 336e9fb8a5 fix(anime): cap outcome polling and upstream stalls by wall clock
- watchPlaybackOutcome now tracks a real deadline so slow property reads eat the timeout budget instead of extending it, and a zero probe interval still terminates
- stream strip proxy aborts upstream GETs that go silent for 15s instead of hanging mpv on that segment forever
- anime browser runtime drops a stream proxy that finishes starting after its bridge already died, instead of leaking a listener pointed at nothing
2026-08-01 03:13:48 -07:00
sudacode dec7851a17 feat(anime): cache stream subtitles to disk for the alass reference
- Download a stream's subtitle tracks to a temp dir and load them into mpv as files instead of streaming from the source URL, so Subsync can use them as the alass reference (mirrors the Jellyfin subtitle preload)
- Detect format from content first, falling back to the URL extension
- A track that fails to download falls back to its URL so the episode still plays
- Remove the temp dir when the next episode loads or the runtime disposes
2026-08-01 02:32:25 -07:00
sudacode 4eaaf5769f fix(anime): report a failed bridge kill instead of a generic timeout
- Track kill errors separately from exit/spawn so a signal failure (e.g. EPERM) surfaces its own message instead of a bare "did not exit after SIGKILL"
- Attach stop() failures as the cause on the readiness-timeout error so a surviving child's kill failure isn't dropped
- Skip stop() entirely when spawn itself failed, and stop polling once the ready deadline has passed
- Add stopTimeoutMs test hook and a test covering a kill that fails without the child exiting
2026-08-01 01:50:19 -07:00
sudacode 0dfba67ae8 style(animeui): tighten source picker and cue rail layout
- Lay source picker label + input inline instead of stacked
- Let detail cover keep natural aspect ratio instead of forced 2:3 crop
- Widen cue rail gutter to match new cue number column width
2026-08-01 01:38:54 -07:00
sudacode 18e0200934 fix(overlay): fall back to did-finish-load when ready-to-show never fires
A hidden overlay window stops producing frames once it is explicitly
hidden and then reloaded (the Yomitan content-script reload right after
startup), so ready-to-show never fires and the content-ready gate keeps
the window hidden forever behind the Overlay loading spinner. Mark
content ready from did-finish-load after a 1.5s grace period so the
overlay always becomes showable; ready-to-show still wins when it fires.
2026-08-01 01:31:48 -07:00
sudacode d7188a2155 fix(anime): restart the bridge on the next request after it dies
The reported 'APK crash' turned out not to exist: repeated full scans of
all installed extensions load cleanly. The real incident was the app's
quit sequence stopping the bridge (its /stop endpoint) while requests
were in flight - and it exposed that a bridge dying for any reason left
the anime browser failing every call with a raw 'fetch failed' until
the whole app was restarted.

SidecarHandle now reports the child's exit (including to late
subscribers), and the runtime detects a death it did not order: it
drops the dead handle, closes the strip proxy, tells the UI, and the
next request transparently starts a fresh bridge. requireBridge became
an async bridge() that starts on demand, which also covers first use.

Verified live: search -> SIGKILL the JVM -> search recovers.
2026-08-01 01:05:40 -07:00
sudacode 1c50ff74cb fix(anime): never forward Range to the bridge so 206 replies cannot skip the strip
ffmpeg opens every HLS segment with 'Range: bytes=0-'. The bridge
answers some of those 206 (depending on its cache state), and the proxy
only rewrites full 200 bodies, so the PNG disguise passed through
untouched and whether an episode played was a coin flip: first attempts
died with "no audio or video data played", the same URL played fine
once the bridge had the segments cached.

Reproduced end to end against a private bridge + mpv driven with the
app's exact commands; with Range dropped every segment is stripped and
playback runs.
2026-08-01 00:40:34 -07:00
sudacode c2781a3f80 fix(anime): retry cold upstream fetches in the stream strip proxy
Right after an episode resolves, the bridge (or the host behind it) can
error on the very first segment fetches and be fine a moment later; mpv
sweeps the playlist of failing segments in ~2s and gives up with "no
audio or video data played" while a manual reload of the same URL plays.
Retry a failed GET once after 400ms and log upstream error statuses so
the next failure names the real cause in the app log.
2026-08-01 00:24:26 -07:00
sudacode ff25e5cafa fix(anime): strip disguised HLS segments and report real playback errors
Some hosts prepend a fake image header (a real 1x1 PNG) to every HLS
segment; ffmpeg probes the segment as a picture and mpv drops back to
idle with no window while the browser claims the episode is playing.

- Route bridge-served m3u8 streams through a local strip proxy that
  scans each segment for the first genuine MPEG-TS packet run and drops
  the junk in front of it; playlists get absolute origins rewritten so
  segment requests come back through the proxy. Non-TS bodies pass
  through untouched.
- Only report ok from playEpisode once mpv configures a video output;
  an end-file with reason error surfaces mpv's own message (e.g. "no
  audio or video data played") in the browser status bar instead of
  "Playing". New end-file event plumbed through the mpv IPC client.
2026-08-01 00:05:31 -07:00
sudacode 7254db66ad fix(anime): pin linux-x64 bridge checksum and fetch release by tag
- Add PINNED_BUNDLE_SHA256 entry for linux-x64-bundle.zip so Linux no longer refuses to start with "No pinned checksum"
- Fetch the bridge release by its pinned tag (releases/tags/<tag>) instead of listing newest releases, so an upstream publish can't swap in an unverified asset
- Update docs and changelog to describe the pin/tag behavior
2026-07-31 23:41:52 -07:00
sudacode 3c19228b35 feat(anime): add language filter to extensions available list
- Add a language chip row above the Available list; pick one or more languages to narrow the repo index, or "All" to clear
- Show language display names (e.g. "Japanese") instead of raw tags, and an "N of M" count on the Available heading
- Add language-filter.ts with collectLanguages/toggleLanguage/pruneSelection/filterByLanguage/languageLabel, plus tests
2026-07-31 23:41:14 -07:00
sudacode 2e34f01adf fix(anime): honor the sidecar readiness deadline and confirm shutdown
The capabilities probe used a fixed 5s timeout, so a readiness budget
shorter than that could be overrun by one stalled request. Pass the
remaining deadline down instead.

stop() also resolved after the SIGKILL wait even when the child had not
exited, letting a restart race a process still holding the port. Throw
in that case, and keep a failed shutdown from masking the readiness
error at startup.
2026-07-31 18:07:32 -07:00
sudacode f5e98dfa9d fix(anime): harden the extension bridge against untrusted repos and hangs
Addresses CodeRabbit review feedback on the Anime Browser:

- reject repository package/apk names that are not plain identifiers, and
  verify the install target resolves inside the extensions directory
- count mpv's %n% option escape in UTF-8 bytes, and escape backslashes in
  header values so a trailing one cannot eat the list separator
- key the bridge extension-id cache by APK content, so an in-place upgrade
  re-uploads instead of running the previous build
- bound every bridge, release-listing, and download request with a timeout
- enforce the APK size limit while streaming rather than after buffering
- read APK bytes on demand instead of holding a base64 copy per extension
  for the lifetime of the browser
- serialize preference mutations and write the file atomically
- handle the sidecar spawn error event, and wait for the child to exit in
  stop() before returning
- report a failed Anime Browser bootstrap instead of showing the starting
  banner forever
- keep the preferences panel's save confirmation and in-flight multi-select
  edits by re-rendering only on a structural schema change
2026-07-31 17:57:01 -07:00
sudacode e64ff1a0ee feat(anime): add anime browser powered by Aniyomi extensions
- Add `subminer anime` / `--anime` and a tray entry to open a browser that searches installed Aniyomi extension sources, shows cover art and episodes, and plays into mpv with overlay/mining attached
- Add an Extensions tab to add repos and install/update/remove sources, and per-source settings for sources needing config
- Support searching all sources at once with streaming, per-source results and status
- Prefer Japanese audio/subtitle tracks from the source and keep the primary subtitle slot reserved for Japanese
- Fix window/tray/Dock handling so the browser and mpv can be switched between without quitting the app or losing the Dock icon
- Add anime.repos, anime.extensionsDir, anime.preferredQuality config keys (no bundled repos or discovery)
2026-07-31 17:21:12 -07:00
sudacode b204d4dd6e feat(anki): add configurable word card type for Kiku/Lapis (#175) 2026-07-31 17:17:29 -07:00
sudacode 89ed675935 fix(overlay): keep Yomitan popup interactive on macOS/Windows (#177) 2026-07-30 19:47:08 -07:00
sudacode 1995200e76 chore(release): prepare v0.19.0 v0.19.0 2026-07-29 22:26:03 -07:00
sudacode e876e483d6 chore(release): prepare v0.19.0-beta.5 v0.19.0-beta.5 2026-07-29 02:09:01 -07:00
sudacode 6d2a72e13b feat(stats): add library entry deletion and app-wide delete progress (#174) 2026-07-29 02:00:16 -07:00
sudacode 0d7084c8aa fix(anilist): resolve later seasons via sequel relations, not title guessing (#173) 2026-07-28 22:56:26 -07:00
sudacode 95e0abc7b7 test(immersion): apply runtime SQLite pragmas in query tests
The query test suite opened each temp database with SQLite's defaults
(rollback journal, synchronous=FULL) while the runtime opens them via
applyPragmas (WAL, synchronous=NORMAL). Every statement therefore ran as
its own fsync-ing transaction.

That is unnoticeable on a fast local disk but dominates on CI: the file
took 40.6s there versus 0.5s locally, and the 106-insert case
'getVocabularyStats pages past hidden rows' crossed the 5s per-test
timeout and failed the quality gate.

Open test databases through a helper that applies the same pragmas as
production, so the tests exercise the runtime's actual SQLite
configuration instead of a slower one.
2026-07-28 02:23:16 -07:00
sudacode d9155ceacb docs: update requirements table and Windows install instructions
- Add TsukiHime and AniSkip to feature table; note xz dependency
- Clarify Anki+AnkiConnect is required to mine, not to run
- Rework Windows setup with winget/scoop commands and PATH steps
- Note ffmpeg has no path-override setting, unlike mpv
- Fix screenshot alt text and installation.md anchor link
2026-07-28 02:10:50 -07:00
sudacode 9d0585423c chore(release): prepare v0.19.0-beta.4 v0.19.0-beta.4 2026-07-28 01:09:48 -07:00
sudacode 455cfff90a docs: fix inaccuracies and add link/anchor validation tests
- Generate config.example.jsonc with a Linux socket path instead of Windows, so the example stays reproducible across platforms
- Document TsukiHime config (`tsukihime.*`), maturity-based known-word highlighting keys, and other config surfaces missing from the reference
- Correct secondary-subtitle auto-load defaults and Anki field-matching (case-insensitive) claims
- Expand Windows installation guide with winget/Scoop package tables and manual PATH setup
- Add docs-site/links.test.ts to catch broken internal links and heading-anchor mismatches
- Fix stale cross-references in jimaku-integration.md and demos.md
2026-07-28 00:56:38 -07:00
sudacode 987b325edb Anki maturity-based known-word highlighting (#172) 2026-07-27 23:58:02 -07:00
sudacode 08c6807cb1 fix(launcher): normalize rofi prompt spacing
- Add formatRofiPrompt to trim trailing whitespace and append a single space, keeping the prompt from running into the input field
- Apply it across all rofi -p usages in picker.ts and history-command.ts
- Add tests for formatRofiPrompt edge cases (empty/whitespace-only prompts)
2026-07-26 00:13:54 -07:00
sudacode 18c6410f24 feat(launcher): add previous episode option to history entry menu
- Extract buildHistoryEntryActions to build the series action menu, now including a previous-episode option alongside replay/next/browse/quit
- Update docs and changelog entry to describe the new option
- Add tests covering previous/replay/next ordering and omission when last watched file is missing
2026-07-25 23:35:44 -07:00
sudacode e223cf9b71 fix(release): restore compatible js-yaml override v0.19.0-beta.3 2026-07-22 23:51:21 -07:00
sudacode c718bb8b34 chore(release): prepare 0.19.0-beta.2 v0.19.0-beta.2 2026-07-22 23:34:08 -07:00
sudacode 177f7e5fd6 docs(readme): remove TsukiHime credit
- Drop the TsukiHime row from the open-source credits table
2026-07-22 23:07:07 -07:00
sudacode 20b50ec4e1 feat(launcher): add post-playback history menu with previous episode (#170) 2026-07-22 23:05:53 -07:00
sudacode deae61b211 refactor: split anki-connect and stats-server resolvers into modules (#169) 2026-07-17 23:05:59 -07:00
sudacode 44959ed282 fix(anki,appimage): handle proxy port conflicts and stray GPU child
- AnkiConnect proxy now detects EADDRINUSE, logs a warning, and surfaces an overlay notification instead of crashing video startup
- Background AppImage bootstrap runs Chromium with in-process-gpu so no GPU child survives app.exit() and outlives the FUSE mount, fixing the remaining DrKonqi "Service Crash" case
- Add changelog fragment for the proxy fix; update the AppImage quit fragment with the GPU child root cause
2026-07-17 23:04:56 -07:00
sudacode 2398f5c030 refactor(main): eliminate local pass-through wrappers in main.ts (#168) 2026-07-15 03:22:03 -07:00
sudacode 8711cf1a48 remove redundant isVerbNonIndependent and consolidate test lanes
- drop always-true isVerbNonIndependent export; collapse shouldMerge branch
- remove node-compat exclusion list from bun-src-full lane
- simplify test:fast (drop test:runtime:compat), test:env, test:smoke:dist scripts
- rename test:launcher:env:src -> test:env in docs
2026-07-14 19:57:50 -07:00
sudacode 9f4888293b chore(release): prepare 0.19.0-beta.1 v0.19.0-beta.1 2026-07-13 23:47:50 -07:00
sudacode 29332b103e Consolidate sync changelog fragments into one entry
- Merge the five separate sync change fragments (launcher command, UI window, close/quit fixes, active-session double-count, monthly rollup timezone) into a single changes/sync.md
- Fold kana-pos-content-classification.md into annotation-noise-rule-table.md
- Drop the now-stale Unreleased sections from CHANGELOG.md and docs-site/changelog.md
- Tweak tsukihime-english-subtitles.md wording
2026-07-13 23:40:28 -07:00
sudacode 8b2cee2c58 ci: reuse quality gate across CI and release workflows (#167) 2026-07-13 23:14:19 -07:00
sudacode 2e2ee3f028 refactor(tsukihime): swap Animetosho backend for TsukiHime API (#165) 2026-07-13 22:03:35 -07:00
sudacode 49b926e08c Sync Stats & History window, headless --sync-cli, and Windows remote support (#160) 2026-07-13 18:56:51 -07:00
sudacode 66f8ca4f80 refactor(renderer): extract modal registry for open/dismiss/active queries (#166) 2026-07-13 18:16:39 -07:00
sudacode 6fe1e0fee4 refactor(stats): drop dead IPC handlers, unify stats types over HTTP (#164) 2026-07-13 12:09:56 -07:00
sudacode 0ab840b362 refactor(tokenizer): extract subtitle annotation filter into rule table (#162) 2026-07-12 23:54:13 -07:00
sudacode 21a06c12fb refactor(tokenizer): consolidate kana/POS helpers into token-classification (#161) 2026-07-12 23:27:25 -07:00
sudacode 14070acceb fix(overlay): defer autoplay release for untokenized startup priming
- Autoplay-ready gate now ignores untokenized subtitle payloads while tokenization warmup is pending, so playback doesn't resume before the first tokenized cue
- Wire isTokenizationReady dep through main.ts and update gate/wiring tests accordingly
- Drop stale release/release-notes.md, add changelog entry under changes/
2026-07-12 02:11:10 -07:00
sudacode 7d81342f0f fix(app): keep AppImage mount alive until Chromium children finish shutdown
Background AppImage launches executed directly from a FUSE mount owned by a
short-lived helper's AppImage runtime. On quit, the runtime unmounted the
squashfs while utility children (network service) were still mid-shutdown,
SIGBUSing them and triggering KDE Service Crash notifications on every video
close. Detach a POSIX-sh supervisor instead: mount via --appimage-mount, run
AppRun from the mount, and release the holder only once no process still
executes from the mount. Falls back to the old direct launch on any failure
or with SUBMINER_NO_APPIMAGE_MOUNT_KEEPALIVE=1.
2026-07-12 02:09:42 -07:00
sudacode 4b7f750919 feat(animetosho): add English/Japanese subtitle download integration (#159) 2026-07-12 00:48:12 -07:00
sudacode 6ab3d823a4 feat(shortcuts): make clipboard-video-append shortcut configurable (#158) 2026-07-11 23:36:44 -07:00
sudacode 8797719a09 docs(docs-site): correct and clarify docs against current code
Comprehensive accuracy pass over docs-site verifying every page against
current source. Fixes wrong/stale claims (AniSkip default, YouTube track
selection, Anki sentence-card requirements, immersion schema v18 + SQL
column names, plugin entrypoint, launcher flags, Cloudflare deploy path,
etc.) and fills gaps (watch history, mediaCache.maxHeight, youtubeSubgen,
character-dictionary refresh/eviction, expanded hot-reload lists).
2026-07-11 00:53:34 -07:00
sudacode 8acc78cc1c test(tokenizer): add golden-file regression corpus with 11 fixtures (#157) 2026-07-11 00:50:03 -07:00