mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-24 12:11:29 -07:00
feat: add app-owned YouTube subtitle flow with absPlayer-style parsing (#31)
* fix: harden preload argv parsing for popup windows * fix: align youtube playback with shared overlay startup * fix: unwrap mpv youtube streams for anki media mining * docs: update docs for youtube subtitle and mining flow * refactor: unify cli and runtime wiring for startup and youtube flow * feat: update subtitle sidebar overlay behavior * chore: add shared log-file source for diagnostics * fix(ci): add changelog fragment for immersion changes * fix: address CodeRabbit review feedback * fix: persist canonical title from youtube metadata * style: format stats library tab * fix: address latest review feedback * style: format stats library files * test: stub launcher youtube deps in CI * test: isolate launcher youtube flow deps * test: stub launcher youtube deps in failing case * test: force x11 backend in launcher ci harness * test: address latest review feedback * fix(launcher): preserve user YouTube ytdl raw options * docs(backlog): update task tracking notes * fix(immersion): special-case youtube media paths in runtime and tracking * feat(stats): improve YouTube media metadata and picker key handling * fix(ci): format stats media library hook * fix: address latest CodeRabbit review items * docs: update youtube release notes and docs * feat: auto-load youtube subtitles before manual picker * fix: restore app-owned youtube subtitle flow * docs: update youtube playback docs and config copy * refactor: remove legacy youtube launcher mode plumbing * fix: refine youtube subtitle startup binding * docs: clarify youtube subtitle startup behavior * fix: address PR #31 latest review follow-ups * fix: address PR #31 follow-up review comments * test: harden youtube picker test harness * udpate backlog * fix: add timeout to youtube metadata probe * docs: refresh youtube and stats docs * update backlog * update backlog * chore: release v0.9.0
This commit is contained in:
@@ -37,6 +37,7 @@ import { createSessionHelpModal } from './modals/session-help.js';
|
||||
import { createSubtitleSidebarModal } from './modals/subtitle-sidebar.js';
|
||||
import { createRuntimeOptionsModal } from './modals/runtime-options.js';
|
||||
import { createSubsyncModal } from './modals/subsync.js';
|
||||
import { createYoutubeTrackPickerModal } from './modals/youtube-track-picker.js';
|
||||
import { createPositioningController } from './positioning.js';
|
||||
import { createOverlayContentMeasurementReporter } from './overlay-content-measurement.js';
|
||||
import { syncOverlayMouseIgnoreState } from './overlay-mouse-ignore.js';
|
||||
@@ -68,6 +69,7 @@ function isAnySettingsModalOpen(): boolean {
|
||||
ctx.state.subsyncModalOpen ||
|
||||
ctx.state.kikuModalOpen ||
|
||||
ctx.state.jimakuModalOpen ||
|
||||
ctx.state.youtubePickerModalOpen ||
|
||||
ctx.state.sessionHelpModalOpen
|
||||
);
|
||||
}
|
||||
@@ -80,6 +82,7 @@ function isAnyModalOpen(): boolean {
|
||||
ctx.state.kikuModalOpen ||
|
||||
ctx.state.runtimeOptionsModalOpen ||
|
||||
ctx.state.subsyncModalOpen ||
|
||||
ctx.state.youtubePickerModalOpen ||
|
||||
ctx.state.sessionHelpModalOpen ||
|
||||
ctx.state.subtitleSidebarModalOpen
|
||||
);
|
||||
@@ -128,11 +131,29 @@ const jimakuModal = createJimakuModal(ctx, {
|
||||
modalStateReader: { isAnyModalOpen },
|
||||
syncSettingsModalSubtitleSuppression,
|
||||
});
|
||||
const mouseHandlers = createMouseHandlers(ctx, {
|
||||
modalStateReader: { isAnySettingsModalOpen, isAnyModalOpen },
|
||||
applyYPercent: positioning.applyYPercent,
|
||||
getCurrentYPercent: positioning.getCurrentYPercent,
|
||||
persistSubtitlePositionPatch: positioning.persistSubtitlePositionPatch,
|
||||
getSubtitleHoverAutoPauseEnabled: () => ctx.state.autoPauseVideoOnSubtitleHover,
|
||||
getYomitanPopupAutoPauseEnabled: () => ctx.state.autoPauseVideoOnYomitanPopup,
|
||||
getPlaybackPaused: () => window.electronAPI.getPlaybackPaused(),
|
||||
sendMpvCommand: (command) => {
|
||||
window.electronAPI.sendMpvCommand(command);
|
||||
},
|
||||
});
|
||||
const youtubePickerModal = createYoutubeTrackPickerModal(ctx, {
|
||||
modalStateReader: { isAnyModalOpen },
|
||||
restorePointerInteractionState: mouseHandlers.restorePointerInteractionState,
|
||||
syncSettingsModalSubtitleSuppression,
|
||||
});
|
||||
const keyboardHandlers = createKeyboardHandlers(ctx, {
|
||||
handleRuntimeOptionsKeydown: runtimeOptionsModal.handleRuntimeOptionsKeydown,
|
||||
handleSubsyncKeydown: subsyncModal.handleSubsyncKeydown,
|
||||
handleKikuKeydown: kikuModal.handleKikuKeydown,
|
||||
handleJimakuKeydown: jimakuModal.handleJimakuKeydown,
|
||||
handleYoutubePickerKeydown: youtubePickerModal.handleYoutubePickerKeydown,
|
||||
handleControllerSelectKeydown: controllerSelectModal.handleControllerSelectKeydown,
|
||||
handleControllerDebugKeydown: controllerDebugModal.handleControllerDebugKeydown,
|
||||
handleSessionHelpKeydown: sessionHelpModal.handleSessionHelpKeydown,
|
||||
@@ -153,18 +174,6 @@ const keyboardHandlers = createKeyboardHandlers(ctx, {
|
||||
void subtitleSidebarModal.toggleSubtitleSidebarModal();
|
||||
},
|
||||
});
|
||||
const mouseHandlers = createMouseHandlers(ctx, {
|
||||
modalStateReader: { isAnySettingsModalOpen, isAnyModalOpen },
|
||||
applyYPercent: positioning.applyYPercent,
|
||||
getCurrentYPercent: positioning.getCurrentYPercent,
|
||||
persistSubtitlePositionPatch: positioning.persistSubtitlePositionPatch,
|
||||
getSubtitleHoverAutoPauseEnabled: () => ctx.state.autoPauseVideoOnSubtitleHover,
|
||||
getYomitanPopupAutoPauseEnabled: () => ctx.state.autoPauseVideoOnYomitanPopup,
|
||||
getPlaybackPaused: () => window.electronAPI.getPlaybackPaused(),
|
||||
sendMpvCommand: (command) => {
|
||||
window.electronAPI.sendMpvCommand(command);
|
||||
},
|
||||
});
|
||||
|
||||
let lastSubtitlePreview = '';
|
||||
let lastSecondarySubtitlePreview = '';
|
||||
@@ -194,6 +203,7 @@ function getActiveModal(): string | null {
|
||||
if (ctx.state.controllerDebugModalOpen) return 'controller-debug';
|
||||
if (ctx.state.subtitleSidebarModalOpen) return 'subtitle-sidebar';
|
||||
if (ctx.state.jimakuModalOpen) return 'jimaku';
|
||||
if (ctx.state.youtubePickerModalOpen) return 'youtube-track-picker';
|
||||
if (ctx.state.kikuModalOpen) return 'kiku';
|
||||
if (ctx.state.runtimeOptionsModalOpen) return 'runtime-options';
|
||||
if (ctx.state.subsyncModalOpen) return 'subsync';
|
||||
@@ -214,6 +224,9 @@ function dismissActiveUiAfterError(): void {
|
||||
if (ctx.state.jimakuModalOpen) {
|
||||
jimakuModal.closeJimakuModal();
|
||||
}
|
||||
if (ctx.state.youtubePickerModalOpen) {
|
||||
youtubePickerModal.closeYoutubePickerModal();
|
||||
}
|
||||
if (ctx.state.runtimeOptionsModalOpen) {
|
||||
runtimeOptionsModal.closeRuntimeOptionsModal();
|
||||
}
|
||||
@@ -416,6 +429,16 @@ function registerModalOpenHandlers(): void {
|
||||
window.electronAPI.notifyOverlayModalOpened('jimaku');
|
||||
});
|
||||
});
|
||||
window.electronAPI.onOpenYoutubeTrackPicker((payload) => {
|
||||
runGuarded('youtube:picker-open', () => {
|
||||
youtubePickerModal.openYoutubePickerModal(payload);
|
||||
});
|
||||
});
|
||||
window.electronAPI.onCancelYoutubeTrackPicker(() => {
|
||||
runGuarded('youtube:picker-cancel', () => {
|
||||
youtubePickerModal.closeYoutubePickerModal();
|
||||
});
|
||||
});
|
||||
window.electronAPI.onSubsyncManualOpen((payload: SubsyncManualPayload) => {
|
||||
runGuarded('subsync:manual-open', () => {
|
||||
subsyncModal.openSubsyncModal(payload);
|
||||
@@ -528,6 +551,7 @@ async function init(): Promise<void> {
|
||||
ctx.dom.secondarySubContainer.addEventListener('mouseleave', mouseHandlers.handleSecondaryMouseLeave);
|
||||
|
||||
mouseHandlers.setupResizeHandler();
|
||||
mouseHandlers.setupPointerTracking();
|
||||
mouseHandlers.setupSelectionObserver();
|
||||
mouseHandlers.setupYomitanObserver();
|
||||
setupDragDropToMpvQueue();
|
||||
@@ -536,6 +560,7 @@ async function init(): Promise<void> {
|
||||
});
|
||||
|
||||
jimakuModal.wireDomEvents();
|
||||
youtubePickerModal.wireDomEvents();
|
||||
kikuModal.wireDomEvents();
|
||||
runtimeOptionsModal.wireDomEvents();
|
||||
subsyncModal.wireDomEvents();
|
||||
|
||||
Reference in New Issue
Block a user