- Change wording from cut away to remove
- separate settings
- show popup when switching mode
pull/276/head
Mikael Finstad 2020-02-28 19:00:30 +08:00
rodzic 6ef45af2d1
commit d3334f1d04
8 zmienionych plików z 118 dodań i 55 usunięć

Wyświetl plik

@ -88,7 +88,7 @@ Unsupported files can still be remuxed (fast) or encoded (slow) to a friendly fo
## Typical workflow
- Drag drop a video file into player or use <kbd></kbd>/<kbd>CTRL</kbd>+<kbd>O</kbd>.
- Press <kbd>SPACE</kbd> to play/pause or <kbd></kbd><kbd></kbd>, <kbd>,</kbd><kbd>.</kbd> or mouse/trackpad wheel to seek back/forth
- Select the cut segment's start and end time by moving the time marker and then pressing <kbd>I</kbd> to set start time, and <kbd>O</kbd> to set end time. *Note that the segments you select will be **preserved** and exported to a new file. You can change this behavior with the Yin Yang symbol ☯️, in which case it will instead **cut away** all selected segments and export the parts between.*
- Select the cut segment's start and end time by moving the time marker and then pressing <kbd>I</kbd> to set start time, and <kbd>O</kbd> to set end time. *Note that the segments you select will be **preserved** and exported to a new file. You can change this behavior with the Yin Yang symbol ☯️, in which case it will instead **remove** all selected segments and export the parts between.*
- *(optional)* If you want to add more than one segment, move to the desired start time and press <kbd>+</kbd>, then select the next segment start/end times with <kbd>I</kbd>/<kbd>O</kbd>.
- *(optional)* If you want to re-merge all the selected segments to one file after cutting, toggle the button `Separate files` to `Merge cuts`.
- *(optional)* If you want to export to a certain dir, press the `Working dir unset` button (default: Input file path)

Wyświetl plik

@ -2,7 +2,6 @@ import React, { memo } from 'react';
import { IoIosCloseCircleOutline } from 'react-icons/io';
import { FaClipboard } from 'react-icons/fa';
import { motion, AnimatePresence } from 'framer-motion';
import { Table } from 'evergreen-ui';
// eslint-disable-next-line import/no-extraneous-dependencies
const { clipboard } = require('electron');
@ -10,7 +9,7 @@ const { clipboard } = require('electron');
const { toast } = require('./util');
const HelpSheet = memo(({
visible, onTogglePress, renderSettings, ffmpegCommandLog,
visible, onTogglePress, ffmpegCommandLog,
}) => (
<AnimatePresence>
{visible && (
@ -47,24 +46,11 @@ const HelpSheet = memo(({
<p style={{ fontWeight: 'bold' }}>Hover mouse over buttons in the main interface to see which function they have.</p>
<Table style={{ marginTop: 40 }}>
<Table.Head>
<Table.TextHeaderCell>
Settings
</Table.TextHeaderCell>
<Table.TextHeaderCell>
Current setting
</Table.TextHeaderCell>
</Table.Head>
<Table.Body>
{renderSettings()}
</Table.Body>
</Table>
<h1 style={{ marginTop: 40 }}>Last ffmpeg commands</h1>
<div style={{ overflowY: 'scroll', height: 200 }}>
{ffmpegCommandLog.reverse().map(({ command, time }) => (
<div key={time} style={{ whiteSpace: 'pre', margin: '5px 0' }}>
{ffmpegCommandLog.reverse().map(({ command }, i) => (
// eslint-disable-next-line react/no-array-index-key
<div key={i} style={{ whiteSpace: 'pre', margin: '5px 0' }}>
<FaClipboard style={{ cursor: 'pointer' }} title="Copy to clipboard" onClick={() => { clipboard.writeText(command); toast.fire({ timer: 2000, icon: 'success', title: 'Copied to clipboard' }); }} /> {command}
</div>
))}

Wyświetl plik

@ -3,35 +3,46 @@ import { Select } from 'evergreen-ui';
import { motion } from 'framer-motion';
import { FaYinYang } from 'react-icons/fa';
const { withBlur } = require('./util');
const { withBlur, toast } = require('./util');
const LeftMenu = memo(({ zoom, setZoom, invertCutSegments, setInvertCutSegments }) => (
<div className="no-user-select" style={{ padding: '.3em', display: 'flex', alignItems: 'center' }}>
<div style={{ marginLeft: 5 }}>
<motion.div
animate={{ rotateX: invertCutSegments ? 0 : 180, width: 26, height: 26 }}
transition={{ duration: 0.3 }}
>
<FaYinYang
size={26}
role="button"
title={invertCutSegments ? 'Discard selected segments' : 'Keep selected segments'}
onClick={withBlur(() => setInvertCutSegments(v => !v))}
/>
</motion.div>
const LeftMenu = memo(({ zoom, setZoom, invertCutSegments, setInvertCutSegments }) => {
function onYinYangClick() {
setInvertCutSegments(v => {
const newVal = !v;
if (newVal) toast.fire({ title: 'When you export, selected segments on the timeline will be REMOVED - the surrounding areas will be KEPT' });
else toast.fire({ title: 'When you export, selected segments on the timeline will be KEPT - the surrounding areas will be REMOVED.' });
return newVal;
});
}
return (
<div className="no-user-select" style={{ padding: '.3em', display: 'flex', alignItems: 'center' }}>
<div style={{ marginLeft: 5 }}>
<motion.div
animate={{ rotateX: invertCutSegments ? 0 : 180, width: 26, height: 26 }}
transition={{ duration: 0.3 }}
>
<FaYinYang
size={26}
role="button"
title={invertCutSegments ? 'Discard selected segments' : 'Keep selected segments'}
onClick={onYinYangClick}
/>
</motion.div>
</div>
<div style={{ marginRight: 5, marginLeft: 10 }} title="Zoom">{Math.floor(zoom)}x</div>
<Select height={20} style={{ width: 20 }} value={zoom.toString()} title="Zoom" onChange={withBlur(e => setZoom(parseInt(e.target.value, 10)))}>
{Array(13).fill().map((unused, z) => {
const val = 2 ** z;
return (
<option key={val} value={String(val)}>Zoom {val}x</option>
);
})}
</Select>
</div>
<div style={{ marginRight: 5, marginLeft: 10 }} title="Zoom">{Math.floor(zoom)}x</div>
<Select height={20} style={{ width: 20 }} value={zoom.toString()} title="Zoom" onChange={withBlur(e => setZoom(parseInt(e.target.value, 10)))}>
{Array(13).fill().map((unused, z) => {
const val = 2 ** z;
return (
<option key={val} value={String(val)}>Zoom {val}x</option>
);
})}
</Select>
</div>
));
);
});
export default LeftMenu;

Wyświetl plik

@ -59,13 +59,13 @@ const Settings = memo(({
<Row>
<KeyCell>
<span role="img" aria-label="Yin Yang"></span> Choose cutting mode: Cut away or keep selected segments from video when exporting?<br />
<span role="img" aria-label="Yin Yang"></span> Choose cutting mode: Remove or keep selected segments from video when exporting?<br />
When <b>Keep</b> is selected, the video inside segments will be kept, while the video outside will be discarded.<br />
When <b>Cut away</b> is selected, the video inside segments will be discarded, while the video surrounding them will be kept.
When <b>Remove</b> is selected, the video inside segments will be discarded, while the video surrounding them will be kept.
</KeyCell>
<Table.TextCell>
<SegmentedControl
options={[{ label: 'Cut away', value: 'discard' }, { label: 'Keep', value: 'keep' }]}
options={[{ label: 'Remove', value: 'discard' }, { label: 'Keep', value: 'keep' }]}
value={invertCutSegments ? 'discard' : 'keep'}
onChange={value => setInvertCutSegments(value === 'discard')}
/>

Wyświetl plik

@ -0,0 +1,43 @@
import React, { memo } from 'react';
import { IoIosCloseCircleOutline } from 'react-icons/io';
import { FaClipboard } from 'react-icons/fa';
import { motion, AnimatePresence } from 'framer-motion';
import { Table } from 'evergreen-ui';
// eslint-disable-next-line import/no-extraneous-dependencies
const { clipboard } = require('electron');
const { toast } = require('./util');
const SettingsSheet = memo(({
visible, onTogglePress, renderSettings,
}) => (
<AnimatePresence>
{visible && (
<motion.div
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0, opacity: 0 }}
className="help-sheet"
>
<IoIosCloseCircleOutline role="button" onClick={onTogglePress} size={30} style={{ position: 'fixed', right: 0, top: 0, padding: 20 }} />
<Table style={{ marginTop: 40 }}>
<Table.Head>
<Table.TextHeaderCell>
Settings
</Table.TextHeaderCell>
<Table.TextHeaderCell>
Current setting
</Table.TextHeaderCell>
</Table.Head>
<Table.Body>
{renderSettings()}
</Table.Body>
</Table>
</motion.div>
)}
</AnimatePresence>
));
export default SettingsSheet;

Wyświetl plik

@ -1,5 +1,5 @@
import React, { Fragment, memo } from 'react';
import { IoIosHelpCircle } from 'react-icons/io';
import { IoIosHelpCircle, IoIosSettings } from 'react-icons/io';
import { Button } from 'evergreen-ui';
import { MdCallSplit, MdCallMerge } from 'react-icons/md';
@ -9,7 +9,7 @@ import { withBlur } from './util';
const TopMenu = memo(({
filePath, copyAnyAudioTrack, toggleStripAudio, customOutDir, setOutputDir,
renderOutFmt, outSegments, autoMerge, toggleAutoMerge, keyframeCut, toggleKeyframeCut, toggleHelp,
numStreamsToCopy, numStreamsTotal, setStreamsSelectorShown,
numStreamsToCopy, numStreamsTotal, setStreamsSelectorShown, toggleSettings,
}) => {
const AutoMergeIcon = autoMerge ? MdCallMerge : MdCallSplit;
@ -68,6 +68,7 @@ const TopMenu = memo(({
)}
<IoIosHelpCircle size={24} role="button" onClick={toggleHelp} style={{ verticalAlign: 'middle', marginLeft: 5 }} />
<IoIosSettings size={24} role="button" onClick={toggleSettings} style={{ verticalAlign: 'middle', marginLeft: 5 }} />
</Fragment>
);
});

Wyświetl plik

@ -72,6 +72,13 @@ module.exports = (app, mainWindow, newVersion) => {
},
},
{ type: 'separator' },
{
label: 'Settings',
click() {
mainWindow.webContents.send('openSettings');
},
},
{ type: 'separator' },
{
label: 'Exit',
click() {
@ -124,7 +131,7 @@ module.exports = (app, mainWindow, newVersion) => {
role: 'help',
submenu: [
{
label: 'Help and Settings',
label: 'Help',
click() {
mainWindow.webContents.send('openHelp');
},

Wyświetl plik

@ -16,6 +16,7 @@ import isEqual from 'lodash/isEqual';
import TopMenu from './TopMenu';
import HelpSheet from './HelpSheet';
import SettingsSheet from './SettingsSheet';
import StreamsSelector from './StreamsSelector';
import SegmentList from './SegmentList';
import Settings from './Settings';
@ -204,6 +205,7 @@ const App = memo(() => {
// Global state
const [helpVisible, setHelpVisible] = useState(false);
const [settingsVisible, setSettingsVisible] = useState(false);
const [mifiLink, setMifiLink] = useState();
const videoRef = useRef();
@ -1040,6 +1042,7 @@ const App = memo(() => {
]);
const toggleHelp = useCallback(() => setHelpVisible(val => !val), []);
const toggleSettings = useCallback(() => setSettingsVisible(val => !val), []);
const jumpSeg = useCallback((val) => setCurrentSegIndex((old) => Math.max(Math.min(old + val, cutSegments.length - 1), 0)), [cutSegments.length]);
@ -1264,6 +1267,10 @@ const App = memo(() => {
toggleHelp();
}
function openSettings() {
toggleSettings();
}
electron.ipcRenderer.on('file-opened', fileOpened);
electron.ipcRenderer.on('close-file', closeFile);
electron.ipcRenderer.on('html5ify', html5ify);
@ -1275,6 +1282,7 @@ const App = memo(() => {
electron.ipcRenderer.on('importEdlFile', importEdlFile);
electron.ipcRenderer.on('exportEdlFile', exportEdlFile);
electron.ipcRenderer.on('openHelp', openHelp);
electron.ipcRenderer.on('openSettings', openSettings);
return () => {
electron.ipcRenderer.removeListener('file-opened', fileOpened);
@ -1288,11 +1296,12 @@ const App = memo(() => {
electron.ipcRenderer.removeListener('importEdlFile', importEdlFile);
electron.ipcRenderer.removeListener('exportEdlFile', exportEdlFile);
electron.ipcRenderer.removeListener('openHelp', openHelp);
electron.ipcRenderer.removeListener('openSettings', openSettings);
};
}, [
load, mergeFiles, outputDir, filePath, isFileOpened, customOutDir, startTimeOffset, getHtml5ifiedPath,
createDummyVideo, resetState, extractAllStreams, userOpenFiles, cutSegmentsHistory,
loadEdlFile, cutSegments, edlFilePath, askBeforeClose, toggleHelp,
loadEdlFile, cutSegments, edlFilePath, askBeforeClose, toggleHelp, toggleSettings,
]);
async function showAddStreamSourceDialog() {
@ -1457,6 +1466,7 @@ const App = memo(() => {
keyframeCut={keyframeCut}
toggleKeyframeCut={toggleKeyframeCut}
toggleHelp={toggleHelp}
toggleSettings={toggleSettings}
numStreamsToCopy={numStreamsToCopy}
numStreamsTotal={numStreamsTotal}
setStreamsSelectorShown={setStreamsSelectorShown}
@ -1685,11 +1695,16 @@ const App = memo(() => {
</motion.div>
<HelpSheet
visible={!!helpVisible}
visible={helpVisible}
onTogglePress={toggleHelp}
renderSettings={renderSettings}
ffmpegCommandLog={ffmpegCommandLog}
/>
<SettingsSheet
visible={settingsVisible}
onTogglePress={toggleSettings}
renderSettings={renderSettings}
/>
</div>
);
});