1
0
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:
Mikael Finstad 2022-12-30 12:47:39 +08:00
parent 64966590e2
commit e937cd0979
No known key found for this signature in database
GPG Key ID: 25AB36E3E81CBC26
4 changed files with 21 additions and 19 deletions

View File

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

View File

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

View File

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

View File

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