kopia lustrzana https://github.com/mifi/lossless-cut
rodzic
dca83a8ab4
commit
fabab12bce
10
src/App.jsx
10
src/App.jsx
|
@ -1293,6 +1293,7 @@ function App() {
|
|||
await onExportConfirm();
|
||||
} else {
|
||||
setExportConfirmVisible(true);
|
||||
setStreamsSelectorShown(false);
|
||||
}
|
||||
}, [filePath, exportConfirmEnabled, exportConfirmVisible, onExportConfirm]);
|
||||
|
||||
|
@ -1646,7 +1647,6 @@ function App() {
|
|||
|
||||
const handleShowStreamsSelectorClick = useCallback(() => {
|
||||
setStreamsSelectorShown(true);
|
||||
setExportConfirmVisible(false);
|
||||
}, []);
|
||||
|
||||
const extractAllStreams = useCallback(async () => {
|
||||
|
@ -2579,7 +2579,9 @@ function App() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<Sheet visible={streamsSelectorShown} onClosePress={() => setStreamsSelectorShown(false)} style={{ padding: '1em 0' }}>
|
||||
<ExportConfirm filePath={filePath} areWeCutting={areWeCutting} nonFilteredSegmentsOrInverse={nonFilteredSegmentsOrInverse} selectedSegments={selectedSegmentsOrInverse} segmentsToExport={segmentsToExport} willMerge={willMerge} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} renderOutFmt={renderOutFmt} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} onShowStreamsSelectorClick={handleShowStreamsSelectorClick} outFormat={fileFormat} setOutSegTemplate={setOutSegTemplate} outSegTemplate={outSegTemplateOrDefault} generateOutSegFileNames={generateOutSegFileNames} currentSegIndexSafe={currentSegIndexSafe} mainCopiedThumbnailStreams={mainCopiedThumbnailStreams} needSmartCut={needSmartCut} mergedOutFileName={mergedOutFileName} setMergedOutFileName={setMergedOutFileName} />
|
||||
|
||||
<Sheet visible={streamsSelectorShown} onClosePress={() => setStreamsSelectorShown(false)} maxWidth={1000}>
|
||||
{mainStreams && (
|
||||
<StreamsSelector
|
||||
mainFilePath={filePath}
|
||||
|
@ -2607,15 +2609,13 @@ function App() {
|
|||
)}
|
||||
</Sheet>
|
||||
|
||||
<ExportConfirm filePath={filePath} areWeCutting={areWeCutting} nonFilteredSegmentsOrInverse={nonFilteredSegmentsOrInverse} selectedSegments={selectedSegmentsOrInverse} segmentsToExport={segmentsToExport} willMerge={willMerge} visible={exportConfirmVisible} onClosePress={closeExportConfirm} onExportConfirm={onExportConfirm} renderOutFmt={renderOutFmt} outputDir={outputDir} numStreamsTotal={numStreamsTotal} numStreamsToCopy={numStreamsToCopy} onShowStreamsSelectorClick={handleShowStreamsSelectorClick} outFormat={fileFormat} setOutSegTemplate={setOutSegTemplate} outSegTemplate={outSegTemplateOrDefault} generateOutSegFileNames={generateOutSegFileNames} currentSegIndexSafe={currentSegIndexSafe} mainCopiedThumbnailStreams={mainCopiedThumbnailStreams} needSmartCut={needSmartCut} mergedOutFileName={mergedOutFileName} setMergedOutFileName={setMergedOutFileName} />
|
||||
|
||||
<LastCommandsSheet
|
||||
visible={lastCommandsVisible}
|
||||
onTogglePress={toggleLastCommands}
|
||||
ffmpegCommandLog={ffmpegCommandLog}
|
||||
/>
|
||||
|
||||
<Sheet visible={settingsVisible} onClosePress={toggleSettings} style={{ padding: '1em 0' }}>
|
||||
<Sheet visible={settingsVisible} onClosePress={toggleSettings}>
|
||||
<Settings
|
||||
onTunerRequested={onTunerRequested}
|
||||
onKeyboardShortcutsDialogRequested={toggleKeyboardShortcuts}
|
||||
|
|
|
@ -8,7 +8,7 @@ const LastCommandsSheet = memo(({ visible, onTogglePress, ffmpegCommandLog }) =>
|
|||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Sheet visible={visible} onClosePress={onTogglePress} style={{ paddingTop: '2em' }}>
|
||||
<Sheet visible={visible} onClosePress={onTogglePress}>
|
||||
<h2>{t('Last ffmpeg commands')}</h2>
|
||||
|
||||
{ffmpegCommandLog.length > 0 ? (
|
||||
|
|
|
@ -42,7 +42,7 @@ const Waveforms = memo(({ calculateTimelinePercent, durationSafe, waveforms, zoo
|
|||
|
||||
const CommandedTime = memo(({ commandedTimePercent }) => {
|
||||
const color = 'var(--gray12)';
|
||||
const commonStyle = { left: commandedTimePercent, position: 'absolute', zIndex: 4, pointerEvents: 'none' };
|
||||
const commonStyle = { left: commandedTimePercent, position: 'absolute', pointerEvents: 'none' };
|
||||
return (
|
||||
<>
|
||||
<FaCaretDown style={{ ...commonStyle, top: 0, color, fontSize: 14, marginLeft: -7, marginTop: -6 }} />
|
||||
|
@ -275,7 +275,7 @@ const Timeline = memo(({
|
|||
const nextThumbTime = nextThumbnail ? nextThumbnail.time : durationSafe;
|
||||
const maxWidthPercent = ((nextThumbTime - thumbnail.time) / durationSafe) * 100 * 0.9;
|
||||
return (
|
||||
<img key={thumbnail.url} src={thumbnail.url} alt="" style={{ position: 'absolute', left: `${leftPercent}%`, height: '100%', boxSizing: 'border-box', zIndex: 1, maxWidth: `${maxWidthPercent}%`, objectFit: 'cover', border: '1px solid rgba(255, 255, 255, 0.5)', borderBottomRightRadius: 15, borderTopLeftRadius: 15, borderTopRightRadius: 15, pointerEvents: 'none' }} />
|
||||
<img key={thumbnail.url} src={thumbnail.url} alt="" style={{ position: 'absolute', left: `${leftPercent}%`, height: '100%', boxSizing: 'border-box', maxWidth: `${maxWidthPercent}%`, objectFit: 'cover', border: '1px solid rgba(255, 255, 255, 0.5)', borderBottomRightRadius: 15, borderTopLeftRadius: 15, borderTopRightRadius: 15, pointerEvents: 'none' }} />
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
@ -285,13 +285,6 @@ const Timeline = memo(({
|
|||
style={{ height: timelineHeight, width: `${zoom * 100}%`, position: 'relative', backgroundColor: timelineBackground, transition: darkModeTransition }}
|
||||
ref={timelineWrapperRef}
|
||||
>
|
||||
{currentTimePercent !== undefined && (
|
||||
<motion.div transition={{ type: 'spring', damping: 70, stiffness: 800 }} animate={{ left: currentTimePercent }} style={{ position: 'absolute', bottom: 0, top: 0, zIndex: 3, backgroundColor: 'var(--gray12)', width: currentTimeWidth, pointerEvents: 'none' }} />
|
||||
)}
|
||||
{commandedTimePercent !== undefined && (
|
||||
<CommandedTime commandedTimePercent={commandedTimePercent} />
|
||||
)}
|
||||
|
||||
{apparentCutSegments.map((seg, i) => {
|
||||
if (seg.start === 0 && seg.end === 0) return null; // No video loaded
|
||||
|
||||
|
@ -325,10 +318,17 @@ const Timeline = memo(({
|
|||
{shouldShowKeyframes && !areKeyframesTooClose && neighbouringKeyFrames.map((f) => (
|
||||
<div key={f.time} style={{ position: 'absolute', top: 0, bottom: 0, left: `${(f.time / durationSafe) * 100}%`, marginLeft: -1, width: 1, background: 'var(--gray11)', pointerEvents: 'none' }} />
|
||||
))}
|
||||
|
||||
{currentTimePercent !== undefined && (
|
||||
<motion.div transition={{ type: 'spring', damping: 70, stiffness: 800 }} animate={{ left: currentTimePercent }} style={{ position: 'absolute', bottom: 0, top: 0, backgroundColor: 'var(--gray12)', width: currentTimeWidth, pointerEvents: 'none' }} />
|
||||
)}
|
||||
{commandedTimePercent !== undefined && (
|
||||
<CommandedTime commandedTimePercent={commandedTimePercent} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ position: 'absolute', height: timelineHeight, left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', zIndex: 2 }}>
|
||||
<div style={{ position: 'absolute', height: timelineHeight, left: 0, right: 0, bottom: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none' }}>
|
||||
<div style={{ background: 'rgba(0,0,0,0.4)', borderRadius: 3, padding: '2px 4px', color: 'rgba(255, 255, 255, 0.8)' }}>
|
||||
{formatTimeAndFrames(displayTime)}{isZoomed ? ` ${displayTimePercent}` : ''}
|
||||
</div>
|
||||
|
|
|
@ -412,7 +412,7 @@ const ExportConfirm = memo(({
|
|||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div style={{ zIndex: 11, position: 'fixed', right: 0, bottom: 0, display: 'flex', alignItems: 'center', margin: 5 }}>
|
||||
<div style={{ position: 'fixed', right: 0, bottom: 0, display: 'flex', alignItems: 'center', margin: 5 }}>
|
||||
<motion.div
|
||||
initial={{ opacity: 0, translateX: 50 }}
|
||||
animate={{ opacity: 1, translateX: 0 }}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
background: var(--whiteA11);
|
||||
color: var(--gray12);
|
||||
backdrop-filter: blur(30px);
|
||||
|
|
|
@ -1,27 +1,33 @@
|
|||
import React, { memo } from 'react';
|
||||
import { IoIosCloseCircleOutline } from 'react-icons/io';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import styles from './Sheet.module.css';
|
||||
|
||||
const Sheet = memo(({ visible, onClosePress, style, children }) => (
|
||||
<AnimatePresence>
|
||||
{visible && (
|
||||
<motion.div
|
||||
initial={{ scale: 0, opacity: 0.5 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0, opacity: 0 }}
|
||||
style={style}
|
||||
className={styles.sheet}
|
||||
>
|
||||
<IoIosCloseCircleOutline role="button" onClick={onClosePress} size={30} style={{ position: 'fixed', right: 0, top: 0, padding: 20, zIndex: 1, cursor: 'pointer' }} />
|
||||
const Sheet = memo(({ visible, onClosePress, children, maxWidth = 800 }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
<div style={{ overflowY: 'scroll', height: '100%' }}>
|
||||
{children}
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
));
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{visible && (
|
||||
<motion.div
|
||||
initial={{ scale: 0, opacity: 0.5 }}
|
||||
animate={{ scale: 1, opacity: 1 }}
|
||||
exit={{ scale: 0, opacity: 0 }}
|
||||
className={styles.sheet}
|
||||
>
|
||||
<div style={{ margin: 'auto', maxWidth, height: '100%', position: 'relative', padding: '1em 0' }}>
|
||||
<div style={{ overflowY: 'scroll', height: '100%' }}>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<IoIosCloseCircleOutline role="button" onClick={onClosePress} title={t('Close')} size={30} style={{ position: 'absolute', padding: '1em', right: 0, top: 0, cursor: 'pointer' }} />
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
});
|
||||
|
||||
export default Sheet;
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
.sheet {
|
||||
padding: 1em 2em;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 10;
|
||||
background: var(--whiteA11);
|
||||
color: var(--gray12);
|
||||
backdrop-filter: blur(30px);
|
||||
|
|
|
@ -11,7 +11,7 @@ const ValueTuner = memo(({ style, title, value, setValue, onFinished, resolution
|
|||
}
|
||||
|
||||
return (
|
||||
<div style={{ background: 'var(--gray1)', color: 'var(--gray12)', position: 'absolute', bottom: 0, zIndex: 10, padding: 10, margin: 10, borderRadius: 10, ...style }}>
|
||||
<div style={{ background: 'var(--gray1)', color: 'var(--gray12)', position: 'absolute', bottom: 0, padding: 10, margin: 10, borderRadius: 10, ...style }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', flexBasis: 400 }}>
|
||||
<div style={{ marginBottom: '.5em' }}>{title}</div>
|
||||
<div style={{ marginLeft: 10, fontWeight: 'bold' }}>{value.toFixed(2)}</div>
|
||||
|
|
|
@ -9,7 +9,7 @@ import loadingLottie from '../7077-magic-flow.json';
|
|||
|
||||
|
||||
const Working = memo(({ text, cutProgress, onAbortClick }) => (
|
||||
<div style={{ position: 'absolute', zIndex: 1, bottom: 0, top: 0, left: 0, right: 0, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||
<div style={{ position: 'absolute', bottom: 0, top: 0, left: 0, right: 0, display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||
<motion.div
|
||||
style={{ background: primaryColor, boxShadow: `${primaryColor} 0px 0px 20px 25px`, borderRadius: 60, paddingBottom: 5, color: 'white', fontSize: 14, display: 'flex', flexDirection: 'column', alignItems: 'center' }}
|
||||
initial={{ opacity: 0, scale: 0 }}
|
||||
|
|
Ładowanie…
Reference in New Issue