allow using external ffmpeg executable #400

pull/901/head
Mikael Finstad 2022-03-01 14:30:30 +08:00
rodzic 402b8290af
commit 8578886f5c
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 25AB36E3E81CBC26
6 zmienionych plików z 57 dodań i 8 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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) => {

Wyświetl plik

@ -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>

Wyświetl plik

@ -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?'),

Wyświetl plik

@ -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);
}

Wyświetl plik

@ -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,
};
};