tag improvements

- add keyboard shortcut
- save when pressing add another
- add warning about duplicate keybinding

closes #1829
pull/1849/head
Mikael Finstad 2 years ago
parent 9ab8ba3a79
commit d2b09744f5
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26

@ -79,7 +79,7 @@ import { askForHtml5ifySpeed } from './dialogs/html5ify';
import { askForOutDir, askForImportChapters, promptTimeOffset, askForFileOpenAction, confirmExtractAllStreamsDialog, showCleanupFilesDialog, showDiskFull, showExportFailedDialog, showConcatFailedDialog, openYouTubeChaptersDialog, showRefuseToOverwrite, openDirToast, openExportFinishedToast, openConcatFinishedToast, showOpenDialog } from './dialogs';
import { openSendReportDialog } from './reporting';
import { fallbackLng } from './i18n';
import { createSegment, getCleanCutSegments, findSegmentsAtCursor, sortSegments, convertSegmentsToChapters, hasAnySegmentOverlap, isDurationValid, playOnlyCurrentSegment } from './segments';
import { createSegment, getCleanCutSegments, findSegmentsAtCursor, sortSegments, convertSegmentsToChapters, hasAnySegmentOverlap, isDurationValid, playOnlyCurrentSegment, getSegmentTags } from './segments';
import { generateOutSegFileNames as generateOutSegFileNamesRaw, defaultOutSegTemplate } from './util/outputNameTemplate';
import { rightBarWidth, leftBarWidth, ffmpegExtractWindow, zoomMax } from './util/constants';
import BigWaveform from './components/BigWaveform';
@ -156,6 +156,8 @@ const App = memo(() => {
const [keyboardShortcutsVisible, setKeyboardShortcutsVisible] = useState(false);
const [mifiLink, setMifiLink] = useState();
const [alwaysConcatMultipleFiles, setAlwaysConcatMultipleFiles] = useState(false);
const [editingSegmentTagsSegmentIndex, setEditingSegmentTagsSegmentIndex] = useState();
const [editingSegmentTags, setEditingSegmentTags] = useState();
// Batch state / concat files
const [batchFiles, setBatchFiles] = useState([]);
@ -1933,6 +1935,15 @@ const App = memo(() => {
}
}, []);
const onEditSegmentTags = useCallback((index) => {
setEditingSegmentTagsSegmentIndex(index);
setEditingSegmentTags(getSegmentTags(apparentCutSegments[index]));
}, [apparentCutSegments]);
const editCurrentSegmentTags = useCallback(() => {
onEditSegmentTags(currentSegIndexSafe);
}, [currentSegIndexSafe, onEditSegmentTags]);
const mainActions = useMemo(() => {
async function exportYouTube() {
if (!checkFileOpened()) return;
@ -2036,6 +2047,7 @@ const App = memo(() => {
deselectAllSegments,
selectAllSegments,
selectOnlyCurrentSegment,
editCurrentSegmentTags,
toggleCurrentSegmentSelected,
invertSelectedSegments,
removeSelectedSegments,
@ -2064,7 +2076,7 @@ const App = memo(() => {
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, toggleFullscreenVideo, 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, editCurrentSegmentTags, 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]);
@ -2455,6 +2467,11 @@ const App = memo(() => {
onSelectSegmentsByTag={onSelectSegmentsByTag}
onLabelSelectedSegments={onLabelSelectedSegments}
updateSegAtIndex={updateSegAtIndex}
editingSegmentTags={editingSegmentTags}
editingSegmentTagsSegmentIndex={editingSegmentTagsSegmentIndex}
setEditingSegmentTags={setEditingSegmentTags}
setEditingSegmentTagsSegmentIndex={setEditingSegmentTagsSegmentIndex}
onEditSegmentTags={onEditSegmentTags}
/>
)}
</AnimatePresence>

@ -162,6 +162,7 @@ const SegmentList = memo(({
onLabelSegment, currentCutSeg, segmentAtCursor, toggleSegmentsList, splitCurrentSegment,
selectedSegments, isSegmentSelected, onSelectSingleSegment, onToggleSegmentSelected, onDeselectAllSegments, onSelectAllSegments, onSelectSegmentsByLabel, onSelectSegmentsByTag, onExtractSegmentFramesAsImages, onLabelSelectedSegments, onInvertSelectedSegments, onDuplicateSegmentClick,
jumpSegStart, jumpSegEnd, updateSegAtIndex,
editingSegmentTags, editingSegmentTagsSegmentIndex, setEditingSegmentTags, setEditingSegmentTagsSegmentIndex, onEditSegmentTags,
}) => {
const { t } = useTranslation();
const { getSegColor } = useSegColors();
@ -271,26 +272,19 @@ const SegmentList = memo(({
);
}
const [editingSegmentTagsSegmentIndex, setEditingSegmentTagsSegmentIndex] = useState();
const [editingSegmentTags, setEditingSegmentTags] = useState();
const [editingTag, setEditingTag] = useState();
const onTagChange = useCallback((tag, value) => setEditingSegmentTags((existingTags) => ({
...existingTags,
[tag]: value,
})), []);
})), [setEditingSegmentTags]);
const onTagReset = useCallback((tag) => setEditingSegmentTags(({ [tag]: deleted, ...rest }) => rest), []);
const onEditSegmentTags = useCallback((index) => {
setEditingSegmentTagsSegmentIndex(index);
setEditingSegmentTags(getSegmentTags(apparentCutSegments[index]));
}, [apparentCutSegments]);
const onTagReset = useCallback((tag) => setEditingSegmentTags(({ [tag]: deleted, ...rest }) => rest), [setEditingSegmentTags]);
const onSegmentTagsCloseComplete = useCallback(() => {
setEditingSegmentTagsSegmentIndex();
setEditingSegmentTags();
}, []);
}, [setEditingSegmentTags, setEditingSegmentTagsSegmentIndex]);
const onSegmentTagsConfirm = useCallback(() => {
updateSegAtIndex(editingSegmentTagsSegmentIndex, { tags: editingSegmentTags });

@ -7,6 +7,7 @@ import groupBy from 'lodash/groupBy';
import orderBy from 'lodash/orderBy';
import uniq from 'lodash/uniq';
import Swal from '../swal';
import SetCutpointButton from './SetCutpointButton';
import SegmentCutpointButton from './SegmentCutpointButton';
@ -295,6 +296,10 @@ const KeyboardShortcuts = memo(({
name: t('Label current segment'),
category: segmentsAndCutpointsCategory,
},
editCurrentSegmentTags: {
name: t('Edit current segment tags'),
category: segmentsAndCutpointsCategory,
},
splitCurrentSegment: {
name: t('Split segment at cursor'),
category: segmentsAndCutpointsCategory,
@ -622,8 +627,9 @@ const KeyboardShortcuts = memo(({
console.log('new key binding', action, keysStr);
setKeyBindings((existingBindings) => {
const haveDuplicate = existingBindings.some((existingBinding) => existingBinding.keys === keysStr);
if (haveDuplicate) {
const duplicate = existingBindings.find((existingBinding) => existingBinding.keys === keysStr);
if (duplicate) {
Swal.fire({ icon: 'error', title: t('Duplicate keyboard combination'), text: t('Combination is already bound to "{{alreadyBoundKey}}"', { alreadyBoundKey: actionsMap[duplicate.action]?.name }) });
console.log('trying to add duplicate');
return existingBindings;
}
@ -632,7 +638,7 @@ const KeyboardShortcuts = memo(({
setCreatingBinding();
return [...existingBindings, { action, keys: keysStr }];
});
}, [setKeyBindings]);
}, [actionsMap, setKeyBindings, t]);
const missingActions = Object.keys(mainActions).filter((key) => actionsMap[key] == null);
if (missingActions.length > 0) throw new Error(`Action(s) missing: ${missingActions.join(',')}`);

@ -24,7 +24,7 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
setNewTag();
}, [editingTag, onTagReset, setEditingTag]);
function onEditClick(tag) {
const onEditClick = useCallback((tag) => {
if (newTag) {
onTagChange(editingTag, editingTagVal);
setEditingTag();
@ -40,7 +40,7 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
setEditingTag(tag);
setEditingTagVal(mergedTags[tag]);
}
}
}, [editingTag, editingTagVal, existingTags, mergedTags, newTag, onResetClick, onTagChange, setEditingTag]);
function onSubmit(e) {
e.preventDefault();
@ -50,12 +50,18 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
const onAddPress = useCallback(async (e) => {
e.preventDefault();
e.target.blur();
if (newTag || editingTag != null) {
// save any unsaved edit
onEditClick();
}
const tag = await askForMetadataKey({ title: addTagTitle, text: addTagText });
if (!tag || Object.keys(mergedTags).includes(tag)) return;
setEditingTag(tag);
setEditingTagVal('');
setNewTag(tag);
}, [addTagText, addTagTitle, mergedTags, setEditingTag]);
}, [addTagText, addTagTitle, editingTag, mergedTags, newTag, onEditClick, setEditingTag]);
useEffect(() => {
ref.current?.focus();
@ -92,7 +98,7 @@ function TagEditor({ existingTags = emptyObject, customTags = emptyObject, editi
</tbody>
</table>
<Button style={{ marginTop: 10 }} iconBefore={PlusIcon} onClick={onAddPress}>{t('Add metadata')}</Button>
<Button style={{ marginTop: 10 }} iconBefore={PlusIcon} onClick={onAddPress}>{addTagTitle}</Button>
</>
);
}

Loading…
Cancel
Save