Skip to content

Commit

Permalink
Improvements:
Browse files Browse the repository at this point in the history
- Make side panel for cut segments
- Use up/down key to jump prev/next segment #254
  • Loading branch information
mifi committed Feb 21, 2020
1 parent 8addb00 commit b22654a
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 49 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"browser": true
},
"rules": {
"max-len": 0,
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "error",

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"mousetrap": "^1.6.1",
"p-map": "^3.0.0",
"p-queue": "^6.2.0",
"pretty-ms": "^6.0.0",
"prop-types": "^15.6.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
Expand Down
23 changes: 6 additions & 17 deletions src/HelpSheet.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { memo } from 'react';
import { IoIosCloseCircleOutline } from 'react-icons/io';
import { FaClipboard } from 'react-icons/fa';
import { motion, AnimatePresence } from 'framer-motion';
Expand All @@ -9,9 +9,8 @@ const { clipboard } = require('electron');

const { toast } = require('./util');

const HelpSheet = ({
visible, onTogglePress, renderSettings, ffmpegCommandLog, sortedCutSegments,
formatTimecode,
const HelpSheet = memo(({
visible, onTogglePress, renderSettings, ffmpegCommandLog,
}) => (
<AnimatePresence>
{visible && (
Expand All @@ -30,8 +29,8 @@ const HelpSheet = ({
<div><kbd>L</kbd> Speed up video</div>
<div><kbd></kbd> Seek backward 1 sec</div>
<div><kbd></kbd> Seek forward 1 sec</div>
<div><kbd>.</kbd> (period) Tiny seek forward (1/60 sec)</div>
<div><kbd>,</kbd> (comma) Tiny seek backward (1/60 sec)</div>
<div><kbd>,</kbd> Seek backward 1 frame</div>
<div><kbd>.</kbd> Seek forward 1 frame</div>
<div><kbd>I</kbd> Mark in / cut start point</div>
<div><kbd>O</kbd> Mark out / cut end point</div>
<div><kbd>E</kbd> Cut (export selection in the same directory)</div>
Expand All @@ -56,16 +55,6 @@ const HelpSheet = ({
</Table.Body>
</Table>

<h1 style={{ marginTop: 40 }}>Segment list</h1>

<div style={{ overflowY: 'scroll', height: 200 }}>
{sortedCutSegments.map((seg) => (
<div key={seg.uuid} style={{ margin: '5px 0' }}>
{formatTimecode(seg.start)} - {formatTimecode(seg.end)} {seg.name}
</div>
))}
</div>

<h1 style={{ marginTop: 40 }}>Last ffmpeg commands</h1>
<div style={{ overflowY: 'scroll', height: 200 }}>
{ffmpegCommandLog.reverse().map((log) => (
Expand All @@ -77,6 +66,6 @@ const HelpSheet = ({
</motion.div>
)}
</AnimatePresence>
);
));

export default HelpSheet;
3 changes: 2 additions & 1 deletion src/InverseCutSegment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { motion } from 'framer-motion';
import { FaTrashAlt, FaSave } from 'react-icons/fa';

import { mySpring } from './animations';
import { saveColor } from './colors';

const InverseCutSegment = ({ seg, duration, invertCutSegments }) => (
<motion.div
Expand All @@ -20,7 +21,7 @@ const InverseCutSegment = ({ seg, duration, invertCutSegments }) => (
>
<div style={{ flexGrow: 1, borderBottom: '1px dashed rgba(255, 255, 255, 0.3)', marginLeft: 5, marginRight: 5 }} />
{invertCutSegments ? (
<FaSave style={{ color: 'hsl(158, 100%, 43%)' }} size={16} />
<FaSave style={{ color: saveColor }} size={16} />
) : (
<FaTrashAlt style={{ color: 'rgba(255, 255, 255, 0.3)' }} size={16} />
)}
Expand Down
69 changes: 69 additions & 0 deletions src/SegmentList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { memo, Fragment } from 'react';
import prettyMs from 'pretty-ms';
import { FaSave } from 'react-icons/fa';
import { motion } from 'framer-motion';

import { saveColor } from './colors';

const SegmentList = memo(({
formatTimecode, cutSegments, getFrameCount, getSegColors, onSegClick,
currentSegIndex, invertCutSegments,
}) => {
if (!cutSegments && invertCutSegments) {
return <div>Make sure you have no overlapping segments.</div>;
}

if (!cutSegments || cutSegments.length === 0) {
return <div>No segments to export.</div>;
}

return (
<Fragment>
<div style={{ fontSize: 14, marginBottom: 10 }}>Segments to export:</div>
{cutSegments.map((seg, index) => {
const duration = seg.end - seg.start;
const durationMs = duration * 1000;

const isActive = !invertCutSegments && currentSegIndex === index;
const uuid = seg.uuid || `${seg.start}`;

function renderNumber() {
if (invertCutSegments) return <FaSave style={{ color: saveColor, marginRight: 5, verticalAlign: 'middle' }} size={14} />;

const {
segBgColor, segBorderColor,
} = getSegColors(seg);

return <b style={{ color: 'white', padding: '0 3px', marginRight: 5, background: segBgColor, border: `1px solid ${isActive ? segBorderColor : 'transparent'}`, borderRadius: 10, fontSize: 12 }}>{index + 1}</b>;
}

return (
<motion.div
role="button"
onClick={() => !invertCutSegments && onSegClick(index)}
key={uuid}
positionTransition
style={{ originY: 0, margin: '5px 0', border: `1px solid rgba(255,255,255,${isActive ? 1 : 0.3})`, padding: 5, borderRadius: 5 }}
initial={{ scaleY: 0 }}
animate={{ scaleY: 1 }}
exit={{ scaleY: 0 }}
>
<div style={{ fontSize: 13, whiteSpace: 'nowrap', color: 'white', marginBottom: 3 }}>
{renderNumber()}
<span>{formatTimecode(seg.start)} - {formatTimecode(seg.end)}</span>
</div>
<div style={{ fontSize: 13 }}>
Duration {prettyMs(durationMs)}
</div>
<div style={{ fontSize: 12 }}>
({Math.floor(durationMs)} ms, {getFrameCount(duration)} frames)
</div>
<div style={{ fontSize: 12, color: 'white' }}>{seg.name}</div>
</motion.div>
);
})}
</Fragment>
);
});

export default SegmentList;
11 changes: 7 additions & 4 deletions src/StreamsSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ const Stream = memo(({ stream, onToggle, copyStream }) => {
});
}

const onClick = () => onToggle && onToggle(stream.index);

return (
<tr style={{ opacity: copyStream ? undefined : 0.4 }}>
<td><Icon size={20} style={{ padding: '0 5px', cursor: 'pointer' }} role="button" onClick={() => onToggle && onToggle(stream.index)} /></td>
<td><Icon size={20} style={{ padding: '0 5px', cursor: 'pointer' }} role="button" onClick={onClick} /></td>
<td>{stream.index}</td>
<td>{stream.codec_type}</td>
<td>{stream.codec_tag !== '0x0000' && stream.codec_tag_string}</td>
Expand All @@ -50,7 +52,7 @@ const Stream = memo(({ stream, onToggle, copyStream }) => {
<td>{stream.nb_frames}</td>
<td>{!Number.isNaN(bitrate) && `${(bitrate / 1e6).toFixed(1)}MBit/s`}</td>
<td>{stream.width && stream.height && `${stream.width}x${stream.height}`} {stream.channels && `${stream.channels}c`} {stream.channel_layout} {streamFps && `${streamFps.toFixed(1)}fps`}</td>
<td><FaInfoCircle role="button" onClick={() => onInfoClick(stream)} size={30} /></td>
<td><FaInfoCircle role="button" onClick={() => onInfoClick(stream)} size={26} /></td>
</tr>
);
});
Expand Down Expand Up @@ -78,7 +80,7 @@ const StreamsSelector = memo(({
<table style={{ marginBottom: 10 }}>
<thead style={{ background: 'rgba(0,0,0,0.1)' }}>
<tr>
<th>?</th>
<th>Keep?</th>
<th />
<th>Type</th>
<th>Tag</th>
Expand All @@ -104,8 +106,9 @@ const StreamsSelector = memo(({
{Object.entries(externalFiles).map(([path, { streams }]) => (
<Fragment key={path}>
<tr>
<td><FaTrashAlt size={20} role="button" style={{ padding: '0 5px', cursor: 'pointer' }} onClick={() => removeFile(path)} /></td>
<td colSpan={9} style={{ paddingTop: 15 }}>
{path} <FaTrashAlt role="button" onClick={() => removeFile(path)} />
{path}
</td>
</tr>

Expand Down
3 changes: 3 additions & 0 deletions src/colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const saveColor = 'hsl(158, 100%, 43%)';
export const primaryColor = 'hsl(194, 78%, 47%)';
export const controlsBackground = '#6b6b6b';
Loading

0 comments on commit b22654a

Please sign in to comment.