import type { KeyboardShortcutContext } from '~/types/video' export const handlePlayPause = (ctx: KeyboardShortcutContext) => { if (ctx.video.paused) { ctx.video.play() } else { ctx.video.pause() } } export const handleRewind = (ctx: KeyboardShortcutContext, seconds: number = 10) => { ctx.video.currentTime = Math.max(0, ctx.video.currentTime - seconds) } export const handleForward = (ctx: KeyboardShortcutContext, seconds: number = 10) => { ctx.video.currentTime = Math.min(ctx.video.duration, ctx.video.currentTime + seconds) } export const handleMute = (ctx: KeyboardShortcutContext) => { ctx.video.muted = !ctx.video.muted } export const handleVolumeChange = (ctx: KeyboardShortcutContext, delta: number) => { const newVolume = Math.max(0, Math.min(1, ctx.video.volume + delta)) ctx.video.volume = newVolume } export const handlePlaybackSpeedChange = (ctx: KeyboardShortcutContext, delta: number) => { const currentSpeed = ctx.video.playbackRate const newSpeed = Math.max(0.25, Math.min(2, currentSpeed + delta)) ctx.video.playbackRate = newSpeed } export const handleFrameStep = (ctx: KeyboardShortcutContext, direction: 'forward' | 'backward' = 'forward') => { if (!ctx.video.paused) { ctx.video.pause() } // Frame step by ~33ms (approximately 1 frame at 30fps, ~16.7ms at 60fps) const frameStep = 'forward' === direction ? 0.033 : -0.033 ctx.video.currentTime = Math.max(0, Math.min(ctx.video.duration, ctx.video.currentTime + frameStep)) } export const handleSeekToPercent = (ctx: KeyboardShortcutContext, percent: number) => { ctx.video.currentTime = (percent / 100) * ctx.video.duration } export const handleSeekBackward = (ctx: KeyboardShortcutContext, seconds: number = 5) => { ctx.video.currentTime = Math.max(0, ctx.video.currentTime - seconds) } export const handleSeekForward = (ctx: KeyboardShortcutContext, seconds: number = 5) => { ctx.video.currentTime = Math.min(ctx.video.duration, ctx.video.currentTime + seconds) } export const handleFullscreen = (videoElement: HTMLVideoElement) => { if (!document.fullscreenElement) { videoElement.requestFullscreen().catch(err => { console.error(`Error attempting to enable fullscreen: ${err.message}`) }) } else { document.exitFullscreen() } } export const handlePictureInPicture = async (videoElement: HTMLVideoElement) => { try { if (document.pictureInPictureElement) { await document.exitPictureInPicture() } else if (document.pictureInPictureEnabled) { await videoElement.requestPictureInPicture() } } catch (error) { console.error(`Picture-in-Picture error: ${error}`) } } export const handleToggleCaptions = (videoElement: HTMLVideoElement) => { const textTracks = videoElement.textTracks if (0 === textTracks.length) { return } for (let i = 0; i < textTracks.length; i++) { const track = textTracks[i] as TextTrack | null if (track && ('captions' === track.kind || 'subtitles' === track.kind)) { track.mode = 'showing' === track.mode ? 'hidden' : 'showing' break } } } export const shouldHandleKeyboardShortcut = (event: KeyboardEvent): boolean => { const target = event.target as HTMLElement const tagName = target?.tagName?.toLowerCase() if ('input' === tagName || 'textarea' === tagName || 'true' === target?.contentEditable || 'true' === target?.getAttribute('contenteditable')) { return false } return true } export const isModifierKey = (event: KeyboardEvent): boolean => event.ctrlKey || event.metaKey || event.altKey