diff --git a/.eslintrc b/.eslintrc index 50a9f078..65734688 100644 --- a/.eslintrc +++ b/.eslintrc @@ -22,6 +22,11 @@ "arrow-parens": 0, "jsx-a11y/control-has-associated-label": 0, "react/prop-types": 0, - "no-multiple-empty-lines": ["error", { "max": 2, "maxBOF": 0, "maxEOF": 0 }] + "no-multiple-empty-lines": ["error", { "max": 2, "maxBOF": 0, "maxEOF": 0 }], + "no-promise-executor-return": 0, + "react/function-component-definition": 0 + }, + "parserOptions": { + "ecmaVersion": 2022 } } diff --git a/icon-gen.js b/icon-gen.js deleted file mode 100644 index e1bc3a40..00000000 --- a/icon-gen.js +++ /dev/null @@ -1,24 +0,0 @@ -const sharp = require('sharp'); -const icongen = require('icon-gen'); - -const svg2png = (from, to, width, height) => sharp(from) - .png() - .resize(width, height, { - fit: sharp.fit.contain, - background: { r: 0, g: 0, b: 0, alpha: 0 }, - }) - .toFile(to); - -(async () => { - await svg2png('src/icon.svg', './icon-build/app-512.png', 512, 512); - await svg2png('src/icon.svg', './build-resources/appx/StoreLogo.png', 50, 50); - await svg2png('src/icon.svg', './build-resources/appx/Square150x150Logo.png', 300, 300); - await svg2png('src/icon.svg', './build-resources/appx/Square44x44Logo.png', 44, 44); - await svg2png('src/icon.svg', './build-resources/appx/Wide310x150Logo.png', 620, 300); - - await icongen('./src/icon.svg', './icon-build', { icns: { sizes: [512, 1024] } }); - - // https://github.com/mifi/lossless-cut/issues/778 - // https://stackoverflow.com/questions/3236115/which-icon-sizes-should-my-windows-applications-icon-include - await icongen('./src/icon.svg', './icon-build', { ico: { sizes: [16, 24, 32, 40, 48, 64, 96, 128, 256, 512] } }); -})(); diff --git a/package.json b/package.json index 54c3c699..08cabc03 100644 --- a/package.json +++ b/package.json @@ -10,14 +10,14 @@ "start": "concurrently -k \"npm run start:frontend\" \"npm run start:electron\"", "start:frontend": "cross-env BROWSER=none PORT=3001 DISABLE_ESLINT_PLUGIN=true react-scripts start", "start:electron": "wait-on http://localhost:3001 && electron .", - "icon-gen": "mkdirp icon-build build-resources/appx && node icon-gen.js", - "download-ffmpeg-mac": "mkdir -p ffmpeg/darwin && cd ffmpeg/darwin && wget https://github.com/mifi/ffmpeg-build-script/releases/download/n4.4.1/ffmpeg -O ffmpeg && wget https://github.com/mifi/ffmpeg-build-script/releases/download/n4.4.1/ffprobe -O ffprobe && chmod +x ffmpeg && chmod +x ffprobe", - "download-ffmpeg-linux": "mkdir -p ffmpeg/linux && cd ffmpeg/linux && wget https://github.com/mifi/ffmpeg-builds/releases/download/4.4.1/ffmpeg-release-amd64-static.tar.xz -O ffmpeg.xz && tar xvf ffmpeg.xz ffmpeg-4.4.1-amd64-static/ffmpeg ffmpeg-4.4.1-amd64-static/ffprobe --strip-components=1", - "download-ffmpeg-windows": "npx shx mkdir -p ffmpeg/win32 && cd ffmpeg/win32 && npx download-cli https://github.com/mifi/ffmpeg-builds/releases/download/4.4.1/ffmpeg-n4.4.1-2-gcc33e73618-win64-gpl-4.4.zip --out . --filename ffmpeg.zip && 7z x ffmpeg.zip && npx shx mv ffmpeg-n4.4.1-2-gcc33e73618-win64-gpl-4.4/bin/ffmpeg.exe ./ && npx shx mv ffmpeg-n4.4.1-2-gcc33e73618-win64-gpl-4.4/bin/ffprobe.exe ./", + "icon-gen": "mkdirp icon-build build-resources/appx && node script/icon-gen.mjs", + "download-ffmpeg-mac": "mkdirp ffmpeg/darwin && cd ffmpeg/darwin && wget https://github.com/mifi/ffmpeg-build-script/releases/download/n4.4.1/ffmpeg -O ffmpeg && wget https://github.com/mifi/ffmpeg-build-script/releases/download/n4.4.1/ffprobe -O ffprobe && chmod +x ffmpeg && chmod +x ffprobe", + "download-ffmpeg-linux": "mkdirp ffmpeg/linux && cd ffmpeg/linux && wget https://github.com/mifi/ffmpeg-builds/releases/download/4.4.1/ffmpeg-release-amd64-static.tar.xz -O ffmpeg.xz && tar xvf ffmpeg.xz ffmpeg-4.4.1-amd64-static/ffmpeg ffmpeg-4.4.1-amd64-static/ffprobe --strip-components=1", + "download-ffmpeg-windows": "mkdirp ffmpeg/win32 && cd ffmpeg/win32 && npx download-cli https://github.com/mifi/ffmpeg-builds/releases/download/4.4.1/ffmpeg-n4.4.1-2-gcc33e73618-win64-gpl-4.4.zip --out . --filename ffmpeg.zip && 7z x ffmpeg.zip && npx shx mv ffmpeg-n4.4.1-2-gcc33e73618-win64-gpl-4.4/bin/ffmpeg.exe ./ && npx shx mv ffmpeg-n4.4.1-2-gcc33e73618-win64-gpl-4.4/bin/ffprobe.exe ./", "build": "yarn icon-gen && react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", - "lint": "eslint --ext .jsx --ext .js .", + "lint": "eslint --ext .jsx --ext .js . --ext .mjs", "pack-mac": "electron-builder --mac -m dmg", "prepack-mac": "yarn build", "pack-mas-dev": "electron-builder --mac -m mas-dev -c.mas.provisioningProfile=LosslessCut_Dev.provisionprofile -c.mas.identity='Apple Development: Mikael Finstad (JH4PH8B3C8)'", @@ -54,7 +54,7 @@ "electron-builder-notarize": "1.1.2", "electron-devtools-installer": "^3.1.1", "eslint": "^7.32.0 || ^8.2.0", - "eslint-config-airbnb": "^18.2.1", + "eslint-config-airbnb": "^19.0.4", "eslint-plugin-import": "^2.25.3", "eslint-plugin-jsx-a11y": "^6.5.1", "eslint-plugin-react": "^7.28.0", diff --git a/script/icon-gen.mjs b/script/icon-gen.mjs new file mode 100644 index 00000000..6d77beff --- /dev/null +++ b/script/icon-gen.mjs @@ -0,0 +1,22 @@ +import sharp from 'sharp'; +import icongen from 'icon-gen'; + +const svg2png = (from, to, width, height) => sharp(from) + .png() + .resize(width, height, { + fit: sharp.fit.contain, + background: { r: 0, g: 0, b: 0, alpha: 0 }, + }) + .toFile(to); + +await svg2png('src/icon.svg', './icon-build/app-512.png', 512, 512); +await svg2png('src/icon.svg', './build-resources/appx/StoreLogo.png', 50, 50); +await svg2png('src/icon.svg', './build-resources/appx/Square150x150Logo.png', 300, 300); +await svg2png('src/icon.svg', './build-resources/appx/Square44x44Logo.png', 44, 44); +await svg2png('src/icon.svg', './build-resources/appx/Wide310x150Logo.png', 620, 300); + +await icongen('./src/icon.svg', './icon-build', { icns: { sizes: [512, 1024] } }); + +// https://github.com/mifi/lossless-cut/issues/778 +// https://stackoverflow.com/questions/3236115/which-icon-sizes-should-my-windows-applications-icon-include +await icongen('./src/icon.svg', './icon-build', { ico: { sizes: [16, 24, 32, 40, 48, 64, 96, 128, 256, 512] } }); diff --git a/src/App.jsx b/src/App.jsx index 1fbe0968..dfa9c7a4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1092,8 +1092,9 @@ const App = memo(() => { } }, [previewFilePath, filePath, edlFilePath, cleanupChoices, isFileOpened, resetState, batchRemoveFile, setWorking]); - const inverseOrNormalSegments = useMemo(() => (invertCutSegments ? inverseCutSegments : apparentCutSegments), - [invertCutSegments, inverseCutSegments, apparentCutSegments]); + const inverseOrNormalSegments = useMemo(() => ( + invertCutSegments ? inverseCutSegments : apparentCutSegments + ), [invertCutSegments, inverseCutSegments, apparentCutSegments]); const enabledSegmentsRaw = useMemo(() => { // For invertCutSegments we do not support filtering diff --git a/src/Canvas.jsx b/src/Canvas.jsx index 6c597fa3..46772e8f 100644 --- a/src/Canvas.jsx +++ b/src/Canvas.jsx @@ -6,8 +6,7 @@ import CanvasPlayer from './CanvasPlayer'; const Canvas = memo(({ rotate, filePath, width, height, playerTime, streamIndex, commandedTime, playing }) => { const canvasRef = useRef(); - const canvasPlayer = useMemo(() => CanvasPlayer({ path: filePath, width, height, streamIndex }), - [filePath, width, height, streamIndex]); + const canvasPlayer = useMemo(() => CanvasPlayer({ path: filePath, width, height, streamIndex }), [filePath, width, height, streamIndex]); useEffect(() => { canvasPlayer.setCanvas(canvasRef.current); diff --git a/src/HelpSheet.jsx b/src/HelpSheet.jsx index 68ce804d..e1b64b0f 100644 --- a/src/HelpSheet.jsx +++ b/src/HelpSheet.jsx @@ -34,7 +34,7 @@ const HelpSheet = memo(({ visible, onTogglePress, ffmpegCommandLog, onKeyboardSh electron.shell.openExternal(githubLink)}>{githubLink}

- +

{t('Hover mouse over buttons in the main interface to see which function they have')}

diff --git a/src/Settings.jsx b/src/Settings.jsx index 0c7e49e0..361ea2c0 100644 --- a/src/Settings.jsx +++ b/src/Settings.jsx @@ -30,6 +30,10 @@ const langNames = { ko: '한국어', }; +// eslint-disable-next-line react/jsx-props-no-spreading +const Row = (props) => ; +// eslint-disable-next-line react/jsx-props-no-spreading +const KeyCell = (props) => ; const Settings = memo(({ changeOutDir, customOutDir, keyframeCut, setKeyframeCut, invertCutSegments, setInvertCutSegments, @@ -43,11 +47,6 @@ const Settings = memo(({ }) => { const { t } = useTranslation(); - // eslint-disable-next-line react/jsx-props-no-spreading - const Row = (props) => ; - // eslint-disable-next-line react/jsx-props-no-spreading - const KeyCell = (props) => ; - const onLangChange = useCallback((e) => { const { value } = e.target; const l = value !== '' ? value : undefined; @@ -83,7 +82,7 @@ const Settings = memo(({ {t('Keyboard & mouse shortcuts')} - + diff --git a/src/StreamsSelector.jsx b/src/StreamsSelector.jsx index 3212991d..9463957e 100644 --- a/src/StreamsSelector.jsx +++ b/src/StreamsSelector.jsx @@ -235,7 +235,7 @@ const Stream = memo(({ filePath, stream, onToggle, batchSetCopyStreamIds, copySt return ( - } onClick={onClick} /> + } onClick={onClick} />
{stream.index}
@@ -250,7 +250,7 @@ const Stream = memo(({ filePath, stream, onToggle, batchSetCopyStreamIds, copySt {stream.width && stream.height && `${stream.width}x${stream.height}`} {stream.channels && `${stream.channels}c`} {stream.channel_layout} {streamFps && `${streamFps.toFixed(2)}fps`} onInfoClick(stream, t('Track info'))} appearance="minimal" iconSize={18} /> - } onClick={onExtractStreamPress} appearance="minimal" iconSize={18} /> + } onClick={onExtractStreamPress} appearance="minimal" iconSize={18} /> - } intent="success" onClick={() => batchSetCopyStreamIds((s) => s.codec_type === stream.codec_type, true)}> + } intent="success" onClick={() => batchSetCopyStreamIds((s) => s.codec_type === stream.codec_type, true)}> {t('Keep all {{type}} tracks', { type: stream.codec_type })} - } intent="danger" onClick={() => batchSetCopyStreamIds((s) => s.codec_type === stream.codec_type, false)}> + } intent="danger" onClick={() => batchSetCopyStreamIds((s) => s.codec_type === stream.codec_type, false)}> {t('Discard all {{type}} tracks', { type: stream.codec_type })} @@ -293,9 +293,9 @@ const FileHeading = ({ path, formatData, chapters, onTrashClick, onEditClick, se {chapters && chapters.length > 0 && onInfoClick(chapters, t('Chapters'))} appearance="minimal" iconSize={18} />} {onEditClick && } {onTrashClick && } - } onClick={() => setCopyAllStreams(true)} appearance="minimal" /> - } onClick={() => setCopyAllStreams(false)} appearance="minimal" /> - {onExtractAllStreamsPress && } onClick={onExtractAllStreamsPress} appearance="minimal" />} + } onClick={() => setCopyAllStreams(true)} appearance="minimal" /> + } onClick={() => setCopyAllStreams(false)} appearance="minimal" /> + {onExtractAllStreamsPress && } onClick={onExtractAllStreamsPress} appearance="minimal" />} ); }; @@ -424,7 +424,7 @@ const StreamsSelector = memo(({ {t('Note: Cutting and including external tracks at the same time does not yet work. If you want to do both, it must be done as separate operations. See github issue #896.')} )} - diff --git a/src/components/MergeExportButton.jsx b/src/components/MergeExportButton.jsx index 6aa8f70e..d8a1bbab 100644 --- a/src/components/MergeExportButton.jsx +++ b/src/components/MergeExportButton.jsx @@ -66,7 +66,7 @@ const MergeExportButton = memo(({ autoMerge, enabledSegments, setAutoMerge, auto style={{ minWidth: 120, textAlign: 'center', opacity: enabledSegments && enabledSegments.length < 2 ? 0.4 : undefined }} title={description} onClick={withBlur(onClick)} - iconBefore={AutoMergeIcon && (() => )} + iconBefore={AutoMergeIcon && } > {title} diff --git a/src/hooks/useFfmpegOperations.js b/src/hooks/useFfmpegOperations.js index 0c2d4108..bd6033af 100644 --- a/src/hooks/useFfmpegOperations.js +++ b/src/hooks/useFfmpegOperations.js @@ -123,12 +123,11 @@ function useFfmpegOperations({ filePath, enableTransferTimestamps }) { return streamCount + copiedStreamIndex; } - const lessDeepMap = (root, fn) => flatMapDeep( + const lessDeepMap = (root, fn) => flatMapDeep(( Object.entries(root), ([path, streamsMap]) => ( Object.entries(streamsMap || {}).map(([streamId, value]) => ( fn(path, streamId, value) - ))), - ); + ))))); // The structure is deep! file -> stream -> key -> value Example: { 'file.mp4': { 0: { key: 'value' } } } const deepMap = (root, fn) => lessDeepMap(root, (path, streamId, tagsMap) => ( diff --git a/src/index.jsx b/src/index.jsx index 6b0c6d74..7fb8bd9f 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -14,10 +14,12 @@ const electron = window.require('electron'); console.log('Version', electron.remote.app.getVersion()); -ReactDOM.render(( - - }> - - - -), document.getElementById('root')); +ReactDOM.render( + ( + + }> + + + + ), document.getElementById('root'), +); diff --git a/yarn.lock b/yarn.lock index 37a0d0f5..a75b1186 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4744,23 +4744,24 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-airbnb-base@^14.2.1: - version "14.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e" - integrity sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA== +eslint-config-airbnb-base@^15.0.0: + version "15.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz#6b09add90ac79c2f8d723a2580e07f3925afd236" + integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== dependencies: confusing-browser-globals "^1.0.10" object.assign "^4.1.2" - object.entries "^1.1.2" + object.entries "^1.1.5" + semver "^6.3.0" -eslint-config-airbnb@^18.2.1: - version "18.2.1" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.2.1.tgz#b7fe2b42f9f8173e825b73c8014b592e449c98d9" - integrity sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg== +eslint-config-airbnb@^19.0.4: + version "19.0.4" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz#84d4c3490ad70a0ffa571138ebcdea6ab085fdc3" + integrity sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew== dependencies: - eslint-config-airbnb-base "^14.2.1" + eslint-config-airbnb-base "^15.0.0" object.assign "^4.1.2" - object.entries "^1.1.2" + object.entries "^1.1.5" eslint-config-react-app@^7.0.0: version "7.0.0" @@ -8146,7 +8147,7 @@ object.assign@^4.0.4, object.assign@^4.1.0, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" -object.entries@^1.1.2, object.entries@^1.1.5: +object.entries@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==