kopia lustrzana https://github.com/mifi/lossless-cut
allow using external ffmpeg executable #400
rodzic
402b8290af
commit
8578886f5c
|
@ -105,6 +105,7 @@ const defaults = {
|
|||
windowBounds: undefined,
|
||||
enableAutoHtml5ify: true,
|
||||
keyBindings: defaultKeyBindings,
|
||||
customFfPath: undefined,
|
||||
};
|
||||
|
||||
// For portable app: https://github.com/mifi/lossless-cut/issues/645
|
||||
|
|
17
src/App.jsx
17
src/App.jsx
|
@ -53,7 +53,7 @@ import { captureFrameFromTag, captureFrameFfmpeg } from './capture-frame';
|
|||
import {
|
||||
getStreamFps, isCuttingStart, isCuttingEnd,
|
||||
readFileMeta, getSmarterOutFormat, renderThumbnails as ffmpegRenderThumbnails,
|
||||
extractStreams, runStartupCheck,
|
||||
extractStreams, runStartupCheck, setCustomFfPath as ffmpegSetCustomFfPath,
|
||||
isIphoneHevc, tryMapChaptersToEdl,
|
||||
getDuration, getTimecodeFromStreams, createChaptersFromSegments, extractSubtitleTrack,
|
||||
} from './ffmpeg';
|
||||
|
@ -197,9 +197,13 @@ const App = memo(() => {
|
|||
const zoomedDuration = isDurationValid(duration) ? duration / zoom : undefined;
|
||||
|
||||
const {
|
||||
captureFormat, setCaptureFormat, customOutDir, setCustomOutDir, keyframeCut, setKeyframeCut, preserveMovData, setPreserveMovData, movFastStart, setMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoMerge, setAutoMerge, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, setAutoExportExtraStreams, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, setAutoSaveProjectFile, wheelSensitivity, setWheelSensitivity, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, autoDeleteMergedSegments, setAutoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, simpleMode, setSimpleMode, outSegTemplate, setOutSegTemplate, keyboardSeekAccFactor, setKeyboardSeekAccFactor, keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed, enableTransferTimestamps, setEnableTransferTimestamps, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, enableAutoHtml5ify, setEnableAutoHtml5ify, segmentsToChaptersOnly, setSegmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, setEnableSmartCut,
|
||||
captureFormat, setCaptureFormat, customOutDir, setCustomOutDir, keyframeCut, setKeyframeCut, preserveMovData, setPreserveMovData, movFastStart, setMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoMerge, setAutoMerge, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, setAutoExportExtraStreams, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, setAutoSaveProjectFile, wheelSensitivity, setWheelSensitivity, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, autoDeleteMergedSegments, setAutoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, simpleMode, setSimpleMode, outSegTemplate, setOutSegTemplate, keyboardSeekAccFactor, setKeyboardSeekAccFactor, keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed, enableTransferTimestamps, setEnableTransferTimestamps, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, enableAutoHtml5ify, setEnableAutoHtml5ify, segmentsToChaptersOnly, setSegmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, setEnableSmartCut, customFfPath, setCustomFfPath,
|
||||
} = useUserSettingsRoot();
|
||||
|
||||
useEffect(() => {
|
||||
ffmpegSetCustomFfPath(customFfPath);
|
||||
}, [customFfPath]);
|
||||
|
||||
const {
|
||||
concatFiles, html5ifyDummy, cutMultiple, autoConcatCutSegments, html5ify, fixInvalidDuration,
|
||||
} = useFfmpegOperations({ filePath, enableTransferTimestamps });
|
||||
|
@ -741,8 +745,8 @@ const App = memo(() => {
|
|||
}), [hideAllNotifications, setSimpleMode]);
|
||||
|
||||
const userSettingsContext = useMemo(() => ({
|
||||
captureFormat, setCaptureFormat, toggleCaptureFormat, customOutDir, setCustomOutDir, changeOutDir, keyframeCut, setKeyframeCut, toggleKeyframeCut, preserveMovData, setPreserveMovData, togglePreserveMovData, movFastStart, setMovFastStart, toggleMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoMerge, setAutoMerge, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, setAutoExportExtraStreams, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, setAutoSaveProjectFile, wheelSensitivity, setWheelSensitivity, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, autoDeleteMergedSegments, setAutoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, toggleSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, togglePreserveMetadataOnMerge, simpleMode, setSimpleMode, toggleSimpleMode, outSegTemplate, setOutSegTemplate, keyboardSeekAccFactor, setKeyboardSeekAccFactor, keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed, enableTransferTimestamps, setEnableTransferTimestamps, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, toggleSafeOutputFileName, enableAutoHtml5ify, setEnableAutoHtml5ify, segmentsToChaptersOnly, setSegmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, setEnableSmartCut,
|
||||
}), [askBeforeClose, autoDeleteMergedSegments, autoExportExtraStreams, autoLoadTimecode, autoMerge, autoSaveProjectFile, avoidNegativeTs, captureFormat, changeOutDir, customOutDir, enableAskForFileOpenAction, enableAskForImportChapters, enableAutoHtml5ify, enableSmartCut, enableTransferTimestamps, exportConfirmEnabled, ffmpegExperimental, hideNotifications, invertCutSegments, invertTimelineScroll, keyBindings, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyframeCut, language, movFastStart, outFormatLocked, outSegTemplate, playbackVolume, preserveMetadataOnMerge, preserveMovData, resetKeyBindings, safeOutputFileName, segmentsToChapters, segmentsToChaptersOnly, setAskBeforeClose, setAutoDeleteMergedSegments, setAutoExportExtraStreams, setAutoLoadTimecode, setAutoMerge, setAutoSaveProjectFile, setAvoidNegativeTs, setCaptureFormat, setCustomOutDir, setEnableAskForFileOpenAction, setEnableAskForImportChapters, setEnableAutoHtml5ify, setEnableSmartCut, setEnableTransferTimestamps, setExportConfirmEnabled, setFfmpegExperimental, setHideNotifications, setInvertCutSegments, setInvertTimelineScroll, setKeyBindings, setKeyboardNormalSeekSpeed, setKeyboardSeekAccFactor, setKeyframeCut, setLanguage, setMovFastStart, setOutFormatLocked, setOutSegTemplate, setPlaybackVolume, setPreserveMetadataOnMerge, setPreserveMovData, setSafeOutputFileName, setSegmentsToChapters, setSegmentsToChaptersOnly, setSimpleMode, setTimecodeFormat, setWheelSensitivity, simpleMode, timecodeFormat, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleMovFastStart, togglePreserveMetadataOnMerge, togglePreserveMovData, toggleSafeOutputFileName, toggleSegmentsToChapters, toggleSimpleMode, wheelSensitivity]);
|
||||
captureFormat, setCaptureFormat, toggleCaptureFormat, customOutDir, setCustomOutDir, changeOutDir, keyframeCut, setKeyframeCut, toggleKeyframeCut, preserveMovData, setPreserveMovData, togglePreserveMovData, movFastStart, setMovFastStart, toggleMovFastStart, avoidNegativeTs, setAvoidNegativeTs, autoMerge, setAutoMerge, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, autoExportExtraStreams, setAutoExportExtraStreams, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, playbackVolume, setPlaybackVolume, autoSaveProjectFile, setAutoSaveProjectFile, wheelSensitivity, setWheelSensitivity, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, autoDeleteMergedSegments, setAutoDeleteMergedSegments, exportConfirmEnabled, setExportConfirmEnabled, toggleExportConfirmEnabled, segmentsToChapters, setSegmentsToChapters, toggleSegmentsToChapters, preserveMetadataOnMerge, setPreserveMetadataOnMerge, togglePreserveMetadataOnMerge, simpleMode, setSimpleMode, toggleSimpleMode, outSegTemplate, setOutSegTemplate, keyboardSeekAccFactor, setKeyboardSeekAccFactor, keyboardNormalSeekSpeed, setKeyboardNormalSeekSpeed, enableTransferTimestamps, setEnableTransferTimestamps, outFormatLocked, setOutFormatLocked, safeOutputFileName, setSafeOutputFileName, toggleSafeOutputFileName, enableAutoHtml5ify, setEnableAutoHtml5ify, segmentsToChaptersOnly, setSegmentsToChaptersOnly, keyBindings, setKeyBindings, resetKeyBindings, enableSmartCut, setEnableSmartCut, customFfPath, setCustomFfPath,
|
||||
}), [askBeforeClose, autoDeleteMergedSegments, autoExportExtraStreams, autoLoadTimecode, autoMerge, autoSaveProjectFile, avoidNegativeTs, captureFormat, changeOutDir, customFfPath, customOutDir, enableAskForFileOpenAction, enableAskForImportChapters, enableAutoHtml5ify, enableSmartCut, enableTransferTimestamps, exportConfirmEnabled, ffmpegExperimental, hideNotifications, invertCutSegments, invertTimelineScroll, keyBindings, keyboardNormalSeekSpeed, keyboardSeekAccFactor, keyframeCut, language, movFastStart, outFormatLocked, outSegTemplate, playbackVolume, preserveMetadataOnMerge, preserveMovData, resetKeyBindings, safeOutputFileName, segmentsToChapters, segmentsToChaptersOnly, setAskBeforeClose, setAutoDeleteMergedSegments, setAutoExportExtraStreams, setAutoLoadTimecode, setAutoMerge, setAutoSaveProjectFile, setAvoidNegativeTs, setCaptureFormat, setCustomFfPath, setCustomOutDir, setEnableAskForFileOpenAction, setEnableAskForImportChapters, setEnableAutoHtml5ify, setEnableSmartCut, setEnableTransferTimestamps, setExportConfirmEnabled, setFfmpegExperimental, setHideNotifications, setInvertCutSegments, setInvertTimelineScroll, setKeyBindings, setKeyboardNormalSeekSpeed, setKeyboardSeekAccFactor, setKeyframeCut, setLanguage, setMovFastStart, setOutFormatLocked, setOutSegTemplate, setPlaybackVolume, setPreserveMetadataOnMerge, setPreserveMovData, setSafeOutputFileName, setSegmentsToChapters, setSegmentsToChaptersOnly, setSimpleMode, setTimecodeFormat, setWheelSensitivity, simpleMode, timecodeFormat, toggleCaptureFormat, toggleExportConfirmEnabled, toggleKeyframeCut, toggleMovFastStart, togglePreserveMetadataOnMerge, togglePreserveMovData, toggleSafeOutputFileName, toggleSegmentsToChapters, toggleSimpleMode, wheelSensitivity]);
|
||||
|
||||
const isCopyingStreamId = useCallback((path, streamId) => (
|
||||
!!(copyStreamIdsByFile[path] || {})[streamId]
|
||||
|
@ -2124,9 +2128,10 @@ const App = memo(() => {
|
|||
if (!isStoreBuild) loadMifiLink().then(setMifiLink);
|
||||
}, []);
|
||||
|
||||
const haveCustomFfPath = !!customFfPath;
|
||||
useEffect(() => {
|
||||
runStartupCheck().catch((err) => handleError('LosslessCut is installation is broken', err));
|
||||
}, []);
|
||||
if (!haveCustomFfPath) runStartupCheck().catch((err) => handleError('LosslessCut is installation is broken', err));
|
||||
}, [haveCustomFfPath]);
|
||||
|
||||
useEffect(() => {
|
||||
const keyScrollPreventer = (e) => {
|
||||
|
|
|
@ -2,9 +2,12 @@ import React, { memo, useCallback, useMemo } from 'react';
|
|||
import { FaYinYang, FaKeyboard } from 'react-icons/fa';
|
||||
import { Button, Table, NumericalIcon, KeyIcon, FolderCloseIcon, DocumentIcon, TimeIcon, Checkbox, Select } from 'evergreen-ui';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import CaptureFormatButton from './components/CaptureFormatButton';
|
||||
import AutoExportToggler from './components/AutoExportToggler';
|
||||
import useUserSettings from './hooks/useUserSettings';
|
||||
import { askForFfPath } from './dialogs';
|
||||
import { isMasBuild } from './util';
|
||||
|
||||
|
||||
// https://www.electronjs.org/docs/api/locales
|
||||
|
@ -44,7 +47,7 @@ const Settings = memo(({
|
|||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { customOutDir, changeOutDir, keyframeCut, toggleKeyframeCut, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, autoSaveProjectFile, setAutoSaveProjectFile, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, enableTransferTimestamps, setEnableTransferTimestamps, enableAutoHtml5ify, setEnableAutoHtml5ify } = useUserSettings();
|
||||
const { customOutDir, changeOutDir, keyframeCut, toggleKeyframeCut, timecodeFormat, setTimecodeFormat, invertCutSegments, setInvertCutSegments, askBeforeClose, setAskBeforeClose, enableAskForImportChapters, setEnableAskForImportChapters, enableAskForFileOpenAction, setEnableAskForFileOpenAction, autoSaveProjectFile, setAutoSaveProjectFile, invertTimelineScroll, setInvertTimelineScroll, language, setLanguage, ffmpegExperimental, setFfmpegExperimental, hideNotifications, setHideNotifications, autoLoadTimecode, setAutoLoadTimecode, enableTransferTimestamps, setEnableTransferTimestamps, enableAutoHtml5ify, setEnableAutoHtml5ify, customFfPath, setCustomFfPath } = useUserSettings();
|
||||
|
||||
const onLangChange = useCallback((e) => {
|
||||
const { value } = e.target;
|
||||
|
@ -66,6 +69,11 @@ const Settings = memo(({
|
|||
setTimecodeFormat(keys[index]);
|
||||
}, [setTimecodeFormat, timecodeFormat, timecodeFormatOptions]);
|
||||
|
||||
const changeCustomFfPath = useCallback(async () => {
|
||||
const newCustomFfPath = await askForFfPath(customFfPath);
|
||||
setCustomFfPath(newCustomFfPath);
|
||||
}, [customFfPath, setCustomFfPath]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Row>
|
||||
|
@ -98,6 +106,21 @@ const Settings = memo(({
|
|||
</Table.TextCell>
|
||||
</Row>
|
||||
|
||||
{!isMasBuild && (
|
||||
<Row>
|
||||
<KeyCell>
|
||||
{t('Custom FFmpeg directory (experimental)')}<br />
|
||||
{t('This allows you to specify custom FFmpeg and FFprobe binaries to use. Make sure the "ffmpeg" and "ffprobe" executables exist in the same directory, and then select the directory.')}
|
||||
</KeyCell>
|
||||
<Table.TextCell>
|
||||
<Button iconBefore={customFfPath ? FolderCloseIcon : DocumentIcon} onClick={changeCustomFfPath}>
|
||||
{customFfPath ? t('Using external ffmpeg') : t('Using built-in ffmpeg')}
|
||||
</Button>
|
||||
<div>{customFfPath}</div>
|
||||
</Table.TextCell>
|
||||
</Row>
|
||||
)}
|
||||
|
||||
<Row>
|
||||
<KeyCell>{t('Set file modification date/time of output files to:')}</KeyCell>
|
||||
<Table.TextCell>
|
||||
|
|
|
@ -138,6 +138,15 @@ export async function askForOutDir(defaultPath) {
|
|||
return (filePaths && filePaths.length === 1) ? filePaths[0] : undefined;
|
||||
}
|
||||
|
||||
export async function askForFfPath(defaultPath) {
|
||||
const { filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory'],
|
||||
defaultPath,
|
||||
title: i18n.t('Select custom FFmpeg directory'),
|
||||
});
|
||||
return (filePaths && filePaths.length === 1) ? filePaths[0] : undefined;
|
||||
}
|
||||
|
||||
export async function askForFileOpenAction(inputOptions) {
|
||||
const { value } = await Swal.fire({
|
||||
text: i18n.t('You opened a new file. What do you want to do?'),
|
||||
|
|
|
@ -15,6 +15,12 @@ const readChunk = window.require('read-chunk');
|
|||
const readline = window.require('readline');
|
||||
const isDev = window.require('electron-is-dev');
|
||||
|
||||
let customFfPath;
|
||||
|
||||
// Note that this does not work on MAS because of sandbox restrictions
|
||||
export function setCustomFfPath(path) {
|
||||
customFfPath = path;
|
||||
}
|
||||
|
||||
export function getFfCommandLine(cmd, args) {
|
||||
const mapArg = arg => (/[^0-9a-zA-Z-_]/.test(arg) ? `'${arg}'` : arg);
|
||||
|
@ -24,7 +30,8 @@ export function getFfCommandLine(cmd, args) {
|
|||
function getFfPath(cmd) {
|
||||
const exeName = isWindows ? `${cmd}.exe` : cmd;
|
||||
|
||||
if (isDev) return `ffmpeg/${platform}/${exeName}`;
|
||||
if (customFfPath) return join(customFfPath, exeName);
|
||||
if (isDev) return join('ffmpeg', platform, exeName);
|
||||
return join(window.process.resourcesPath, exeName);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,6 +104,8 @@ export default () => {
|
|||
useEffect(() => safeSetConfig('keyBindings', keyBindings), [keyBindings]);
|
||||
const [enableSmartCut, setEnableSmartCut] = useState(safeGetConfig('enableSmartCut'));
|
||||
useEffect(() => safeSetConfig('enableSmartCut', enableSmartCut), [enableSmartCut]);
|
||||
const [customFfPath, setCustomFfPath] = useState(safeGetConfig('customFfPath'));
|
||||
useEffect(() => safeSetConfig('customFfPath', customFfPath), [customFfPath]);
|
||||
|
||||
const resetKeyBindings = useCallback(() => {
|
||||
configStore.reset('keyBindings');
|
||||
|
@ -192,5 +194,7 @@ export default () => {
|
|||
resetKeyBindings,
|
||||
enableSmartCut,
|
||||
setEnableSmartCut,
|
||||
customFfPath,
|
||||
setCustomFfPath,
|
||||
};
|
||||
};
|
||||
|
|
Ładowanie…
Reference in New Issue