feat(stats): improve YouTube media metadata and picker key handling

This commit is contained in:
2026-03-23 00:36:23 -07:00
parent 2e43d95396
commit e9fc6bf8ec
13 changed files with 336 additions and 29 deletions

View File

@@ -619,6 +619,33 @@ test('keyboard mode: configured stats toggle works even while popup is open', as
}
});
test('youtube picker: unhandled keys still dispatch mpv keybindings', async () => {
const { ctx, handlers, testGlobals } = createKeyboardHandlerHarness();
try {
await handlers.setupMpvInputForwarding();
handlers.updateKeybindings([
{
key: 'Space',
command: ['cycle', 'pause'],
},
{
key: 'KeyQ',
command: ['quit'],
},
] as never);
ctx.state.youtubePickerModalOpen = true;
testGlobals.dispatchKeydown({ key: ' ', code: 'Space' });
testGlobals.dispatchKeydown({ key: 'q', code: 'KeyQ' });
assert.deepEqual(testGlobals.mpvCommands.slice(-2), [['cycle', 'pause'], ['quit']]);
} finally {
testGlobals.restore();
}
});
test('keyboard mode: h moves left when popup is closed', async () => {
const { ctx, handlers, testGlobals } = createKeyboardHandlerHarness();

View File

@@ -843,8 +843,9 @@ export function createKeyboardHandlers(
return;
}
if (ctx.state.youtubePickerModalOpen) {
options.handleYoutubePickerKeydown(e);
return;
if (options.handleYoutubePickerKeydown(e)) {
return;
}
}
if (ctx.state.controllerSelectModalOpen) {
options.handleControllerSelectKeydown(e);

View File

@@ -348,3 +348,91 @@ test('youtube track picker surfaces rejected resolve calls as modal status', asy
Object.defineProperty(globalThis, 'document', { configurable: true, value: originalDocument });
}
});
test('youtube track picker only consumes handled keys', async () => {
const originalWindow = globalThis.window;
const originalDocument = globalThis.document;
Object.defineProperty(globalThis, 'document', {
configurable: true,
value: {
createElement: () => createFakeElement(),
},
});
Object.defineProperty(globalThis, 'window', {
configurable: true,
value: {
dispatchEvent: () => true,
focus: () => {},
electronAPI: {
notifyOverlayModalOpened: () => {},
notifyOverlayModalClosed: () => {},
youtubePickerResolve: async () => ({ ok: true, message: '' }),
setIgnoreMouseEvents: () => {},
},
},
});
try {
const state = createRendererState();
const dom = {
overlay: {
classList: createClassList(),
focus: () => {},
},
youtubePickerModal: createFakeElement(),
youtubePickerTitle: createFakeElement(),
youtubePickerPrimarySelect: createFakeElement(),
youtubePickerSecondarySelect: createFakeElement(),
youtubePickerTracks: createFakeElement(),
youtubePickerStatus: createFakeElement(),
youtubePickerContinueButton: createFakeElement(),
youtubePickerCloseButton: createFakeElement(),
};
const modal = createYoutubeTrackPickerModal(
{
state,
dom,
platform: {
shouldToggleMouseIgnore: false,
},
} as never,
{
modalStateReader: { isAnyModalOpen: () => true },
restorePointerInteractionState: () => {},
syncSettingsModalSubtitleSuppression: () => {},
},
);
modal.openYoutubePickerModal({
sessionId: 'yt-1',
url: 'https://example.com',
mode: 'download',
tracks: [],
defaultPrimaryTrackId: null,
defaultSecondaryTrackId: null,
hasTracks: false,
});
assert.equal(
modal.handleYoutubePickerKeydown({
key: ' ',
preventDefault: () => {},
} as KeyboardEvent),
false,
);
assert.equal(
modal.handleYoutubePickerKeydown({
key: 'Escape',
preventDefault: () => {},
} as KeyboardEvent),
true,
);
await Promise.resolve();
} finally {
Object.defineProperty(globalThis, 'window', { configurable: true, value: originalWindow });
Object.defineProperty(globalThis, 'document', { configurable: true, value: originalDocument });
}
});

View File

@@ -209,7 +209,7 @@ export function createYoutubeTrackPickerModal(
return true;
}
return true;
return false;
}
function wireDomEvents(): void {