feat(youtube): add YouTube browser window and Whisper subtitle source

- Add `subminer youtube`/`yt` and tray "Browse YouTube" window that plays or queues picked videos in mpv
- Add `youtube.subtitleSource` (`youtube` | `whisper`) to transcribe YouTube audio via subtitle generation
- Allow the subtitle generation modal on YouTube videos
- Pick the real Japanese track over machine-translated ones and fall back to yt-dlp on HTTP 429
- Remove legacy launcher youtubeSubgen/whisper/AI args and `--youtube-mode`
- Fix EPIPE crash when the launching terminal is closed with Ctrl+C
- Thin media timing review handles with orange original-boundary bars
- Update docs and change fragments
This commit is contained in:
sudacode committed 2026-09-28 20:46:11 -07:00
commit b347fdf21a
105 files changed
+2297 -623

No files matched your search

+7 -8
View File
@@ -7,7 +7,6 @@ export interface CliArgs {
launchMpv: boolean;
launchMpvTargets: string[];
youtubePlay?: string;
youtubeMode?: 'download' | 'generate';
stop: boolean;
toggle: boolean;
toggleVisibleOverlay: boolean;
@@ -16,6 +15,7 @@ export interface CliArgs {
hachidori: boolean;
settings: boolean;
syncWindow: boolean;
youtubeBrowser: boolean;
setup: boolean;
show: boolean;
hide: boolean;
@@ -129,7 +129,6 @@ export function parseArgs(argv: string[]): CliArgs {
launchMpv: false,
launchMpvTargets: [],
youtubePlay: undefined,
youtubeMode: undefined,
stop: false,
toggle: false,
toggleVisibleOverlay: false,
@@ -138,6 +137,7 @@ export function parseArgs(argv: string[]): CliArgs {
hachidori: false,
settings: false,
syncWindow: false,
youtubeBrowser: false,
setup: false,
show: false,
hide: false,
@@ -272,12 +272,6 @@ export function parseArgs(argv: string[]): CliArgs {
} else if (arg === '--youtube-play') {
const value = readValue(argv[i + 1]);
if (value) args.youtubePlay = value;
} else if (arg.startsWith('--youtube-mode=')) {
const value = arg.split('=', 2)[1];
if (value === 'download' || value === 'generate') args.youtubeMode = value;
} else if (arg === '--youtube-mode') {
const value = readValue(argv[i + 1]);
if (value === 'download' || value === 'generate') args.youtubeMode = value;
} else if (arg === '--launch-mpv') {
args.launchMpv = true;
args.launchMpvTargets = argv.slice(i + 1).filter((value) => value && !value.startsWith('--'));
@@ -290,6 +284,7 @@ export function parseArgs(argv: string[]): CliArgs {
else if (arg === '--hachidori') args.hachidori = true;
else if (arg === '--settings') args.settings = true;
else if (arg === '--sync-window') args.syncWindow = true;
else if (arg === '--youtube-browser') args.youtubeBrowser = true;
else if (arg === '--setup') args.setup = true;
else if (arg === '--show') args.show = true;
else if (arg === '--hide') args.hide = true;
@@ -574,6 +569,7 @@ export function hasExplicitCommand(args: CliArgs): boolean {
args.hachidori ||
args.settings ||
args.syncWindow ||
args.youtubeBrowser ||
args.setup ||
args.show ||
args.hide ||
@@ -654,6 +650,7 @@ export function isStandaloneTexthookerCommand(args: CliArgs): boolean {
!args.hachidori &&
!args.settings &&
!args.syncWindow &&
!args.youtubeBrowser &&
!args.setup &&
!args.show &&
!args.hide &&
@@ -727,6 +724,7 @@ export function shouldStartApp(args: CliArgs): boolean {
args.hachidori ||
args.settings ||
args.syncWindow ||
args.youtubeBrowser ||
args.setup ||
args.copySubtitle ||
args.copySubtitleMultiple ||
@@ -789,6 +787,7 @@ export function shouldRunDictionarySettingsOnlyStartup(args: CliArgs): boolean {
!args.togglePrimarySubtitleBar &&
!args.settings &&
!args.syncWindow &&
!args.youtubeBrowser &&
!args.show &&
!args.hide &&
!args.setup &&