import React, { memo, useCallback, useMemo } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Button, Select, CrossIcon } from 'evergreen-ui';
import { FaRegCheckCircle } from 'react-icons/fa';
import i18n from 'i18next';
import { useTranslation, Trans } from 'react-i18next';
import { IoIosHelpCircle } from 'react-icons/io';
import KeyframeCutButton from './components/KeyframeCutButton';
import ExportButton from './components/ExportButton';
import ExportModeButton from './components/ExportModeButton';
import PreserveMovDataButton from './components/PreserveMovDataButton';
import MovFastStartButton from './components/MovFastStartButton';
import ToggleExportConfirm from './components/ToggleExportConfirm';
import OutSegTemplateEditor from './components/OutSegTemplateEditor';
import HighlightedText from './components/HighlightedText';
import { withBlur, toast } from './util';
import { isMov as ffmpegIsMov } from './util/streams';
import useUserSettings from './hooks/useUserSettings';
const sheetStyle = {
position: 'fixed',
left: 0,
right: 0,
top: 0,
bottom: 0,
zIndex: 10,
background: 'rgba(105, 105, 105, 0.7)',
backdropFilter: 'blur(10px)',
overflowY: 'scroll',
display: 'flex',
};
const boxStyle = { margin: '15px 15px 50px 15px', background: 'rgba(25, 25, 25, 0.6)', borderRadius: 10, padding: '10px 20px', minHeight: 500, position: 'relative' };
const outDirStyle = { background: 'rgb(193, 98, 0)', borderRadius: '.4em', padding: '0 .3em', wordBreak: 'break-all', cursor: 'pointer' };
const warningStyle = { color: '#faa', fontSize: '80%' };
const HelpIcon = ({ onClick }) => ;
const ExportConfirm = memo(({
areWeCutting, selectedSegments, segmentsToExport, willMerge, visible, onClosePress, onExportConfirm,
outFormat, renderOutFmt, outputDir, numStreamsTotal, numStreamsToCopy, setStreamsSelectorShown, outSegTemplate,
setOutSegTemplate, generateOutSegFileNames, filePath, currentSegIndexSafe, getOutSegError, nonFilteredSegments,
}) => {
const { t } = useTranslation();
const { changeOutDir, keyframeCut, preserveMovData, movFastStart, avoidNegativeTs, setAvoidNegativeTs, autoDeleteMergedSegments, exportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, toggleSegmentsToChapters, preserveMetadataOnMerge, togglePreserveMetadataOnMerge, enableSmartCut, setEnableSmartCut, effectiveExportMode } = useUserSettings();
const isMov = ffmpegIsMov(outFormat);
const isIpod = outFormat === 'ipod';
const exportModeDescription = useMemo(() => ({
sesgments_to_chapters: t('Don\'t cut the file, but instead export an unmodified original which has chapters generated from segments'),
merge: t('Auto merge segments to one file after export'),
'merge+separate': t('Auto merge segments to one file after export, but keep segments too'),
separate: t('Export to separate files'),
})[effectiveExportMode], [effectiveExportMode, t]);
const onPreserveMovDataHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Preserve all MOV/MP4 metadata tags (e.g. EXIF, GPS position etc.) from source file? Note that some players have trouble playing back files where all metadata is preserved, like iTunes and other Apple software') });
}, []);
const onMovFastStartHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Enable this to allow faster playback of the resulting file. This may cause processing to take a little longer') });
}, []);
const onOutFmtHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Defaults to same format as input file. You can losslessly change the file format (container) of the file with this option. Not all formats support all codecs. Matroska/MP4/MOV support the most common codecs. Sometimes it\'s even impossible to export to the same output format as input.') });
}, []);
const onKeyframeCutHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('With "keyframe cut", we will cut at the nearest keyframe before the desired start cutpoint. This is recommended for most files. With "Normal cut" you may have to manually set the cutpoint a few frames before the next keyframe to achieve a precise cut') });
}, []);
const onSmartCutHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('This experimental feature will re-encode the part of the video from the cutpoint until the next keyframe in order to attempt to make a 100% accurate cut. Only works on some files. I\'ve had success with some h264 files, and only a few h265 files. See more here: {{url}}', { url: 'https://github.com/mifi/lossless-cut/issues/126' }) });
}, []);
const onTracksHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('Not all formats support all track types, and LosslessCut is unable to properly cut some track types, so you may have to sacrifice some tracks by disabling them in order to get correct result.') });
}, []);
const onSegmentsToChaptersHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('When merging, do you want to create chapters in the merged file, according to the cut segments? NOTE: This may dramatically increase processing time') });
}, []);
const onPreserveMetadataOnMergeHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('When merging, do you want to preserve metadata from your original file? NOTE: This may dramatically increase processing time') });
}, []);
const onOutSegTemplateHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: i18n.t('You can customize the file name of the output segment(s) using special variables.') });
}, []);
const onExportModeHelpPress = useCallback(() => {
toast.fire({ icon: 'info', timer: 10000, text: exportModeDescription });
}, [exportModeDescription]);
const onAvoidNegativeTsHelpPress = useCallback(() => {
// https://ffmpeg.org/ffmpeg-all.html#Format-Options
const texts = {
make_non_negative: i18n.t('Shift timestamps to make them non-negative. Also note that this affects only leading negative timestamps, and not non-monotonic negative timestamps.'),
make_zero: i18n.t('Shift timestamps so that the first timestamp is 0. (LosslessCut default)'),
auto: i18n.t('Enables shifting when required by the target format.'),
disabled: i18n.t('Disables shifting of timestamp.'),
};
toast.fire({ icon: 'info', timer: 10000, text: `${avoidNegativeTs}: ${texts[avoidNegativeTs]}` });
}, [avoidNegativeTs]);
const outSegTemplateHelpIcon = ;
const canEditTemplate = !willMerge || !autoDeleteMergedSegments;
// https://stackoverflow.com/questions/33454533/cant-scroll-to-top-of-flex-item-that-is-overflowing-container
return (
{visible && (
<>