mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
clean up and add new showcase videos
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 21 MiB After Width: | Height: | Size: 3.0 MiB |
@@ -1,10 +1,11 @@
|
|||||||
---
|
---
|
||||||
id: TASK-133
|
id: TASK-133
|
||||||
title: Improve AniList character dictionary parity with upstream guide
|
title: Improve AniList character dictionary parity with upstream guide
|
||||||
status: To Do
|
status: In Progress
|
||||||
assignee: []
|
assignee:
|
||||||
|
- OpenCode
|
||||||
created_date: '2026-03-08 21:06'
|
created_date: '2026-03-08 21:06'
|
||||||
updated_date: '2026-03-08 21:35'
|
updated_date: '2026-03-10 06:18'
|
||||||
labels:
|
labels:
|
||||||
- dictionary
|
- dictionary
|
||||||
- anilist
|
- anilist
|
||||||
@@ -28,15 +29,11 @@ priority: high
|
|||||||
## Description
|
## Description
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
<!-- SECTION:DESCRIPTION:END -->
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
<!-- AC:BEGIN -->
|
<!-- AC:BEGIN -->
|
||||||
|
|
||||||
- [ ] #1 AniList character queries include first/last name fields and preserve them through runtime data models.
|
- [ ] #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.
|
- [ ] #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.
|
- [ ] #3 Honorific generation is expanded substantially toward upstream coverage and is covered by regression tests.
|
||||||
@@ -44,10 +41,20 @@ Plan and implement guide-faithful parity improvements for the AniList character
|
|||||||
- [ ] #5 AniList requests handle 429 responses with bounded exponential backoff and tests cover retry behavior.
|
- [ ] #5 AniList requests handle 429 responses with bounded exponential backoff and tests cover retry behavior.
|
||||||
<!-- AC:END -->
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Add targeted regression tests in `src/main/character-dictionary-runtime.test.ts` for AniList 429 retry behavior and thumbnail conversion/fallback image paths before changing runtime code.
|
||||||
|
2. Update `fetchAniList` in `src/main/character-dictionary-runtime.ts` to retry 429 responses with bounded exponential backoff or `Retry-After` delays, while preserving existing request pacing hook behavior.
|
||||||
|
3. Update `downloadCharacterImage` to run downloaded bytes through `convertImageToThumbnail`, returning `.jpg` paths when conversion succeeds and preserving original bytes/extensions on fallback.
|
||||||
|
4. Run focused character-dictionary tests first, then broader required verification commands if the focused lane passes.
|
||||||
|
5. Record progress against acceptance criteria #4 and #5 in TASK-133 notes/final checklist once verified.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
## Implementation Notes
|
## Implementation Notes
|
||||||
|
|
||||||
<!-- SECTION:NOTES:BEGIN -->
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
Resumed TASK-133 to finish remaining AC #4 image thumbnail wiring and AC #5 AniList 429 retry handling. Existing code already contains helper constants/functions; next work is wiring plus regression coverage.
|
||||||
<!-- SECTION:NOTES:END -->
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
id: TASK-151
|
||||||
|
title: Keep JLPT underline color stable during Yomitan text selection
|
||||||
|
status: Done
|
||||||
|
assignee:
|
||||||
|
- OpenCode
|
||||||
|
created_date: '2026-03-10 06:42'
|
||||||
|
updated_date: '2026-03-10 07:54'
|
||||||
|
labels: []
|
||||||
|
dependencies: []
|
||||||
|
references:
|
||||||
|
- src/renderer/style.css
|
||||||
|
- src/renderer/subtitle-render.test.ts
|
||||||
|
documentation:
|
||||||
|
- ../subminer-docs/development.md
|
||||||
|
- ../subminer-docs/architecture.md
|
||||||
|
priority: medium
|
||||||
|
---
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||||
|
Fix the subtitle overlay so JLPT underlines keep their JLPT color when Yomitan lookups trigger hover/selection styling on tokens that are also known, N+1, or frequency-highlighted.
|
||||||
|
<!-- SECTION:DESCRIPTION:END -->
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
<!-- AC:BEGIN -->
|
||||||
|
- [x] #1 JLPT-tagged subtitle tokens keep their JLPT underline color during hover/selection states used by Yomitan lookup.
|
||||||
|
- [x] #2 Tokens that combine JLPT with known, N+1, name-match, or frequency styling preserve their primary text color while keeping the JLPT underline color.
|
||||||
|
- [x] #3 Renderer regression coverage verifies the JLPT underline color remains explicit in the stylesheet for combined styling states.
|
||||||
|
<!-- AC:END -->
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
<!-- SECTION:PLAN:BEGIN -->
|
||||||
|
1. Revert the temporary box-shadow JLPT marker experiment and restore native JLPT underline styling in `src/renderer/style.css`.
|
||||||
|
2. Add explicit JLPT hover/selection color rules with higher specificity so Yomitan lookup highlight states cannot repaint the underline to the token hover color.
|
||||||
|
3. Update `src/renderer/subtitle-render.test.ts` to verify JLPT hover/selection rules keep the predefined JLPT underline color, then rerun focused and relevant verification commands.
|
||||||
|
|
||||||
|
4. Add JLPT-specific `text-decoration-color` overrides directly on child `.c` spans and their hover/selection states, since Yomitan lookup likely paints the decorated child text runs rather than only the parent token span.
|
||||||
|
<!-- SECTION:PLAN:END -->
|
||||||
|
|
||||||
|
## Implementation Notes
|
||||||
|
|
||||||
|
<!-- SECTION:NOTES:BEGIN -->
|
||||||
|
Added a renderer regression test that asserts JLPT underline color stays explicit through hover and selection styling.
|
||||||
|
|
||||||
|
Updated `src/renderer/style.css` to route JLPT underline color through `--subtitle-jlpt-underline-color` and re-apply it on word/character hover and selection states.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run test:fast`, `bun run build`, `bun run changelog:lint`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts changes/jlpt-underline-yomitan.md`. `bun run format:check:src` still fails because of unrelated existing formatting issues in `src/main-entry-runtime.test.ts` and `src/main-entry-runtime.ts`.
|
||||||
|
|
||||||
|
User verified the original CSS variable propagation fix did not resolve the live issue; continuing investigation and reopening the task.
|
||||||
|
|
||||||
|
Confirmed Chromium was repainting native `text-decoration` underlines with the selected text color; reproduced it in a temporary browser repro and verified that switching the JLPT marker to an inset box-shadow keeps the JLPT color stable during selection.
|
||||||
|
|
||||||
|
Replaced the previous native underline approach with a fragment-safe JLPT marker using `box-shadow` plus `box-decoration-break`, then updated stylesheet regression coverage to match the new implementation.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run test:fast`, `bun run build`.
|
||||||
|
|
||||||
|
User requested reverting the box-shadow marker experiment. Narrowing the fix to preserve native JLPT underline color during Yomitan hover/selection instead of changing the underline rendering method.
|
||||||
|
|
||||||
|
Reverted the box-shadow experiment per user request. Restored native JLPT underline styling and added explicit JLPT hover/selection rules so Yomitan lookup states keep the predefined JLPT underline color with `!important`.
|
||||||
|
|
||||||
|
Verification after revert/fix: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run build`.
|
||||||
|
|
||||||
|
Issue still reproduces specifically during Shift-held Yomitan lookup. Investigating decoration propagation onto child `.c` spans during lookup-triggered hover/selection states.
|
||||||
|
|
||||||
|
Added JLPT-specific `text-decoration-color` overrides directly to child `.c` spans and their hover/selection states, in addition to the token span itself, so lookup-triggered decoration painting on child runs keeps the JLPT color.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run typecheck`, `bun run build`.
|
||||||
|
|
||||||
|
Added a final high-specificity combined-state JLPT override after the annotation hover/selection rules. It targets tokens that are both JLPT-tagged and known/N+1/name-match/frequency-highlighted, forcing `text-decoration-color` back to the JLPT variable after the later annotation state blocks.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run build`.
|
||||||
|
|
||||||
|
Switched the JLPT underline from native `text-decoration` to a separate painted underline layer using `background-image` on JLPT tokens. Also changed hover token highlight rules to use `background-color` so the JLPT underline layer survives Shift+Yomitan hover/lookup interactions.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run build`.
|
||||||
|
|
||||||
|
User reports the latest background-image JLPT underline change introduced grey-looking token highlights. Investigating the interaction between the new underline layer and hover background styling.
|
||||||
|
|
||||||
|
The background-image underline layer interacts poorly with hover fills and makes highlighted tokens look grey. Switching the separate JLPT underline layer to an inset box-shadow so hover background color and JLPT underline paint can coexist cleanly.
|
||||||
|
|
||||||
|
Adjusted the separate JLPT underline layer from `background-image` to inset `box-shadow`. This keeps the underline decoupled from Yomitan hover color while avoiding the grey-looking underline artifact introduced by the background-image approach.
|
||||||
|
|
||||||
|
User reports the separate underline layer still disappears/greys out during lookup. Switching strategy again: use a dedicated `border-bottom` JLPT marker instead of box-shadow/background so the underline stays independent from Yomitan hover paint without blending artifacts.
|
||||||
|
|
||||||
|
Replaced the separate JLPT underline layer with `border-bottom` plus a small bottom padding. This keeps the underline visually separate from Yomitan hover paint and avoids the disappearing/grey artifact seen with background-image and box-shadow.
|
||||||
|
<!-- SECTION:NOTES:END -->
|
||||||
|
|
||||||
|
## Final Summary
|
||||||
|
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:BEGIN -->
|
||||||
|
Switched the JLPT underline layer in `src/renderer/style.css` to `border-bottom` plus `padding-bottom`, keeping it independent from Yomitan hover/selection repainting while avoiding the disappearing or grey artifact seen with the background-image and box-shadow approaches.
|
||||||
|
|
||||||
|
Updated `src/renderer/subtitle-render.test.ts` to assert the border-bottom-based JLPT underline layer.
|
||||||
|
|
||||||
|
Verification: `bun test src/renderer/subtitle-render.test.ts`, `bunx prettier --check src/renderer/style.css src/renderer/subtitle-render.test.ts`, `bun run build`.
|
||||||
|
<!-- SECTION:FINAL_SUMMARY:END -->
|
||||||
@@ -50,7 +50,7 @@
|
|||||||
"test:launcher": "bun run test:launcher:src",
|
"test:launcher": "bun run test:launcher:src",
|
||||||
"test:core": "bun run test:core:src",
|
"test:core": "bun run test:core:src",
|
||||||
"test:subtitle": "bun run test:subtitle:src",
|
"test:subtitle": "bun run test:subtitle:src",
|
||||||
"test:fast": "bun run test:config:src && bun run test:core:src && bun test src/main-entry-runtime.test.ts src/anki-integration/anki-connect-proxy.test.ts src/release-workflow.test.ts src/ci-workflow.test.ts scripts/build-changelog.test.ts && bun run tsc && bun test dist/main/runtime/registry.test.js",
|
"test:fast": "bun run test:config:src && bun run test:core:src && bun test src/main-entry-runtime.test.ts src/anki-integration/anki-connect-proxy.test.ts src/release-workflow.test.ts src/ci-workflow.test.ts scripts/build-changelog.test.ts scripts/mkv-to-readme-video.test.ts && bun run tsc && bun test dist/main/runtime/registry.test.js",
|
||||||
"generate:config-example": "bun run build && bun dist/generate-config-example.js",
|
"generate:config-example": "bun run build && bun dist/generate-config-example.js",
|
||||||
"start": "bun run build && electron . --start",
|
"start": "bun run build && electron . --start",
|
||||||
"dev": "bun run build && electron . --start --dev",
|
"dev": "bun run build && electron . --start --dev",
|
||||||
|
|||||||
@@ -102,7 +102,19 @@ fi
|
|||||||
|
|
||||||
has_encoder() {
|
has_encoder() {
|
||||||
local encoder="$1"
|
local encoder="$1"
|
||||||
ffmpeg -hide_banner -encoders 2> /dev/null | grep -qE "[[:space:]]${encoder}[[:space:]]"
|
ffmpeg -hide_banner -encoders 2> /dev/null | awk -v encoder="$encoder" '$2 == encoder { found = 1 } END { exit(found ? 0 : 1) }'
|
||||||
|
}
|
||||||
|
|
||||||
|
pick_webp_encoder() {
|
||||||
|
if has_encoder "libwebp_anim"; then
|
||||||
|
echo "libwebp_anim"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if has_encoder "libwebp"; then
|
||||||
|
echo "libwebp"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
crop_vf="crop=1920:1080:760:205"
|
crop_vf="crop=1920:1080:760:205"
|
||||||
@@ -168,14 +180,14 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$generate_webp" -eq 1 ]]; then
|
if [[ "$generate_webp" -eq 1 ]]; then
|
||||||
if ! has_encoder "libwebp"; then
|
if ! webp_encoder="$(pick_webp_encoder)"; then
|
||||||
echo "Error: encoder not found: libwebp" >&2
|
echo "Error: encoder not found: libwebp" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
echo "Generating animated WebP: $webp_out"
|
echo "Generating animated WebP with $webp_encoder: $webp_out"
|
||||||
ffmpeg "$overwrite_flag" -i "$input" \
|
ffmpeg "$overwrite_flag" -i "$input" \
|
||||||
-vf "${crop_vf},fps=24,scale=960:-1:flags=lanczos" \
|
-vf "${crop_vf},fps=24,scale=960:-1:flags=lanczos" \
|
||||||
-c:v libwebp \
|
-c:v "$webp_encoder" \
|
||||||
-q:v 80 \
|
-q:v 80 \
|
||||||
-loop 0 \
|
-loop 0 \
|
||||||
-an \
|
-an \
|
||||||
|
|||||||
75
scripts/mkv-to-readme-video.test.ts
Normal file
75
scripts/mkv-to-readme-video.test.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import os from 'node:os';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { spawnSync } from 'node:child_process';
|
||||||
|
import test from 'node:test';
|
||||||
|
|
||||||
|
function withTempDir<T>(fn: (dir: string) => T): T {
|
||||||
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'subminer-mkv-to-readme-video-test-'));
|
||||||
|
try {
|
||||||
|
return fn(dir);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(dir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeExecutable(filePath: string, contents: string): void {
|
||||||
|
fs.writeFileSync(filePath, contents, 'utf8');
|
||||||
|
fs.chmodSync(filePath, 0o755);
|
||||||
|
}
|
||||||
|
|
||||||
|
test('mkv-to-readme-video accepts libwebp_anim when libwebp is unavailable', () => {
|
||||||
|
withTempDir((root) => {
|
||||||
|
const binDir = path.join(root, 'bin');
|
||||||
|
const inputPath = path.join(root, 'sample.mkv');
|
||||||
|
const ffmpegLogPath = path.join(root, 'ffmpeg-args.log');
|
||||||
|
|
||||||
|
fs.mkdirSync(binDir, { recursive: true });
|
||||||
|
fs.writeFileSync(inputPath, 'fake-video', 'utf8');
|
||||||
|
|
||||||
|
writeExecutable(
|
||||||
|
path.join(binDir, 'ffmpeg'),
|
||||||
|
`#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ "$#" -ge 2 && "$1" == "-hide_banner" && "$2" == "-encoders" ]]; then
|
||||||
|
cat <<'EOF'
|
||||||
|
V....D libx264 libx264 H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 (codec h264)
|
||||||
|
V....D libvpx-vp9 libvpx VP9 (codec vp9)
|
||||||
|
V....D libwebp_anim libwebp WebP image (codec webp)
|
||||||
|
A....D aac AAC (Advanced Audio Coding)
|
||||||
|
A....D libopus libopus Opus (codec opus)
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\\n' "$*" >> "${ffmpegLogPath}"
|
||||||
|
output=""
|
||||||
|
for arg in "$@"; do
|
||||||
|
output="$arg"
|
||||||
|
done
|
||||||
|
mkdir -p "$(dirname "$output")"
|
||||||
|
touch "$output"
|
||||||
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = spawnSync('bash', ['scripts/mkv-to-readme-video.sh', '--webp', inputPath], {
|
||||||
|
cwd: process.cwd(),
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
PATH: `${binDir}:${process.env.PATH || ''}`,
|
||||||
|
},
|
||||||
|
encoding: 'utf8',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
||||||
|
assert.equal(fs.existsSync(path.join(root, 'sample.mp4')), true);
|
||||||
|
assert.equal(fs.existsSync(path.join(root, 'sample.webm')), true);
|
||||||
|
assert.equal(fs.existsSync(path.join(root, 'sample.webp')), true);
|
||||||
|
assert.equal(fs.existsSync(path.join(root, 'sample-poster.jpg')), true);
|
||||||
|
|
||||||
|
const ffmpegLog = fs.readFileSync(ffmpegLogPath, 'utf8');
|
||||||
|
assert.match(ffmpegLog, /-c:v libwebp_anim/);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user