diff --git a/package.json b/package.json index 67222cff..496b344b 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-react": "^7.23.1", "eslint-plugin-react-hooks": "^4.2.0", - "evergreen-ui": "^4.23.0", + "evergreen-ui": "^6.4.0", "fast-xml-parser": "^3.17.4", "file-url": "^3.0.0", "framer-motion": "1", diff --git a/src/App.jsx b/src/App.jsx index c667e6d1..92f474be 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,7 +3,7 @@ import { FaAngleLeft, FaWindowClose } from 'react-icons/fa'; import { AnimatePresence, motion } from 'framer-motion'; import Swal from 'sweetalert2'; import Lottie from 'react-lottie-player'; -import { SideSheet, Button, Position, SegmentedControl, Select } from 'evergreen-ui'; +import { SideSheet, Button, Position, ForkIcon, DisableIcon, Select, ThemeProvider } from 'evergreen-ui'; import { useStateWithHistory } from 'react-use/lib/useStateWithHistory'; import useDebounceOld from 'react-use/lib/useDebounce'; // Want to phase out this import { useDebounce } from 'use-debounce'; @@ -19,6 +19,7 @@ import sortBy from 'lodash/sortBy'; import flatMap from 'lodash/flatMap'; import isEqual from 'lodash/isEqual'; +import theme from './theme'; import useTimelineScroll from './hooks/useTimelineScroll'; import useUserPreferences from './hooks/useUserPreferences'; import useFfmpegOperations from './hooks/useFfmpegOperations'; @@ -53,7 +54,7 @@ import { import { saveCsv, saveTsv, loadCsv, loadXmeml, loadCue, loadPbf, loadMplayerEdl, saveCsvHuman, saveLlcProject, loadLlcProject } from './edlStore'; import { formatYouTube } from './edlFormats'; import { - getOutPath, toast, errorToast, showFfmpegFail, setFileNameTitle, getOutDir, withBlur, + getOutPath, toast, errorToast, handleError, showFfmpegFail, setFileNameTitle, getOutDir, withBlur, checkDirWriteAccess, dirExists, openDirToast, isMasBuild, isStoreBuild, dragPreventer, doesPlayerSupportFile, isDurationValid, isWindows, filenamify, getOutFileExtension, generateSegFileName, defaultOutSegTemplate, hasDuplicates, havePermissionToReadFile, isMac, getFileBaseName, resolvePathIfNeeded, pathExists, @@ -469,7 +470,7 @@ const App = memo(() => { } */ setCutTime('start', startTime); } catch (err) { - errorToast(err.message); + handleError(err); } } }, [setCutTime, currentCutSeg, addCutSegment, filePath]); @@ -486,7 +487,7 @@ const App = memo(() => { } */ setCutTime('end', endTime); } catch (err) { - errorToast(err.message); + handleError(err); } }, [setCutTime, filePath]); @@ -1620,7 +1621,7 @@ const App = memo(() => { return; } if (openFileResponse === 'tracks') { - addStreamSourceFile(firstFilePath); + await addStreamSourceFile(firstFilePath); setStreamsSelectorShown(true); return; } @@ -1929,9 +1930,13 @@ const App = memo(() => { ]); async function showAddStreamSourceDialog() { - const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'] }); - if (canceled || filePaths.length < 1) return; - await addStreamSourceFile(filePaths[0]); + try { + const { canceled, filePaths } = await dialog.showOpenDialog({ properties: ['openFile'] }); + if (canceled || filePaths.length < 1) return; + await addStreamSourceFile(filePaths[0]); + } catch (err) { + handleError(err); + } } useEffect(() => { @@ -1961,9 +1966,9 @@ const App = memo(() => { )); } - const renderOutFmt = useCallback((props) => ( + const renderOutFmt = useCallback((style) => ( // eslint-disable-next-line react/jsx-props-no-spreading - onOutputFormatUserChange(e.target.value))}> {detectedFileFormat && ( @@ -1992,11 +1997,9 @@ const App = memo(() => { ), [captureFormat, toggleCaptureFormat]); const AutoExportToggler = useCallback(() => ( - setAutoExportExtraStreams(value === 'extract')} - /> + ), [autoExportExtraStreams, setAutoExportExtraStreams]); const onTunerRequested = useCallback((type) => { @@ -2109,304 +2112,306 @@ const App = memo(() => { // throw new Error('Test'); return ( -
-
- setStreamsSelectorShown(false)} - > - +
+
+ setStreamsSelectorShown(false)} + > + + + + - +
- -
+ {!isFileOpened && } - {!isFileOpened && } + + {working && ( +
+ +
+ +
- - {working && ( +
+ {working}... +
+ + {(cutProgress != null) && ( +
+ {`${(cutProgress * 100).toFixed(1)} %`} +
+ )} +
+
+ )} +
+ +
+ {/* eslint-disable-next-line jsx-a11y/media-has-caption */} +
+ + {isRotationSet && !hideCanvasPreview && (
- -
- -
- -
- {working}... -
- - {(cutProgress != null) && ( -
- {`${(cutProgress * 100).toFixed(1)} %`} -
- )} -
+ {t('Rotation preview')} + {!canvasPlayerRequired && setHideCanvasPreview(true)} />}
)} - -
- {/* eslint-disable-next-line jsx-a11y/media-has-caption */} -
- - + + {showSideBar && ( + + + + )} + + + )} - - -
- + - -
-
- +
+ - + +
+ - + - {tunerVisible && renderTuner(tunerVisible)} -
+ + + + + {tunerVisible && renderTuner(tunerVisible)} +
+ ); }); diff --git a/src/ExportConfirm.jsx b/src/ExportConfirm.jsx index 7abae31b..7b2269ea 100644 --- a/src/ExportConfirm.jsx +++ b/src/ExportConfirm.jsx @@ -1,6 +1,6 @@ import React, { memo } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; -import { Button, Select } from 'evergreen-ui'; +import { Button, Select, CrossIcon } from 'evergreen-ui'; import i18n from 'i18next'; import { useTranslation, Trans } from 'react-i18next'; import { IoIosHelpCircle } from 'react-icons/io'; @@ -30,7 +30,7 @@ const sheetStyle = { display: 'flex', }; -const boxStyle = { margin: '15px 15px 50px 15px', background: 'rgba(25, 25, 25, 0.6)', borderRadius: 10, padding: '10px 20px', minHeight: 450 }; +const boxStyle = { margin: '15px 15px 50px 15px', background: 'rgba(25, 25, 25, 0.6)', borderRadius: 10, padding: '10px 20px', minHeight: 450, position: 'relative' }; const outDirStyle = { background: 'rgb(193, 98, 0)', borderRadius: '.4em', padding: '0 .3em', wordBreak: 'break-all', cursor: 'pointer' }; @@ -111,6 +111,8 @@ const ExportConfirm = memo(({ >
+ +

{t('Export options')}

    {enabledOutSegments.length >= 2 &&
  • {t('Merge {{segments}} cut segments to one file?', { segments: enabledOutSegments.length })}
  • } @@ -190,11 +192,7 @@ const ExportConfirm = memo(({ transition={{ duration: 0.4, easings: ['easeOut'] }} style={{ display: 'flex', alignItems: 'flex-end' }} > - - - +
    {t('Show this page before exporting?')}
    diff --git a/src/Settings.jsx b/src/Settings.jsx index 3a59f28d..f087d113 100644 --- a/src/Settings.jsx +++ b/src/Settings.jsx @@ -1,5 +1,6 @@ import React, { memo } from 'react'; -import { Button, Table, SegmentedControl, Checkbox, Select } from 'evergreen-ui'; +import { FaYinYang } from 'react-icons/fa'; +import { Button, Table, NumericalIcon, KeyIcon, FolderCloseIcon, DocumentIcon, TimeIcon, Checkbox, Select } from 'evergreen-ui'; import { useTranslation } from 'react-i18next'; @@ -66,11 +67,11 @@ const Settings = memo(({ {t('Working directory')}
    - {t('This is where working files, exported files, project files (CSV) are stored.')} + {t('This is where working files, exported files, project files (LLC) are stored.')}
    -
    {customOutDir}
    @@ -79,11 +80,9 @@ const Settings = memo(({ {t('Set file modification date/time of output files to:')} - setEnableTransferTimestamps(value === 'true')} - /> + @@ -94,26 +93,22 @@ const Settings = memo(({ {t('Normal cut')}: {t('Accurate time but could leave an empty portion at the beginning of the video. Equiv to')} ffmpeg -i -ss ...
    - setKeyframeCut(value === 'keyframe')} - /> +
    - ☯️ {t('Choose cutting mode: Remove or keep selected segments from video when exporting?')}
    + {t('Choose cutting mode: Remove or keep selected segments from video when exporting?')}
    {t('Keep')}: {t('The video inside segments will be kept, while the video outside will be discarded.')}
    {t('Remove')}: {t('The video inside segments will be discarded, while the video surrounding them will be kept.')}
    - setInvertCutSegments(value === 'discard')} - /> +
    @@ -164,11 +159,9 @@ const Settings = memo(({ {t('In timecode show')} - setTimecodeShowFrames(value === 'frames')} - /> + diff --git a/src/StreamsSelector.jsx b/src/StreamsSelector.jsx index 6f9ee3e4..c83b22a3 100644 --- a/src/StreamsSelector.jsx +++ b/src/StreamsSelector.jsx @@ -1,10 +1,10 @@ import React, { memo, useState, useMemo } from 'react'; -import { FaVideo, FaVideoSlash, FaFileExport, FaFileImport, FaVolumeUp, FaVolumeMute, FaBan, FaTrashAlt, FaInfoCircle } from 'react-icons/fa'; +import { FaVideo, FaVideoSlash, FaFileImport, FaVolumeUp, FaVolumeMute, FaBan, FaTrashAlt, FaInfoCircle, FaFileExport } from 'react-icons/fa'; import { GoFileBinary } from 'react-icons/go'; import { FiEdit, FiCheck, FiTrash } from 'react-icons/fi'; import { MdSubtitles } from 'react-icons/md'; -import { SegmentedControl, Dialog, Button } from 'evergreen-ui'; +import { SortAscIcon, SortDescIcon, Dialog, Button, PlusIcon, Pane, ForkIcon } from 'evergreen-ui'; import { useTranslation } from 'react-i18next'; import { askForMetadataKey, showJson5Dialog } from './dialogs'; @@ -89,7 +89,7 @@ const TagEditor = memo(({ existingTags, customTags, onTagChange, onTagReset }) = - + ); }); @@ -207,7 +207,7 @@ const FileHeading = ({ path, formatData, onTrashClick, onEditClick }) => { const { t } = useTranslation(); return ( -
    +
    {path.replace(/.*\/([^/]+)$/, '$1')}
    onInfoClick(formatData, t('File info'))} size={20} style={{ padding: '0 5px 0 10px' }} /> {onEditClick && } @@ -237,7 +237,7 @@ const Thead = () => { }; const tableStyle = { fontSize: 14, width: '100%' }; -const fileStyle = { marginBottom: 10, backgroundColor: 'rgba(0,0,0,0.04)', padding: 5, borderRadius: 7 }; +const fileStyle = { marginBottom: 20, padding: 5 }; const StreamsSelector = memo(({ mainFilePath, mainFileFormatData, streams: mainFileStreams, isCopyingStreamId, toggleCopyStreamId, @@ -273,7 +273,7 @@ const StreamsSelector = memo(({

    {t('Click to select which tracks to keep when exporting:')}

    -
    + {/* We only support editing main file metadata for now */} setEditingFile(mainFilePath)} /> @@ -293,10 +293,10 @@ const StreamsSelector = memo(({ ))}
    -
    + {externalFilesEntries.map(([path, { streams, formatData }]) => ( -
    + removeFile(path)} /> @@ -314,36 +314,32 @@ const StreamsSelector = memo(({ ))}
    -
    + ))} - {externalFilesEntries.length > 0 && ( -
    -
    - {t('When tracks have different lengths, do you want to make the output file as long as the longest or the shortest track?')} -
    - setShortestFlag(value === 'shortest')} - /> -
    - )} + -
    - {t('Include more tracks from other file')} -
    + {externalFilesEntries.length === 0 && ( + + )} {nonCopiedExtraStreams.length > 0 && (
    - {t('Discard or extract unprocessable tracks to separate files?')} + {t('Discard or extract unprocessable tracks to separate files?')}
    )} - {externalFilesEntries.length === 0 && ( -
    - {t('Export each track as individual files')} + {externalFilesEntries.length > 0 && ( +
    + {t('When tracks have different lengths, do you want to make the output file as long as the longest or the shortest track?')} +
    )}
    diff --git a/src/TopMenu.jsx b/src/TopMenu.jsx index d3509420..010dab5d 100644 --- a/src/TopMenu.jsx +++ b/src/TopMenu.jsx @@ -1,7 +1,7 @@ import React, { memo } from 'react'; import { IoIosHelpCircle, IoIosSettings } from 'react-icons/io'; import { FaLock, FaUnlock } from 'react-icons/fa'; -import { IconButton, Button, CrossIcon } from 'evergreen-ui'; +import { IconButton, Button, CrossIcon, ListIcon, VolumeUpIcon, VolumeOffIcon } from 'evergreen-ui'; import { useTranslation } from 'react-i18next'; import MergeExportButton from './components/MergeExportButton'; @@ -29,12 +29,12 @@ const TopMenu = memo(({ <> {filePath && ( <> -