mirror of
https://github.com/mifi/lossless-cut.git
synced 2024-11-26 04:02:51 +01:00
improvements
- make zoom exponential - make segments copyable - fixes #719
This commit is contained in:
parent
64966590e2
commit
e937cd0979
14
src/App.jsx
14
src/App.jsx
@ -75,7 +75,7 @@ import { fallbackLng } from './i18n';
|
||||
import { createSegment, getCleanCutSegments, getSegApparentStart, findSegmentsAtCursor, sortSegments, invertSegments, getSegmentTags, convertSegmentsToChapters, hasAnySegmentOverlap } from './segments';
|
||||
import { getOutSegError as getOutSegErrorRaw } from './util/outputNameTemplate';
|
||||
import * as ffmpegParameters from './ffmpeg-parameters';
|
||||
import { maxSegmentsAllowed } from './util/constants';
|
||||
import { maxSegmentsAllowed, ffmpegExtractWindow, zoomMax, rightBarWidth, leftBarWidth } from './util/constants';
|
||||
|
||||
import isDev from './isDev';
|
||||
|
||||
@ -91,16 +91,10 @@ const { dialog } = remote;
|
||||
const { focusWindow } = remote.require('./electron');
|
||||
|
||||
|
||||
const ffmpegExtractWindow = 60;
|
||||
const calcShouldShowWaveform = (zoomedDuration) => (zoomedDuration != null && zoomedDuration < ffmpegExtractWindow * 8);
|
||||
const calcShouldShowKeyframes = (zoomedDuration) => (zoomedDuration != null && zoomedDuration < ffmpegExtractWindow * 8);
|
||||
|
||||
|
||||
const zoomMax = 2 ** 14;
|
||||
|
||||
const rightBarWidth = 200;
|
||||
const leftBarWidth = 240;
|
||||
|
||||
|
||||
const videoStyle = { width: '100%', height: '100%', objectFit: 'contain' };
|
||||
const bottomMotionStyle = { background: controlsBackground };
|
||||
@ -133,7 +127,7 @@ const App = memo(() => {
|
||||
const [copyStreamIdsByFile, setCopyStreamIdsByFile] = useState({});
|
||||
const [streamsSelectorShown, setStreamsSelectorShown] = useState(false);
|
||||
const [concatDialogVisible, setConcatDialogVisible] = useState(false);
|
||||
const [zoom, setZoom] = useState(1);
|
||||
const [zoomUnrounded, setZoom] = useState(1);
|
||||
const [thumbnails, setThumbnails] = useState([]);
|
||||
const [shortestFlag, setShortestFlag] = useState(false);
|
||||
const [zoomWindowStartTime, setZoomWindowStartTime] = useState(0);
|
||||
@ -198,6 +192,8 @@ const App = memo(() => {
|
||||
setWorkingState(val);
|
||||
}, []);
|
||||
|
||||
const zoom = Math.floor(zoomUnrounded);
|
||||
|
||||
const durationSafe = isDurationValid(duration) ? duration : 1;
|
||||
const zoomedDuration = isDurationValid(duration) ? duration / zoom : undefined;
|
||||
|
||||
@ -325,7 +321,7 @@ const App = memo(() => {
|
||||
const isRotationSet = rotation !== 360;
|
||||
const effectiveRotation = isRotationSet ? rotation : (mainVideoStream && mainVideoStream.tags && mainVideoStream.tags.rotate && parseInt(mainVideoStream.tags.rotate, 10));
|
||||
|
||||
const zoomRel = useCallback((rel) => setZoom(z => Math.min(Math.max(z + rel, 1), zoomMax)), []);
|
||||
const zoomRel = useCallback((rel) => setZoom((z) => Math.min(Math.max(z + (rel * (1 + (z / 10))), 1), zoomMax)), []);
|
||||
const canvasPlayerRequired = !!(mainVideoStream && usingDummyVideo);
|
||||
const canvasPlayerWanted = !!(mainVideoStream && isRotationSet && !hideCanvasPreview);
|
||||
// Allow user to disable it
|
||||
|
@ -75,11 +75,11 @@ const Segment = memo(({ seg, index, currentSegIndex, formatTimecode, getFrameCou
|
||||
}, 300, [isActive]);
|
||||
|
||||
function renderNumber() {
|
||||
if (invertCutSegments) return <FaSave style={{ color: saveColor, marginRight: 5, verticalAlign: 'middle' }} size={14} />;
|
||||
if (invertCutSegments) return <FaSave style={{ cursor: 'grab', color: saveColor, marginRight: 5, verticalAlign: 'middle' }} size={14} />;
|
||||
|
||||
const segColor = getSegColor(seg);
|
||||
|
||||
return <b style={{ color: 'white', padding: '0 4px', marginRight: 3, marginLeft: -3, background: segColor.alpha(0.5).string(), border: `1px solid ${isActive ? segColor.lighten(0.3).string() : 'transparent'}`, borderRadius: 10, fontSize: 12 }}>{index + 1}</b>;
|
||||
return <b style={{ cursor: 'grab', color: 'white', padding: '0 4px', marginRight: 3, marginLeft: -3, background: segColor.alpha(0.5).string(), border: `1px solid ${isActive ? segColor.lighten(0.3).string() : 'transparent'}`, borderRadius: 10, fontSize: 12 }}>{index + 1}</b>;
|
||||
}
|
||||
|
||||
const timeStr = useMemo(() => `${formatTimecode({ seconds: seg.start })} - ${formatTimecode({ seconds: seg.end })}`, [seg.start, seg.end, formatTimecode]);
|
||||
@ -110,16 +110,17 @@ const Segment = memo(({ seg, index, currentSegIndex, formatTimecode, getFrameCou
|
||||
onClick={() => !invertCutSegments && onClick(index)}
|
||||
onDoubleClick={onDoubleClick}
|
||||
positionTransition
|
||||
style={{ cursor: 'grab', originY: 0, margin: '5px 0', background: 'rgba(0,0,0,0.1)', border: `1px solid rgba(255,255,255,${isActive ? 1 : 0.3})`, padding: 5, borderRadius: 5, position: 'relative', opacity: !enabled && !invertCutSegments ? 0.5 : undefined }}
|
||||
style={{ originY: 0, margin: '5px 0', background: 'rgba(0,0,0,0.1)', border: `1px solid rgba(255,255,255,${isActive ? 1 : 0.3})`, padding: 5, borderRadius: 5, position: 'relative', opacity: !enabled && !invertCutSegments ? 0.5 : undefined }}
|
||||
initial={{ scaleY: 0 }}
|
||||
animate={{ scaleY: 1 }}
|
||||
exit={{ scaleY: 0 }}
|
||||
className="segment-list-entry"
|
||||
>
|
||||
<div style={{ color: 'white', marginBottom: 3, display: 'flex', alignItems: 'center', height: 16 }}>
|
||||
<div className="segment-handle" style={{ cursor: 'grab', color: 'white', marginBottom: 3, display: 'flex', alignItems: 'center', height: 16 }}>
|
||||
{renderNumber()}
|
||||
<span style={{ fontSize: Math.min(310 / timeStr.length, 14), whiteSpace: 'nowrap' }}>{timeStr}</span>
|
||||
<span style={{ cursor: 'grab', fontSize: Math.min(310 / timeStr.length, 14), whiteSpace: 'nowrap' }}>{timeStr}</span>
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: 12, color: 'white' }}>{seg.name}</div>
|
||||
<div style={{ fontSize: 13 }}>
|
||||
{t('Duration')} {formatTimecode({ seconds: duration, shorten: true })}
|
||||
@ -248,13 +249,12 @@ const SegmentList = memo(({
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className="no-user-select"
|
||||
style={{ width, background: controlsBackground, color: 'rgba(255,255,255,0.7)', display: 'flex', flexDirection: 'column', overflowY: 'hidden' }}
|
||||
initial={{ x: width }}
|
||||
animate={{ x: 0 }}
|
||||
exit={{ x: width }}
|
||||
>
|
||||
<div style={{ fontSize: 14, padding: '0 5px', display: 'flex', alignItems: 'center' }}>
|
||||
<div style={{ fontSize: 14, padding: '0 5px', display: 'flex', alignItems: 'center' }} className="no-user-select">
|
||||
<FaAngleRight
|
||||
title={t('Close sidebar')}
|
||||
size={20}
|
||||
@ -266,7 +266,7 @@ const SegmentList = memo(({
|
||||
{header}
|
||||
</div>
|
||||
<div style={{ padding: '0 10px', overflowY: 'scroll', flexGrow: 1 }} className="hide-scrollbar">
|
||||
<ReactSortable list={sortableList} setList={setSortableList} sort={!invertCutSegments}>
|
||||
<ReactSortable list={sortableList} setList={setSortableList} sort={!invertCutSegments} handle=".segment-handle">
|
||||
{sortableList.map(({ id, seg }, index) => {
|
||||
const enabled = !invertCutSegments && selectedSegmentsRaw.includes(seg);
|
||||
return (
|
||||
|
@ -22,7 +22,7 @@ export const silencedetect = () => ({
|
||||
},
|
||||
duration: {
|
||||
value: '2.0',
|
||||
hint: i18n.t('Set silence duration until notification (default is 2 seconds).'),
|
||||
hint: i18n.t('Set minimum silence duration that will be converted into a segment.'),
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -1,3 +1,9 @@
|
||||
// anything more than this will probably cause the UI to become unusably slow
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const maxSegmentsAllowed = 2000;
|
||||
|
||||
export const ffmpegExtractWindow = 60;
|
||||
|
||||
export const zoomMax = 2 ** 14;
|
||||
|
||||
export const rightBarWidth = 200;
|
||||
export const leftBarWidth = 240;
|
||||
|
Loading…
Reference in New Issue
Block a user