diff --git a/backlog/tasks/task-117 - Replace-YouTube-subtitle-generation-with-pure-TypeScript-pipeline-and-shared-AI-config.md b/backlog/tasks/task-117 - Replace-YouTube-subtitle-generation-with-pure-TypeScript-pipeline-and-shared-AI-config.md new file mode 100644 index 0000000..16c498b --- /dev/null +++ b/backlog/tasks/task-117 - Replace-YouTube-subtitle-generation-with-pure-TypeScript-pipeline-and-shared-AI-config.md @@ -0,0 +1,66 @@ +--- +id: TASK-117 +title: >- + Replace YouTube subtitle generation with pure TypeScript pipeline and shared + AI config +status: Done +assignee: + - codex +created_date: '2026-03-08 03:16' +updated_date: '2026-03-08 03:35' +labels: [] +dependencies: [] +references: + - /Users/sudacode/projects/japanese/SubMiner/launcher/youtube.ts + - /Users/sudacode/projects/japanese/SubMiner/src/anki-integration/ai.ts + - /Users/sudacode/projects/japanese/SubMiner/src/types.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/config/definitions/defaults-integrations.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/config/resolve/subtitle-domains.ts + - /Users/sudacode/projects/japanese/SubMiner/config.example.jsonc +--- + +## Description + + +Replace the launcher YouTube subtitle generation flow with a pure TypeScript pipeline that prefers real downloadable YouTube subtitles, never uses YouTube auto-generated subtitles, locally generates missing tracks with whisper.cpp, and can optionally fix generated subtitles via a shared OpenAI-compatible AI provider config. This feature also introduces a breaking config cleanup: move provider settings to a new top-level ai section and reduce ankiConnect.ai to a boolean feature toggle. + + +## Acceptance Criteria + +- [x] #1 Launcher YouTube subtitle generation prefers downloadable manual YouTube subtitles, never uses YouTube auto-generated subtitles, and locally generates only missing tracks with whisper.cpp. +- [x] #2 Generated whisper subtitle tracks can optionally be post-processed with an OpenAI-compatible AI provider using shared top-level ai config, with validation and fallback to raw whisper output on failure. +- [x] #3 Configuration is updated so top-level ai is canonical shared provider config, ankiConnect.ai is boolean-only, and youtubeSubgen includes whisperVadModel, whisperThreads, and fixWithAi. +- [x] #4 Launcher CLI/config parsing, config example, and docs reflect the new breaking config shape with no migration layer. +- [x] #5 Automated tests cover the new YouTube generation behavior, AI-fix fallback/validation behavior, shared AI config usage, and breaking config validation. + + +## Implementation Plan + + +1. Introduce canonical top-level ai config plus youtubeSubgen runtime knobs (whisperVadModel, whisperThreads, fixWithAi) and convert ankiConnect.ai to a boolean-only toggle across types, defaults, validation, option registries, launcher config parsing, and config example/docs. +2. Extract shared OpenAI-compatible AI client helpers from the current Anki translation code, including base URL normalization, API key / apiKeyCommand resolution, timeout handling, and response text extraction. +3. Update Anki translation flow and hot-reload/runtime plumbing to consume global ai config while treating ankiConnect.ai as a feature gate only. +4. Replace launcher/youtube.ts with a modular launcher/youtube pipeline that fetches only manual YouTube subtitles, generates missing tracks locally with ffmpeg + whisper.cpp + optional VAD/thread controls, and preserves preprocess/automatic playback behavior. +5. Add optional AI subtitle-fix processing for whisper-generated tracks using the shared ai client, with strict SRT batching/validation and fallback to raw whisper output on provider or format failure. +6. Expand automated coverage for config validation, shared AI usage, launcher config parsing, and YouTube subtitle generation behavior including removal of yt-dlp auto-subs and AI-fix fallback rules. + + +## Implementation Notes + + +Implemented pure TypeScript launcher/youtube pipeline modules for manual subtitle fetch, audio extraction, whisper runs, SRT utilities, and optional AI subtitle fixing. Removed yt-dlp auto-subtitle usage from the generation path. + +Added shared top-level ai config plus shared AI client helpers; converted ankiConnect.ai to a boolean feature gate and updated Anki runtime wiring to consume global ai config. + +Updated launcher config parsing, config template sections, and config.example.jsonc for the breaking config shape including youtubeSubgen.whisperVadModel, youtubeSubgen.whisperThreads, and youtubeSubgen.fixWithAi. + +Verification: bun run test:config:src passed; targeted AI/Anki/runtime tests passed; bun run typecheck passed. bun run test:launcher:unit:src reported one unrelated existing failure in launcher/aniskip-metadata.test.ts (resolveAniSkipMetadataForFile resolves MAL id and intro payload). + + +## Final Summary + + +Replaced the launcher YouTube subtitle flow with a modular TypeScript pipeline that prefers manual YouTube subtitles, transcribes only missing tracks with whisper.cpp, and can optionally post-fix whisper output through a shared OpenAI-compatible AI client with strict SRT validation/fallback. Introduced canonical top-level ai config, reduced ankiConnect.ai to a boolean feature gate, updated launcher/config parsing and checked-in config artifacts, and added coverage for YouTube orchestration, whisper args, SRT validation, AI fix behavior, and breaking config validation. + diff --git a/backlog/tasks/task-119 - Add-Jellyfin-remote-session-subtitle-streaming-to-texthooker.md b/backlog/tasks/task-119 - Add-Jellyfin-remote-session-subtitle-streaming-to-texthooker.md new file mode 100644 index 0000000..53cbe46 --- /dev/null +++ b/backlog/tasks/task-119 - Add-Jellyfin-remote-session-subtitle-streaming-to-texthooker.md @@ -0,0 +1,37 @@ +--- +id: TASK-119 +title: Add Jellyfin remote-session subtitle streaming to texthooker +status: To Do +assignee: [] +created_date: '2026-03-08 03:46' +labels: + - jellyfin + - texthooker + - subtitle +dependencies: [] +references: + - >- + /Users/sudacode/projects/japanese/SubMiner/src/main/runtime/jellyfin-remote-commands.ts + - /Users/sudacode/projects/japanese/SubMiner/src/core/services/jellyfin.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/core/services/subtitle-processing-controller.ts + - 'https://api.jellyfin.org/' +documentation: + - 'https://api.jellyfin.org/' +priority: medium +--- + +## Description + + +Allow SubMiner to follow subtitles from a separate Jellyfin client session, such as a TV app, without requiring local mpv playback. The feature should fetch the active subtitle stream from Jellyfin, map the remote playback position to subtitle cues, and feed the existing subtitle tokenization plus annotated texthooker websocket pipeline so texthooker-only mode can be used while watching on another device. + + +## Acceptance Criteria + +- [ ] #1 User can target a remote Jellyfin session and stream its current subtitle cue into SubMiner's existing subtitle-processing pipeline without launching local Jellyfin playback in mpv. +- [ ] #2 Texthooker-only mode can display subtitle updates from the tracked remote Jellyfin session through the existing annotation websocket feed. +- [ ] #3 Remote session changes are handled safely: item changes, subtitle-track changes, pause/seek/stop, and session disconnects clear or refresh subtitle state without crashing. +- [ ] #4 The feature degrades clearly when the remote session has no usable text subtitle stream or uses an unsupported subtitle format. +- [ ] #5 Automated tests cover session tracking, subtitle cue selection, and feed integration; user-facing docs/config docs are updated. + diff --git a/backlog/tasks/task-121 - Fix-YouTube-manual-subtitle-selection-regression-when-downloadable-tracks-exist.md b/backlog/tasks/task-121 - Fix-YouTube-manual-subtitle-selection-regression-when-downloadable-tracks-exist.md new file mode 100644 index 0000000..ec97b5a --- /dev/null +++ b/backlog/tasks/task-121 - Fix-YouTube-manual-subtitle-selection-regression-when-downloadable-tracks-exist.md @@ -0,0 +1,52 @@ +--- +id: TASK-121 +title: >- + Fix YouTube manual subtitle selection regression when downloadable tracks + exist +status: Done +assignee: + - '@codex' +created_date: '2026-03-08 05:37' +updated_date: '2026-03-08 05:42' +labels: + - bug + - youtube + - subtitles +dependencies: [] +references: + - /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/manual-subs.ts + - /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/orchestrator.ts + - 'https://www.youtube.com/watch?v=MXzQRLmN9hE' +priority: high +--- + +## Description + + +Ensure launcher YouTube subtitle generation reuses downloadable manual subtitle tracks when the video already has requested languages available, instead of falling back to whisper generation. Reproduce against videos like MXzQRLmN9hE that expose manual en/ja subtitles via yt-dlp. + + +## Acceptance Criteria + +- [x] #1 When requested primary/secondary manual YouTube subtitle tracks exist, planning selects them and schedules no whisper generation for those tracks. +- [x] #2 Filename normalization handles manual subtitle outputs produced by yt-dlp for language-tagged downloads. +- [x] #3 Automated tests cover the reproduced manual en/ja selection case. + + +## Implementation Notes + + +Reproduced against https://www.youtube.com/watch?v=MXzQRLmN9hE with yt-dlp --list-subs: manual zh/en/ja/ko subtitle tracks are available from YouTube. + +Adjusted launcher YouTube orchestration so detected manual subtitle tracks suppress whisper generation but are no longer materialized as external subtitle files. SubMiner now relies on the native YouTube/mpv subtitle tracks for those languages. + +Added orchestration tests covering the manual-track reuse plan and ran a direct runtime probe against MXzQRLmN9hE. Probe result: primary/secondary native tracks detected, no external subtitle aliases emitted, output directory remained empty. + +Verification: bun test launcher/youtube/orchestrator.test.ts launcher/config-domain-parsers.test.ts launcher/mpv.test.ts passed; bun run typecheck passed. + + +## Final Summary + + +Fixed the YouTube subtitle regression where videos with real downloadable subtitle tracks still ended up with duplicate external subtitle files. Manual subtitle availability now suppresses whisper generation and external subtitle publication, so videos like MXzQRLmN9hE use the native YouTube/mpv subtitle tracks directly. Launcher preprocess logging was also updated to report native subtitle availability instead of misleading missing statuses. + diff --git a/backlog/tasks/task-122 - Harden-changelog-workflow-and-CI-enforcement.md b/backlog/tasks/task-122 - Harden-changelog-workflow-and-CI-enforcement.md new file mode 100644 index 0000000..b93b292 --- /dev/null +++ b/backlog/tasks/task-122 - Harden-changelog-workflow-and-CI-enforcement.md @@ -0,0 +1,71 @@ +--- +id: TASK-122 +title: Harden changelog workflow and CI enforcement +status: Done +assignee: + - Codex +created_date: '2026-03-08 06:13' +updated_date: '2026-03-08 06:28' +labels: + - release + - changelog + - ci +dependencies: [] +references: + - /Users/sudacode/projects/japanese/SubMiner/scripts/build-changelog.ts + - /Users/sudacode/projects/japanese/SubMiner/scripts/build-changelog.test.ts + - /Users/sudacode/projects/japanese/SubMiner/.github/workflows/ci.yml + - /Users/sudacode/projects/japanese/SubMiner/.github/workflows/release.yml + - /Users/sudacode/projects/japanese/SubMiner/docs/RELEASING.md + - /Users/sudacode/projects/japanese/SubMiner/changes/README.md +priority: medium +--- + +## Description + + +Improve the release changelog workflow so changelog fragments are reliable, release output is more readable, and pull requests get early feedback when changelog metadata is missing or malformed. + + +## Acceptance Criteria + +- [x] #1 `scripts/build-changelog.ts` ignores non-fragment files in `changes/` and validates fragment structure before generating changelog output. +- [x] #2 Generated `CHANGELOG.md` and `release/release-notes.md` group public changes into readable sections instead of a flat bullet list. +- [x] #3 CI enforces changelog validation on pull requests and provides an explicit opt-out path for changes that should not produce release notes. +- [x] #4 Contributor docs explain the fragment format and the PR/release workflow for changelog generation. +- [x] #5 Automated tests cover fragment parsing/building behavior and workflow enforcement expectations. + + +## Implementation Plan + + +1. Add failing tests for changelog fragment discovery, structured fragment parsing/rendering, release-note output, and CI workflow expectations. +2. Update scripts/build-changelog.ts to ignore non-fragment files, parse fragment metadata, group generated output by change type, add lint/PR-check commands, and simplify output paths to repo-local artifacts. +3. Update CI and PR workflow files to run changelog validation on pull requests with an explicit skip path, and keep release workflow using committed changelog output. +4. Refresh changes/README.md, docs/RELEASING.md, and any PR template text so contributors know how to write fragments and when opt-out is allowed. +5. Run targeted tests and changelog commands, then record results and finalize the task. + + +## Implementation Notes + + +Implemented structured changelog fragments with required `type` and `area` metadata; `changes/README.md` is now ignored by the generator and verified by regression tests. + +Added `changelog:lint` and `changelog:pr-check`, plus PR CI enforcement with `skip-changelog` opt-out. PR check now reads git name-status output so deleted fragment files do not satisfy the requirement. + +Changed generated changelog/release notes output to grouped sections (`Added`, `Changed`, `Fixed`, etc.) and simplified release notes to highlights + install/assets pointers. + +Kept changelog output repo-local. This aligns with existing repo direction where docs updates happen in the sibling docs repo explicitly rather than implicit local writes from app-repo generators. + +Verification: `bun test scripts/build-changelog.test.ts src/ci-workflow.test.ts src/release-workflow.test.ts` passed; `bun run typecheck` passed; `bun run changelog:lint` passed. `bun run test:fast` still fails in unrelated existing `src/core/services/subsync.test.ts` cases (`runSubsyncManual keeps internal alass source file alive until sync finishes`, `runSubsyncManual resolves string sid values from mpv stream properties`). + + +## Final Summary + + +Hardened the changelog workflow end-to-end. `scripts/build-changelog.ts` now ignores helper files like `changes/README.md`, requires structured fragment metadata (`type` + `area`), groups generated release sections by change type, and emits shorter release notes focused on highlights plus install/assets pointers. Added explicit `changelog:lint` and `changelog:pr-check` commands, with PR validation based on git name-status so deleted fragment files do not satisfy the fragment requirement. + +Updated contributor-facing workflow docs in `changes/README.md`, `docs/RELEASING.md`, and a new PR template so authors know to add a fragment or apply the `skip-changelog` label. CI now runs fragment linting on every run and enforces fragment presence on pull requests. Added regression coverage in `scripts/build-changelog.test.ts` and a new `src/ci-workflow.test.ts` to lock the workflow contract. + +Verification completed: `bun test scripts/build-changelog.test.ts src/ci-workflow.test.ts src/release-workflow.test.ts`, `bun run typecheck`, and `bun run changelog:lint` all passed. A broader `bun run test:fast` run still fails in unrelated existing `src/core/services/subsync.test.ts` cases outside the changelog/workflow scope. + diff --git a/backlog/tasks/task-123 - Add-progress-logging-for-YouTube-subtitle-generation-phases.md b/backlog/tasks/task-123 - Add-progress-logging-for-YouTube-subtitle-generation-phases.md new file mode 100644 index 0000000..b4eda62 --- /dev/null +++ b/backlog/tasks/task-123 - Add-progress-logging-for-YouTube-subtitle-generation-phases.md @@ -0,0 +1,52 @@ +--- +id: TASK-123 +title: Add progress logging for YouTube subtitle generation phases +status: Done +assignee: + - '@codex' +created_date: '2026-03-08 07:07' +updated_date: '2026-03-08 07:15' +labels: + - ux + - logging + - youtube + - subtitles +dependencies: [] +references: + - /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/orchestrator.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/audio-extraction.ts + - /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/whisper.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/launcher/youtube/subtitle-fix-ai.ts +priority: medium +--- + +## Description + + +Improve launcher YouTube subtitle generation observability so users can tell that work is happening and roughly how long each phase is taking. Cover manual subtitle probe, audio extraction, ffmpeg prep, whisper generation, and optional AI subtitle fix phases without flooding normal logs. + + +## Acceptance Criteria + +- [x] #1 Users see clear info-level phase logs for YouTube subtitle generation work including subtitle probe, fallback audio extraction, whisper, and optional AI fix phases. +- [x] #2 Long-running phases surface elapsed-time progress or explicit start/finish timing so it is obvious the process is still active. +- [x] #3 Automated tests cover the new logging/progress helper behavior where practical. + + +## Implementation Notes + + +Implemented a shared timed YouTube phase logger in launcher/youtube/progress.ts with info-level start/finish messages and warn-level failure messages that include elapsed time. + +Wired phase logging into YouTube metadata probe, manual subtitle probe, fallback audio extraction, ffmpeg whisper prep, whisper primary/secondary generation, and optional AI subtitle fix phases. + +Verification: bun test launcher/youtube/progress.test.ts launcher/youtube/orchestrator.test.ts passed; bun run typecheck passed. + + +## Final Summary + + +Added clear phase-level observability for YouTube subtitle generation without noisy tool output. Users now see start/finish logs with elapsed time for subtitle probe, fallback audio extraction, ffmpeg prep, whisper generation, and optional AI subtitle-fix phases, making it obvious when generation is active and roughly how long each step took. + diff --git a/backlog/tasks/task-124 - Remove-YouTube-subtitle-generation-modes-and-make-YouTube-playback-always-generate-load-subtitles.md b/backlog/tasks/task-124 - Remove-YouTube-subtitle-generation-modes-and-make-YouTube-playback-always-generate-load-subtitles.md new file mode 100644 index 0000000..466212a --- /dev/null +++ b/backlog/tasks/task-124 - Remove-YouTube-subtitle-generation-modes-and-make-YouTube-playback-always-generate-load-subtitles.md @@ -0,0 +1,76 @@ +--- +id: TASK-124 +title: >- + Remove YouTube subtitle generation modes and make YouTube playback always + generate/load subtitles +status: Done +assignee: + - codex +created_date: '2026-03-08 07:18' +updated_date: '2026-03-08 07:28' +labels: + - launcher + - youtube + - subtitles +dependencies: [] +references: + - >- + /Users/sudacode/projects/japanese/SubMiner/launcher/commands/playback-command.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/launcher/config/args-normalizer.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/launcher/config/youtube-subgen-config.ts + - /Users/sudacode/projects/japanese/SubMiner/launcher/types.ts + - /Users/sudacode/projects/japanese/SubMiner/config.example.jsonc + - >- + /Users/sudacode/projects/japanese/SubMiner/src/config/definitions/options-integrations.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/config/resolve/subtitle-domains.ts +priority: high +--- + +## Description + + +Simplify launcher YouTube playback by removing the configurable subtitle generation mode. For YouTube targets, the launcher should treat subtitle generation/loading as the canonical behavior instead of supporting off/preprocess/automatic branches. This change should remove the unreliable automatic/background path and the mode concept from config/CLI/env/docs, while preserving the core YouTube subtitle generation pipeline and mpv loading flow. + + +## Acceptance Criteria + +- [x] #1 Launcher playback no longer supports or branches on a YouTube subtitle generation mode; YouTube URLs follow a single generation-and-load flow. +- [x] #2 Configuration, CLI parsing, and environment handling no longer expose a YouTube subtitle generation mode option, and stale automatic/preprocess/off values are not part of the supported interface. +- [x] #3 Tests cover the new single-flow behavior and the removal of mode parsing/branching. +- [x] #4 User-facing config/docs/examples are updated to reflect the removed mode concept. + + +## Implementation Plan + + +1. Remove the YouTube subtitle generation mode concept from launcher/shared types, config parsing, CLI options, and environment normalization so no supported interface accepts automatic/preprocess/off. +2. Update playback orchestration so YouTube targets always run subtitle generation/loading before mpv startup and delete the background automatic path. +3. Adjust mpv YouTube URL argument construction to no longer branch on mode while preserving subtitle/audio language behavior and preloaded subtitle file injection. +4. Add/modify tests first to cover removed mode parsing and the single YouTube preload flow, then update config/docs/examples to match the simplified interface. +5. Run focused launcher/config tests plus typecheck, then summarize any remaining gaps. + + +## Implementation Notes + + +Removed launcher/shared youtubeSubgen.mode handling and collapsed YouTube playback onto a single preload-before-mpv subtitle generation flow. + +Added launcher integration coverage proving YouTube subtitle generation runs before mpv startup and that the removed --mode flag now errors. + +Verification: bun test launcher/config-domain-parsers.test.ts launcher/parse-args.test.ts launcher/mpv.test.ts launcher/main.test.ts src/config/config.test.ts; bun run test:config:src; bun run typecheck. + +Broader repo checks still show pre-existing issues outside this change: bun run test:launcher:unit:src fails in launcher/aniskip-metadata.test.ts (MAL id assertion), and format scope check reports unrelated existing files launcher/youtube/orchestrator.ts, scripts/build-changelog.test.ts, scripts/build-changelog.ts. + + +## Final Summary + + +Removed the launcher YouTube subtitle generation mode surface so YouTube playback now always runs the subtitle generation pipeline before starting mpv. The launcher no longer accepts youtubeSubgen.mode from shared config, CLI, or env normalization, and the old automatic/background loading path has been deleted from playback. + +Updated mpv YouTube startup options to keep manual subtitle discovery enabled without requesting auto subtitles, and refreshed user-facing config/docs to describe a single YouTube subtitle generation flow. Added regression coverage for mode removal, config/template cleanup, and launcher ordering so YouTube subtitle work is confirmed to happen before mpv launch. + +Verification: bun test launcher/config-domain-parsers.test.ts launcher/parse-args.test.ts launcher/mpv.test.ts launcher/main.test.ts src/config/config.test.ts; bun run test:config:src; bun run typecheck. Broader unrelated repo issues remain in launcher/aniskip-metadata.test.ts and existing formatting drift in launcher/youtube/orchestrator.ts plus scripts/build-changelog files. + diff --git a/backlog/tasks/task-125 - Add-native-AI-API-key-secret-storage.md b/backlog/tasks/task-125 - Add-native-AI-API-key-secret-storage.md new file mode 100644 index 0000000..71da5f3 --- /dev/null +++ b/backlog/tasks/task-125 - Add-native-AI-API-key-secret-storage.md @@ -0,0 +1,35 @@ +--- +id: TASK-125 +title: Add native AI API key secret storage +status: To Do +assignee: [] +created_date: '2026-03-08 07:25' +labels: + - ai + - config + - security +dependencies: [] +references: + - /Users/sudacode/projects/japanese/SubMiner/src/ai/client.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/core/services/anilist/anilist-token-store.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/core/services/jellyfin-token-store.ts + - /Users/sudacode/projects/japanese/SubMiner/src/main.ts +priority: medium +--- + +## Description + + +Store the shared AI provider API key using the app's native secret-storage pattern so users do not need to keep the OpenRouter key in config files or shell commands. + + +## Acceptance Criteria + +- [ ] #1 Users can configure the shared AI provider without storing the API key in config.jsonc. +- [ ] #2 The app persists and reloads the shared AI API key using encrypted native secret storage when available. +- [ ] #3 Behavior is defined for existing ai.apiKey and ai.apiKeyCommand configs, including compatibility during migration. +- [ ] #4 The feature has regression tests covering key resolution and storage behavior. +- [ ] #5 User-facing configuration/docs are updated to describe the supported setup. + diff --git a/backlog/tasks/task-126 - Improve-secondary-subtitle-readability-with-hover-only-background-and-stronger-text-separation.md b/backlog/tasks/task-126 - Improve-secondary-subtitle-readability-with-hover-only-background-and-stronger-text-separation.md new file mode 100644 index 0000000..55bd9cc --- /dev/null +++ b/backlog/tasks/task-126 - Improve-secondary-subtitle-readability-with-hover-only-background-and-stronger-text-separation.md @@ -0,0 +1,43 @@ +--- +id: TASK-126 +title: >- + Improve secondary subtitle readability with hover-only background and stronger + text separation +status: Done +assignee: [] +created_date: '2026-03-08 07:35' +updated_date: '2026-03-08 07:40' +labels: + - overlay + - subtitles + - ui +dependencies: [] +priority: medium +--- + +## Description + + +Adjust overlay secondary subtitle styling so translation text stays readable on bright video backgrounds. Keep the dark background hidden by default in hover mode and show it only while hovered. Increase secondary subtitle weight to 600 and strengthen edge separation without changing primary subtitle styling. + + +## Acceptance Criteria + +- [x] #1 Secondary subtitles render with stronger edge separation than today. +- [x] #2 Secondary subtitle font weight defaults to 600. +- [x] #3 When secondary subtitle mode is hover, the secondary background appears only while hovered. +- [x] #4 Primary subtitle styling behavior remains unchanged. +- [x] #5 Renderer tests cover the new secondary hover background behavior and default secondary style values. + + +## Implementation Notes + + +Adjusted secondary subtitle defaults to use stronger shadowing, 600 font weight, and a translucent dark background. Routed secondary background/backdrop styling through CSS custom properties so hover mode can keep the background hidden until the secondary subtitle is actually hovered. Added renderer and config tests covering default values and hover-only background behavior. + + +## Final Summary + + +Improved secondary subtitle readability by strengthening default text separation, increasing the default secondary weight to 600, and making the configured dark background appear only while hovered in secondary hover mode. Added config and renderer coverage for the new defaults and hover-aware style routing. + diff --git a/backlog/tasks/task-128 - Prevent-AI-subtitle-fix-from-translating-primary-YouTube-subtitles-into-the-wrong-language.md b/backlog/tasks/task-128 - Prevent-AI-subtitle-fix-from-translating-primary-YouTube-subtitles-into-the-wrong-language.md new file mode 100644 index 0000000..b08eec1 --- /dev/null +++ b/backlog/tasks/task-128 - Prevent-AI-subtitle-fix-from-translating-primary-YouTube-subtitles-into-the-wrong-language.md @@ -0,0 +1,35 @@ +--- +id: TASK-128 +title: >- + Prevent AI subtitle fix from translating primary YouTube subtitles into the + wrong language +status: Done +assignee: [] +created_date: '2026-03-08 09:02' +updated_date: '2026-03-08 09:17' +labels: + - bug + - youtube-subgen + - ai +dependencies: [] +priority: high +--- + +## Description + + +AI subtitle cleanup can preserve cue structure while changing subtitle language, causing primary Japanese subtitle files to come back in English. Add guards so AI-fixed subtitles preserve expected language and fall back to raw Whisper output when language drifts. + + +## Acceptance Criteria + +- [x] #1 Primary AI subtitle fix rejects output that drifts away from the expected source language. +- [x] #2 Rejected AI fixes fall back to the raw Whisper subtitle without corrupting published subtitle language. +- [x] #3 Regression tests cover a primary Japanese subtitle batch being translated into English by the AI fixer. + + +## Final Summary + + +Added a primary-language guard to AI subtitle fixing so Japanese source subtitles are rejected if the AI rewrites them into English while preserving SRT structure. The fixer now receives the expected source language from the YouTube orchestrator, and regression coverage verifies that language drift falls back to the raw Whisper subtitle path. + diff --git a/backlog/tasks/task-129 - Split-AI-model-and-system-prompt-config-between-Anki-and-YouTube-subtitle-generation.md b/backlog/tasks/task-129 - Split-AI-model-and-system-prompt-config-between-Anki-and-YouTube-subtitle-generation.md new file mode 100644 index 0000000..e86e33f --- /dev/null +++ b/backlog/tasks/task-129 - Split-AI-model-and-system-prompt-config-between-Anki-and-YouTube-subtitle-generation.md @@ -0,0 +1,37 @@ +--- +id: TASK-129 +title: >- + Split AI model and system prompt config between Anki and YouTube subtitle + generation +status: Done +assignee: [] +created_date: '2026-03-08 09:40' +updated_date: '2026-03-08 09:57' +labels: + - config + - ai + - anki + - youtube-subgen +dependencies: [] +priority: high +--- + +## Description + + +The current top-level shared AI config forces Anki translation and YouTube subtitle fixing to share the same model and system prompt, which caused subtitle-fix requests to inherit a translation prompt and translate Japanese primary subtitles into English. Refactor config so provider credentials stay shared while model and system prompt can be configured per feature. + + +## Acceptance Criteria + +- [x] #1 Anki integration can use its own AI model and system prompt independently of YouTube subtitle generation. +- [x] #2 YouTube subtitle generation can use its own AI model and system prompt independently of Anki integration. +- [x] #3 Existing shared provider credentials remain reusable without duplicating API key/base URL config. +- [x] #4 Config example, defaults, validation, and regression tests cover the new per-feature override shape. + + +## Final Summary + + +Added per-feature AI model/systemPrompt overrides for Anki and YouTube subtitle generation while keeping shared provider transport settings reusable. Anki now accepts `ankiConnect.ai` object config with `enabled`, `model`, and `systemPrompt`; YouTube subtitle generation accepts `youtubeSubgen.ai` overrides and merges them over the shared AI provider config. Updated config resolution, launcher parsing, runtime wiring, hot-reload handling, example config, and regression coverage. + diff --git a/backlog/tasks/task-130 - Keep-background-SubMiner-alive-after-launcher-managed-mpv-exits.md b/backlog/tasks/task-130 - Keep-background-SubMiner-alive-after-launcher-managed-mpv-exits.md new file mode 100644 index 0000000..05b9f0d --- /dev/null +++ b/backlog/tasks/task-130 - Keep-background-SubMiner-alive-after-launcher-managed-mpv-exits.md @@ -0,0 +1,76 @@ +--- +id: TASK-130 +title: Keep background SubMiner alive after launcher-managed mpv exits +status: Done +assignee: + - codex +created_date: '2026-03-08 10:08' +updated_date: '2026-03-08 11:00' +labels: + - bug + - launcher + - mpv + - overlay +dependencies: [] +priority: high +--- + +## Description + + +The launcher currently tears down the running SubMiner background process when a launcher-managed mpv session exits. Background SubMiner should remain alive so a later mpv instance can reconnect and request the overlay without restarting the app. + + +## Acceptance Criteria + +- [x] #1 Closing a launcher-managed mpv session does not send `--stop` to the running SubMiner background process. +- [x] #2 Closing a launcher-managed mpv session does not SIGTERM the tracked SubMiner process just because mpv exited. +- [x] #3 Launcher cleanup still terminates mpv and launcher-owned helper children without regressing existing overlay start behavior. +- [x] #4 Automated tests cover the no-stop-on-mpv-exit behavior. + + +## Implementation Plan + + +1. Add a launcher regression test that proves mpv exit no longer triggers SubMiner `--stop` or launcher SIGTERM of the tracked overlay process. +2. Update launcher teardown so normal mpv-session cleanup only stops mpv/helper children and preserves the background SubMiner process for future reconnects. +3. Run the focused launcher tests and smoke coverage for the affected behavior, then record results in the task. + + +## Implementation Notes + + +Split launcher cleanup so normal mpv-session shutdown no longer sends `--stop` to SubMiner or SIGTERM to the tracked overlay process. Added `cleanupPlaybackSession()` for mpv/helper-child cleanup only, and switched playback finalization to use it. + +Updated launcher smoke coverage to assert the background app stays alive after mpv exits, and added a focused unit regression for the new cleanup path. + +Validation: `bun test launcher/mpv.test.ts launcher/smoke.e2e.test.ts` passed; `bun run typecheck` passed. `bun run test:launcher:unit:src` still reports an unrelated pre-existing failure in `launcher/aniskip-metadata.test.ts`. + +Added changelog fragment `changes/task-130.md` for the launcher fix and verified it with `bun run changelog:lint`. + +User verified the bug still reproduces when closing playback with `q`. Root cause narrowed further: the mpv plugin `plugin/subminer/lifecycle.lua` calls `process.stop_overlay()` on mpv `shutdown`, which still sends SubMiner `--stop` even after launcher cleanup was fixed. + +Patched the remaining stop path in `plugin/subminer/lifecycle.lua`: mpv `shutdown` no longer calls `process.stop_overlay()`. Pressing mpv `q` should now preserve the background app and only tear down the mpv session. + +Validation update: `lua scripts/test-plugin-start-gate.lua` passed after adding a shutdown regression, and `bun test launcher/mpv.test.ts launcher/smoke.e2e.test.ts` still passed. + +Fixed a second-instance reconnect bug in `src/core/services/cli-command.ts`: `--start` on an already-initialized running instance now still updates the MPV socket path and reconnects the MPV client instead of treating the command as a no-op. This keeps the already-warmed background app reusable for later mpv launches. + + +## Final Summary + + +Kept the background SubMiner process reusable across both mpv shutdown and later reconnects. The first fix separated launcher playback cleanup from full app shutdown. The second fix removed the mpv plugin `shutdown` stop call so default mpv `q` no longer sends SubMiner `--stop`. The third fix corrected second-instance CLI handling so `--start` on an already-running, already-initialized instance still reconnects MPV instead of being ignored. + +Net effect: background SubMiner can stay alive, keep its warm state, and reconnect to later mpv instances without rerunning startup/warmup work in a fresh app instance. + +Coverage now includes: launcher playback cleanup (`launcher/mpv.test.ts`), launcher smoke reconnect/keep-alive flow (`launcher/smoke.e2e.test.ts`), mpv plugin shutdown preservation (`scripts/test-plugin-start-gate.lua`), and second-instance start/reconnect behavior (`src/core/services/cli-command.test.ts`). + +Tests run: +- `bun test src/core/services/cli-command.test.ts launcher/mpv.test.ts launcher/smoke.e2e.test.ts` +- `lua scripts/test-plugin-start-gate.lua` +- `bun run typecheck` +- `bun run changelog:lint` + +Note: the broader `bun run test:launcher:unit:src` lane still has an unrelated pre-existing failure in `launcher/aniskip-metadata.test.ts`. + diff --git a/backlog/tasks/task-132 - Gate-macOS-overlay-shortcuts-to-the-focused-mpv-window.md b/backlog/tasks/task-132 - Gate-macOS-overlay-shortcuts-to-the-focused-mpv-window.md new file mode 100644 index 0000000..5161589 --- /dev/null +++ b/backlog/tasks/task-132 - Gate-macOS-overlay-shortcuts-to-the-focused-mpv-window.md @@ -0,0 +1,69 @@ +--- +id: TASK-132 +title: Gate macOS overlay shortcuts to the focused mpv window +status: Done +assignee: + - codex +created_date: '2026-03-08 18:24' +updated_date: '2026-03-08 18:55' +labels: + - bug + - macos + - shortcuts +dependencies: [] +references: + - >- + /Users/sudacode/projects/japanese/SubMiner/src/core/services/overlay-shortcut.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/window-trackers/macos-tracker.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/scripts/get-mpv-window-macos.swift +priority: high +--- + +## Description + + +Fix the macOS shortcut handling so SubMiner overlay keybinds do not intercept system or other-app shortcuts while SubMiner is in the background. Overlay shortcuts should only be active while the tracked mpv window is present and focused, and should stop grabbing keyboard input when mpv is not the frontmost window. + + +## Acceptance Criteria + +- [x] #1 On macOS, overlay shortcuts do not trigger while mpv is not the focused/frontmost window. +- [x] #2 On macOS, overlay shortcuts remain available while the tracked mpv window is open and focused. +- [x] #3 Existing non-macOS shortcut behavior is unchanged. +- [x] #4 Automated tests cover the macOS focus-gating behavior and guard against background shortcut interception. +- [x] #5 Any user-facing docs/config notes affected by the behavior change are updated in the same task if needed. + + +## Implementation Plan + + +1. Add a failing macOS-focused shortcut lifecycle test that proves overlay shortcuts stay inactive when the tracked mpv window exists but is not frontmost, and activate when that tracked window becomes frontmost. +2. Add a failing tracker/helper test that covers the focused/frontmost signal parsed from the macOS helper output. +3. Extend the macOS helper/tracker contract to surface both geometry and focused/frontmost state for the tracked mpv window. +4. Wire overlay shortcut activation to require both overlay runtime initialization and tracked-mpv focus on macOS, while leaving non-macOS behavior unchanged. +5. Re-run the targeted shortcut/tracker tests, then the broader related shortcut/runtime suite, and update task notes/acceptance criteria based on results. + + +## Implementation Notes + + +Added a macOS-specific shortcut activation predicate so global overlay shortcuts now require both overlay runtime readiness and a focused tracked mpv window; non-macOS behavior still keys off runtime readiness only. + +Extended the base window tracker with optional focus-state callbacks/getters and wired initializeOverlayRuntime to re-sync overlay shortcuts whenever tracker focus changes. + +Updated the macOS helper/tracker contract to return geometry plus frontmost/focused state for the tracked mpv process and added parser coverage for focused and unfocused output. + +Verified with `bun x tsc -p tsconfig.json --noEmit`, targeted shortcut/tracker tests, and `bun run test:core:src` (439 passing). + +No user-facing config or documentation surface changed, so no docs update was required for this fix. + + +## Final Summary + + +Fixed the macOS background shortcut interception bug by gating SubMiner's global overlay shortcuts on tracked mpv focus instead of overlay-runtime initialization alone. The macOS window helper now reports whether the tracked mpv process is frontmost, the tracker exposes focus change callbacks, and overlay shortcut synchronization re-runs when that focus state flips so `Ctrl+C`/`Ctrl+V` and similar shortcuts are no longer captured while mpv is in the background. + +The change keeps existing non-macOS shortcut behavior unchanged. Added regression coverage for the activation decision, tracker focus-change re-sync, and macOS helper output parsing. Verification: `bun x tsc -p tsconfig.json --noEmit`, targeted shortcut/tracker tests, and `bun run test:core:src` (439 passing). + diff --git a/backlog/tasks/task-133 - Improve-AniList-character-dictionary-parity-with-upstream-guide.md b/backlog/tasks/task-133 - Improve-AniList-character-dictionary-parity-with-upstream-guide.md new file mode 100644 index 0000000..c9179ce --- /dev/null +++ b/backlog/tasks/task-133 - Improve-AniList-character-dictionary-parity-with-upstream-guide.md @@ -0,0 +1,47 @@ +--- +id: TASK-133 +title: Improve AniList character dictionary parity with upstream guide +status: To Do +assignee: [] +created_date: '2026-03-08 21:06' +updated_date: '2026-03-08 21:35' +labels: + - dictionary + - anilist + - planning +dependencies: [] +references: + - >- + https://github.com/bee-san/Japanese_Character_Name_Dictionary/blob/main/docs/agents_read_me.md + - >- + /Users/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.ts + - >- + /Users/sudacode/projects/japanese/SubMiner/src/main/character-dictionary-runtime.test.ts +documentation: + - >- + /Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-08-anilist-character-dictionary-parity-design.md + - >- + /Users/sudacode/projects/japanese/SubMiner/docs/plans/2026-03-08-anilist-character-dictionary-parity.md +priority: high +--- + +## Description + + +Plan and implement guide-faithful parity improvements for the AniList character dictionary flow inside SubMiner's current single-media generation path. Scope includes AniList first/last name hints, hint-aware reading generation for kanji/native names, expanded honorific coverage, 160x200 JPEG thumbnail handling, and AniList 429 retry/backoff behavior. + + +## Acceptance Criteria + +- [ ] #1 AniList character queries include first/last name fields and preserve them through runtime data models. +- [ ] #2 Dictionary generation uses hint-aware name splitting and reading generation for kanji and mixed native names, not only kana-only readings. +- [ ] #3 Honorific generation is expanded substantially toward upstream coverage and is covered by regression tests. +- [ ] #4 Character and voice-actor images are resized or re-encoded to bounded JPEG thumbnails with fallback behavior. +- [ ] #5 AniList requests handle 429 responses with bounded exponential backoff and tests cover retry behavior. + + +## Implementation Notes + + +Approved design and implementation plan captured on 2026-03-08. Scope stays within current single-media AniList dictionary flow; excludes username-driven CURRENT-list fetching and Yomitan auto-update schema work. +