diff --git a/package.json b/package.json index 8a1e5873..bb34bc6a 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "react-sortablejs": "^6.1.4", "react-syntax-highlighter": "^15.4.3", "react-use": "^17.4.0", + "screenfull": "^6.0.2", "scroll-into-view-if-needed": "^2.2.28", "sharp": "^0.32.6", "smpte-timecode": "^1.2.3", diff --git a/public/configStore.js b/public/configStore.js index 8a621da8..48f90565 100644 --- a/public/configStore.js +++ b/public/configStore.js @@ -65,6 +65,8 @@ const defaultKeyBindings = [ { keys: 'ctrl+c', action: 'copySegmentsToClipboard' }, { keys: 'command+c', action: 'copySegmentsToClipboard' }, + { key: 'f', action: 'toggleFullscreenVideo' }, + { keys: 'enter', action: 'labelCurrentSegment' }, { keys: 'e', action: 'export' }, diff --git a/src/App.jsx b/src/App.jsx index 79ade467..4a538966 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,6 +8,7 @@ import { useDebounce } from 'use-debounce'; import i18n from 'i18next'; import { useTranslation } from 'react-i18next'; import { produce } from 'immer'; +import screenfull from 'screenfull'; import fromPairs from 'lodash/fromPairs'; import sortBy from 'lodash/sortBy'; @@ -1909,6 +1910,21 @@ const App = memo(() => { } }, [addStreamSourceFile]); + const toggleFullscreenVideo = useCallback(async () => { + if (!screenfull.isEnabled) { + console.warn('Fullscreen not allowed'); + return; + } + try { + if (videoRef.current == null) { + console.warn('No video tag to full screen'); + return; + } + await screenfull.toggle(videoRef.current, { navigationUI: 'hide' }); + } catch (err) { + console.error('Failed to toggle fullscreen', err); + } + }, []); const mainActions = useMemo(() => { async function exportYouTube() { @@ -2039,8 +2055,9 @@ const App = memo(() => { toggleShowThumbnails, toggleShowKeyframes, showIncludeExternalStreamsDialog, + toggleFullscreenVideo, }; - }, [addSegment, alignSegmentTimesToKeyframes, apparentCutSegments, askStartTimeOffset, batchFileJump, batchOpenSelectedFile, captureSnapshot, captureSnapshotAsCoverArt, changePlaybackRate, checkFileOpened, cleanupFilesDialog, clearSegments, closeBatch, closeFileWithConfirm, combineOverlappingSegments, combineSelectedSegments, concatBatch, convertFormatBatch, copySegmentsToClipboard, createFixedDurationSegments, createNumSegments, createRandomSegments, createSegmentsFromKeyframes, currentSegIndexSafe, cutSegmentsHistory, deselectAllSegments, detectBlackScenes, detectSceneChanges, detectSilentScenes, duplicateCurrentSegment, extractAllStreams, extractCurrentSegmentFramesAsImages, extractSelectedSegmentsFramesAsImages, fillSegmentsGaps, goToTimecode, handleShowStreamsSelectorClick, increaseRotation, invertAllSegments, invertSelectedSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, onExportPress, onLabelSegment, openFilesDialog, openSendReportDialogWithState, pause, play, removeCutSegment, removeSelectedSegments, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, selectAllSegments, selectOnlyCurrentSegment, setCutEnd, setCutStart, setPlaybackVolume, shiftAllSegmentTimes, shortStep, showIncludeExternalStreamsDialog, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentSelected, toggleKeyboardShortcuts, toggleKeyframeCut, toggleLastCommands, toggleLoopSelectedSegments, togglePlay, toggleSegmentsList, toggleSettings, toggleShowKeyframes, toggleShowThumbnails, toggleStreamsSelector, toggleStripAudio, toggleWaveformMode, tryFixInvalidDuration, userHtml5ifyCurrentFile, zoomRel]); + }, [addSegment, alignSegmentTimesToKeyframes, apparentCutSegments, askStartTimeOffset, batchFileJump, batchOpenSelectedFile, captureSnapshot, captureSnapshotAsCoverArt, changePlaybackRate, checkFileOpened, cleanupFilesDialog, clearSegments, closeBatch, closeFileWithConfirm, combineOverlappingSegments, combineSelectedSegments, concatBatch, convertFormatBatch, copySegmentsToClipboard, createFixedDurationSegments, createNumSegments, createRandomSegments, createSegmentsFromKeyframes, currentSegIndexSafe, cutSegmentsHistory, deselectAllSegments, detectBlackScenes, detectSceneChanges, detectSilentScenes, duplicateCurrentSegment, extractAllStreams, extractCurrentSegmentFramesAsImages, extractSelectedSegmentsFramesAsImages, fillSegmentsGaps, goToTimecode, handleShowStreamsSelectorClick, increaseRotation, invertAllSegments, invertSelectedSegments, jumpCutEnd, jumpCutStart, jumpSeg, jumpTimelineEnd, jumpTimelineStart, keyboardNormalSeekSpeed, keyboardSeekAccFactor, onExportPress, onLabelSegment, openFilesDialog, openSendReportDialogWithState, pause, play, removeCutSegment, removeSelectedSegments, reorderSegsByStartTime, seekClosestKeyframe, seekRel, seekRelPercent, selectAllSegments, selectOnlyCurrentSegment, setCutEnd, setCutStart, setPlaybackVolume, shiftAllSegmentTimes, shortStep, showIncludeExternalStreamsDialog, shuffleSegments, splitCurrentSegment, timelineToggleComfortZoom, toggleCaptureFormat, toggleCurrentSegmentSelected, toggleFullscreenVideo, toggleKeyboardShortcuts, toggleKeyframeCut, toggleLastCommands, toggleLoopSelectedSegments, togglePlay, toggleSegmentsList, toggleSettings, toggleShowKeyframes, toggleShowThumbnails, toggleStreamsSelector, toggleStripAudio, toggleWaveformMode, tryFixInvalidDuration, userHtml5ifyCurrentFile, zoomRel]); const getKeyboardAction = useCallback((action) => mainActions[action], [mainActions]); @@ -2342,6 +2359,7 @@ const App = memo(() => {