This commit is contained in:
ct
2025-06-15 08:33:39 +08:00
parent 361f630293
commit 6431bdafc1
3 changed files with 101 additions and 141 deletions

View File

@@ -25,8 +25,8 @@ const VideoEditor = ({ width, height }) => {
duration: 5, duration: 5,
x: 50, x: 50,
y: 50, y: 50,
width: 300, // Will be updated when video loads width: 300,
height: 200, // Will be updated when video loads height: 200,
}, },
{ {
id: '2', id: '2',
@@ -41,8 +41,8 @@ const VideoEditor = ({ width, height }) => {
duration: 4, duration: 4,
x: 100, x: 100,
y: 100, y: 100,
width: 250, // Will be updated when video loads width: 250,
height: 150, // Will be updated when video loads height: 150,
}, },
{ {
id: '3', id: '3',
@@ -57,8 +57,8 @@ const VideoEditor = ({ width, height }) => {
duration: 6, duration: 6,
x: 200, x: 200,
y: 200, y: 200,
width: 280, // Will be updated when video loads width: 280,
height: 180, // Will be updated when video loads height: 180,
}, },
{ {
id: '4', id: '4',
@@ -91,10 +91,7 @@ const VideoEditor = ({ width, height }) => {
]); ]);
const lastUpdateRef = useRef(0); const lastUpdateRef = useRef(0);
// FFmpeg WASM states
const ffmpegRef = useRef(new FFmpeg()); const ffmpegRef = useRef(new FFmpeg());
const emitter = useMitt(); const emitter = useMitt();
const [isExporting, setIsExporting] = useState(false); const [isExporting, setIsExporting] = useState(false);
@@ -106,8 +103,6 @@ const VideoEditor = ({ width, height }) => {
const [videoElements, setVideoElements] = useState({}); const [videoElements, setVideoElements] = useState({});
const [loadedVideos, setLoadedVideos] = useState(new Set()); const [loadedVideos, setLoadedVideos] = useState(new Set());
const [status, setStatus] = useState('Loading videos...'); const [status, setStatus] = useState('Loading videos...');
// Track which videos should be playing - this is the key optimization
const [videoStates, setVideoStates] = useState({}); const [videoStates, setVideoStates] = useState({});
const animationRef = useRef(null); const animationRef = useRef(null);
@@ -119,12 +114,10 @@ const VideoEditor = ({ width, height }) => {
useEffect(() => { useEffect(() => {
setVideoIsPlaying(isPlaying); setVideoIsPlaying(isPlaying);
}, [isPlaying]); }, [isPlaying, setVideoIsPlaying]);
// Calculate total timeline duration
const totalDuration = Math.max(...timelineElements.map((el) => el.startTime + el.duration)); const totalDuration = Math.max(...timelineElements.map((el) => el.startTime + el.duration));
// Generate FFmpeg command - COMPLETE VERSION
const generateFFmpegCommand = useCallback( const generateFFmpegCommand = useCallback(
(is_string = true, useLocalFiles = false) => { (is_string = true, useLocalFiles = false) => {
console.log('🎬 STARTING FFmpeg generation'); console.log('🎬 STARTING FFmpeg generation');
@@ -142,20 +135,15 @@ const VideoEditor = ({ width, height }) => {
} }
} }
// Build inputs
let inputArgs = []; let inputArgs = [];
videos.forEach((v, i) => { videos.forEach((v, i) => {
inputArgs.push('-i'); inputArgs.push('-i');
inputArgs.push(useLocalFiles ? `input${i}.webm` : v.source); inputArgs.push(useLocalFiles ? `input${i}.webm` : v.source);
}); });
// Build filter parts array
let filters = []; let filters = [];
// Base canvas
filters.push(`color=black:size=${dimensions.width}x${dimensions.height}:duration=${totalDuration}[base]`); filters.push(`color=black:size=${dimensions.width}x${dimensions.height}:duration=${totalDuration}[base]`);
// Process video streams
let videoLayer = 'base'; let videoLayer = 'base';
videos.forEach((v, i) => { videos.forEach((v, i) => {
filters.push(`[${i}:v]trim=start=${v.inPoint}:duration=${v.duration},setpts=PTS-STARTPTS[v${i}_trim]`); filters.push(`[${i}:v]trim=start=${v.inPoint}:duration=${v.duration},setpts=PTS-STARTPTS[v${i}_trim]`);
@@ -168,7 +156,6 @@ const VideoEditor = ({ width, height }) => {
videoLayer = `v${i}_out`; videoLayer = `v${i}_out`;
}); });
// AUDIO PROCESSING - EXPLICIT AND COMPLETE
console.log('🎵 PROCESSING AUDIO FOR', videos.length, 'VIDEOS'); console.log('🎵 PROCESSING AUDIO FOR', videos.length, 'VIDEOS');
let audioOutputs = []; let audioOutputs = [];
@@ -179,7 +166,6 @@ const VideoEditor = ({ width, height }) => {
audioOutputs.push(`[a${i}]`); audioOutputs.push(`[a${i}]`);
}); });
// Audio mixing
let audioArgs = []; let audioArgs = [];
if (audioOutputs.length === 1) { if (audioOutputs.length === 1) {
filters.push(`[a0]apad=pad_dur=${totalDuration}[audio_final]`); filters.push(`[a0]apad=pad_dur=${totalDuration}[audio_final]`);
@@ -191,7 +177,6 @@ const VideoEditor = ({ width, height }) => {
console.log('🎵 Audio args:', audioArgs); console.log('🎵 Audio args:', audioArgs);
// Add text overlays
texts.forEach((t, i) => { texts.forEach((t, i) => {
const escapedText = t.text.replace(/'/g, is_string ? "\\'" : "'").replace(/:/g, '\\:'); const escapedText = t.text.replace(/'/g, is_string ? "\\'" : "'").replace(/:/g, '\\:');
@@ -204,11 +189,10 @@ const VideoEditor = ({ width, height }) => {
); );
videoLayer = `t${i}`; videoLayer = `t${i}`;
}); });
// Join all filter parts
const filterComplex = filters.join('; '); const filterComplex = filters.join('; ');
console.log('🎵 Filter includes atrim:', filterComplex.includes('atrim')); console.log('🎵 Filter includes atrim:', filterComplex.includes('atrim'));
// Build final arguments
const finalArgs = [ const finalArgs = [
...inputArgs, ...inputArgs,
'-filter_complex', '-filter_complex',
@@ -228,7 +212,6 @@ const VideoEditor = ({ width, height }) => {
]; ];
if (is_string) { if (is_string) {
// Build final command string
const inputs = videos.map((v, i) => `-i "${useLocalFiles ? `input${i}.webm` : v.source}"`).join(' '); const inputs = videos.map((v, i) => `-i "${useLocalFiles ? `input${i}.webm` : v.source}"`).join(' ');
const audioMap = audioArgs.length > 0 ? ` ${audioArgs.join(' ')}` : ''; const audioMap = audioArgs.length > 0 ? ` ${audioArgs.join(' ')}` : '';
const command = `ffmpeg ${inputs} -filter_complex "${filterComplex}" -map "[${videoLayer}]"${audioMap} -c:v libx264 -pix_fmt yuv420p -r 30 -t ${totalDuration} output.mp4`; const command = `ffmpeg ${inputs} -filter_complex "${filterComplex}" -map "[${videoLayer}]"${audioMap} -c:v libx264 -pix_fmt yuv420p -r 30 -t ${totalDuration} output.mp4`;
@@ -245,20 +228,16 @@ const VideoEditor = ({ width, height }) => {
[timelineElements, dimensions, totalDuration], [timelineElements, dimensions, totalDuration],
); );
// Memoize the FFmpeg command
const ffmpegCommand = useMemo(() => { const ffmpegCommand = useMemo(() => {
return generateFFmpegCommand(true, false); return generateFFmpegCommand(true, false);
}, [generateFFmpegCommand]); }, [generateFFmpegCommand]);
// Memoize the copy function
const copyFFmpegCommand = useCallback(() => { const copyFFmpegCommand = useCallback(() => {
console.log('🎬 FFMPEG COMMAND GENERATED:'); console.log('🎬 FFMPEG COMMAND GENERATED:');
console.log('Command:', ffmpegCommand); console.log('Command:', ffmpegCommand);
navigator.clipboard.writeText(ffmpegCommand); navigator.clipboard.writeText(ffmpegCommand);
}, [ffmpegCommand]); }, [ffmpegCommand]);
// Create video elements
// Replace your existing useEffect with this:
useEffect(() => { useEffect(() => {
const videoEls = {}; const videoEls = {};
const videoElementsData = timelineElements.filter((el) => el.type === 'video'); const videoElementsData = timelineElements.filter((el) => el.type === 'video');
@@ -282,13 +261,11 @@ const VideoEditor = ({ width, height }) => {
video.appendChild(sourceMov); video.appendChild(sourceMov);
video.appendChild(sourceWebM); video.appendChild(sourceWebM);
// Load poster separately
const posterImg = new Image(); const posterImg = new Image();
posterImg.crossOrigin = 'anonymous'; posterImg.crossOrigin = 'anonymous';
posterImg.src = element.poster; posterImg.src = element.poster;
posterImg.onload = () => { posterImg.onload = () => {
// Calculate scaling for poster
const maxWidth = dimensions.width; const maxWidth = dimensions.width;
const maxHeight = dimensions.height; const maxHeight = dimensions.height;
const posterWidth = posterImg.naturalWidth; const posterWidth = posterImg.naturalWidth;
@@ -309,7 +286,6 @@ const VideoEditor = ({ width, height }) => {
const centeredX = (maxWidth - scaledWidth) / 2; const centeredX = (maxWidth - scaledWidth) / 2;
const centeredY = (maxHeight - scaledHeight) / 2; const centeredY = (maxHeight - scaledHeight) / 2;
// Update timeline element with poster
setTimelineElements((prev) => setTimelineElements((prev) =>
prev.map((el) => { prev.map((el) => {
if (el.id === element.id && el.type === 'video') { if (el.id === element.id && el.type === 'video') {
@@ -319,8 +295,8 @@ const VideoEditor = ({ width, height }) => {
y: centeredY, y: centeredY,
width: scaledWidth, width: scaledWidth,
height: scaledHeight, height: scaledHeight,
posterImage: posterImg, // Store poster reference posterImage: posterImg,
isVideoPoster: true, // Flag to indicate poster is loaded isVideoPoster: true,
}; };
} }
return el; return el;
@@ -335,14 +311,13 @@ const VideoEditor = ({ width, height }) => {
}; };
video.addEventListener('loadedmetadata', () => { video.addEventListener('loadedmetadata', () => {
// Video metadata loaded - store video reference
setTimelineElements((prev) => setTimelineElements((prev) =>
prev.map((el) => { prev.map((el) => {
if (el.id === element.id && el.type === 'video') { if (el.id === element.id && el.type === 'video') {
return { return {
...el, ...el,
videoElement: video, // Store video reference videoElement: video,
isVideoReady: true, // Flag to indicate video is ready isVideoReady: true,
}; };
} }
return el; return el;
@@ -369,9 +344,8 @@ const VideoEditor = ({ width, height }) => {
video.load(); video.load();
}); });
}; };
}, [dimensions.width, dimensions.height]); // Add dimensions as dependency }, [dimensions.width, dimensions.height]);
// Update status when videos load
useEffect(() => { useEffect(() => {
const videoCount = timelineElements.filter((el) => el.type === 'video').length; const videoCount = timelineElements.filter((el) => el.type === 'video').length;
if (loadedVideos.size === videoCount && videoCount > 0) { if (loadedVideos.size === videoCount && videoCount > 0) {
@@ -383,12 +357,12 @@ const VideoEditor = ({ width, height }) => {
} }
}, [loadedVideos, timelineElements]); }, [loadedVideos, timelineElements]);
// FIXED: Removed currentTime dependency to prevent excessive recreation
const handlePause = useCallback(() => { const handlePause = useCallback(() => {
if (isPlaying) { if (isPlaying) {
setIsPlaying(false); setIsPlaying(false);
pausedTimeRef.current = currentTime; pausedTimeRef.current = currentTime;
// Pause and mute all videos when pausing timeline
Object.values(videoElements).forEach((video) => { Object.values(videoElements).forEach((video) => {
if (!video.paused) { if (!video.paused) {
video.pause(); video.pause();
@@ -396,14 +370,14 @@ const VideoEditor = ({ width, height }) => {
video.muted = true; video.muted = true;
}); });
// Reset video states tracking
setVideoStates({}); setVideoStates({});
if (animationRef.current) { if (animationRef.current) {
animationRef.current.stop(); animationRef.current.stop();
animationRef.current = null;
} }
} }
}, [isPlaying, currentTime, videoElements]); }, [isPlaying, videoElements]);
const exportVideo = async () => { const exportVideo = async () => {
setIsExporting(true); setIsExporting(true);
@@ -443,13 +417,11 @@ const VideoEditor = ({ width, height }) => {
console.log('FFmpeg loaded!'); console.log('FFmpeg loaded!');
setExportProgress(20); setExportProgress(20);
// Write arial.ttf font into FFmpeg FS (fetch from GitHub)
setExportStatus('Loading font...'); setExportStatus('Loading font...');
await ffmpeg.writeFile('arial.ttf', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/arial.ttf')); await ffmpeg.writeFile('arial.ttf', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/arial.ttf'));
console.log('Font loaded!'); console.log('Font loaded!');
setExportProgress(30); setExportProgress(30);
// Download videos
setExportStatus('Downloading videos...'); setExportStatus('Downloading videos...');
const videos = timelineElements.filter((el) => el.type === 'video'); const videos = timelineElements.filter((el) => el.type === 'video');
@@ -458,18 +430,12 @@ const VideoEditor = ({ width, height }) => {
setExportProgress(30 + Math.round(((i + 1) / videos.length) * 30)); setExportProgress(30 + Math.round(((i + 1) / videos.length) * 30));
} }
// Generate your FFmpeg command, but be sure to include fontfile=/arial.ttf in all drawtext filters
setExportStatus('Processing video...'); setExportStatus('Processing video...');
let args = generateFFmpegCommand(false, true); let args = generateFFmpegCommand(false, true);
// Example: if your command uses drawtext filters, add fontfile argument like:
// drawtext=fontfile=/arial.ttf:text='Your text':x=50:y=600:fontsize=24:fontcolor=white:borderw=1:bordercolor=black
// Make sure your generateFFmpegCommand function inserts this correctly.
setExportProgress(70); setExportProgress(70);
await ffmpeg.exec(args); await ffmpeg.exec(args);
// Download result
setExportStatus('Downloading...'); setExportStatus('Downloading...');
setExportProgress(90); setExportProgress(90);
@@ -501,7 +467,6 @@ const VideoEditor = ({ width, height }) => {
} }
}; };
// Get currently active elements based on timeline position
const getActiveElements = useCallback( const getActiveElements = useCallback(
(time) => { (time) => {
return timelineElements.filter((element) => { return timelineElements.filter((element) => {
@@ -512,10 +477,8 @@ const VideoEditor = ({ width, height }) => {
[timelineElements], [timelineElements],
); );
// Calculate which videos should be playing based on current time
const getDesiredVideoStates = useCallback( const getDesiredVideoStates = useCallback(
(time) => { (time) => {
// Accept time as parameter
const states = {}; const states = {};
timelineElements.forEach((element) => { timelineElements.forEach((element) => {
if (element.type === 'video') { if (element.type === 'video') {
@@ -525,10 +488,9 @@ const VideoEditor = ({ width, height }) => {
}); });
return states; return states;
}, },
[timelineElements], // Removed dependency on currentTime [timelineElements],
); );
// Update video times based on timeline position - optimized to reduce seeking
const updateVideoTimes = useCallback( const updateVideoTimes = useCallback(
(time) => { (time) => {
timelineElements.forEach((element) => { timelineElements.forEach((element) => {
@@ -540,7 +502,6 @@ const VideoEditor = ({ width, height }) => {
const relativeTime = time - element.startTime; const relativeTime = time - element.startTime;
const videoTime = element.inPoint + relativeTime; const videoTime = element.inPoint + relativeTime;
// Only seek if time difference is significant
if (Math.abs(video.currentTime - videoTime) > 0.5) { if (Math.abs(video.currentTime - videoTime) > 0.5) {
video.currentTime = videoTime; video.currentTime = videoTime;
} }
@@ -551,13 +512,11 @@ const VideoEditor = ({ width, height }) => {
[timelineElements, videoElements], [timelineElements, videoElements],
); );
// OPTIMIZED: Manage video play/pause states only when needed
useEffect(() => { useEffect(() => {
if (!isPlaying) return; if (!isPlaying) return;
const desiredStates = getDesiredVideoStates(currentTime); const desiredStates = getDesiredVideoStates(currentTime);
// Smarter play/pause without excessive updates
Object.entries(desiredStates).forEach(([videoId, shouldPlay]) => { Object.entries(desiredStates).forEach(([videoId, shouldPlay]) => {
const video = videoElements[videoId]; const video = videoElements[videoId];
const isCurrentlyPlaying = !video?.paused; const isCurrentlyPlaying = !video?.paused;
@@ -576,137 +535,140 @@ const VideoEditor = ({ width, height }) => {
setVideoStates(desiredStates); setVideoStates(desiredStates);
}, [currentTime, isPlaying, videoElements, getDesiredVideoStates]); }, [currentTime, isPlaying, videoElements, getDesiredVideoStates]);
const animate = useCallback(() => { // FIXED: Properly stop animation when not playing
if (!animationRef.current || !isPlaying) return; useEffect(() => {
if (!isPlaying) {
const now = Date.now() / 1000; if (animationRef.current) {
const newTime = pausedTimeRef.current + (now - startTimeRef.current); animationRef.current.stop();
animationRef.current = null;
if (newTime >= totalDuration) { }
handlePause();
handleSeek(0); // ⬅️ Reset timeline
return; return;
} }
if (newTime - lastUpdateRef.current >= 0.05) { let animationId;
lastUpdateRef.current = newTime; let isRunning = true;
setCurrentTime(newTime);
updateVideoTimes(newTime);
if (layerRef.current) { const animateFrame = () => {
layerRef.current.batchDraw(); if (!isRunning) return;
const now = Date.now() / 1000;
const newTime = pausedTimeRef.current + (now - startTimeRef.current);
if (newTime >= totalDuration) {
handlePause();
handleSeek(0);
return;
} }
}
}, [isPlaying, totalDuration, updateVideoTimes, handlePause]);
// Start animation loop - using requestAnimationFrame for better performance if (newTime - lastUpdateRef.current >= 0.05) {
useEffect(() => { lastUpdateRef.current = newTime;
if (isPlaying) { setCurrentTime(newTime);
let animationId; updateVideoTimes(newTime);
const animateFrame = () => { if (layerRef.current) {
animate(); layerRef.current.batchDraw();
animationId = requestAnimationFrame(animateFrame);
};
animationId = requestAnimationFrame(animateFrame);
animationRef.current = { stop: () => cancelAnimationFrame(animationId) };
return () => {
if (animationRef.current) {
animationRef.current.stop();
} }
}; }
}
}, [isPlaying, animate]);
const handlePlay = () => { if (isRunning) {
animationId = requestAnimationFrame(animateFrame);
}
};
startTimeRef.current = Date.now() / 1000;
animationId = requestAnimationFrame(animateFrame);
animationRef.current = {
stop: () => {
isRunning = false;
if (animationId) {
cancelAnimationFrame(animationId);
}
},
};
return () => {
isRunning = false;
if (animationId) {
cancelAnimationFrame(animationId);
}
};
}, [isPlaying, totalDuration, handlePause, updateVideoTimes]);
// FIXED: Stabilized handlers
const handlePlay = useCallback(() => {
if (!isPlaying) { if (!isPlaying) {
setIsPlaying(true); setIsPlaying(true);
startTimeRef.current = Date.now() / 1000; startTimeRef.current = Date.now() / 1000;
lastUpdateRef.current = 0; // ✅ Reset debounce tracker lastUpdateRef.current = 0;
setStatus(''); setStatus('');
} }
}; }, [isPlaying]);
const handleSeek = (time) => { const handleSeek = useCallback(
const clampedTime = Math.max(0, Math.min(time, totalDuration)); (time) => {
setCurrentTime(clampedTime); const clampedTime = Math.max(0, Math.min(time, totalDuration));
pausedTimeRef.current = clampedTime; setCurrentTime(clampedTime);
updateVideoTimes(clampedTime); pausedTimeRef.current = clampedTime;
updateVideoTimes(clampedTime);
// Reset video states when seeking to force re-evaluation setVideoStates({});
setVideoStates({});
if (layerRef.current) { if (layerRef.current) {
layerRef.current.draw(); layerRef.current.draw();
} }
}; },
[totalDuration, updateVideoTimes],
);
const handleReset = () => { const handleReset = useCallback(() => {
handlePause(); handlePause();
handleSeek(0); handleSeek(0);
lastUpdateRef.current = 0; // ✅ Reset debounce tracker lastUpdateRef.current = 0;
// Ensure all videos are muted
Object.values(videoElements).forEach((video) => { Object.values(videoElements).forEach((video) => {
video.muted = true; video.muted = true;
}); });
}; }, [handlePause, handleSeek, videoElements]);
const activeElements = getActiveElements(currentTime); const activeElements = getActiveElements(currentTime);
// FIXED: Added missing dependencies to event listeners
useEffect(() => { useEffect(() => {
emitter.on('video-play', () => { emitter.on('video-play', handlePlay);
console.log('video-play'); emitter.on('video-reset', handleReset);
handlePlay(); emitter.on('video-seek', handleSeek);
});
emitter.on('video-pause', () => {
console.log('video-pause');
handlePause();
});
emitter.on('video-seek', (time) => {
handleSeek(time);
});
return () => { return () => {
emitter.off('video-play'); emitter.off('video-play', handlePlay);
emitter.off('video-pause'); emitter.off('video-reset', handleReset);
emitter.off('video-seek'); emitter.off('video-seek', handleSeek);
}; };
}, [emitter]); }, [emitter, handlePlay, handleReset, handleSeek]);
return ( return (
<div style={{ width: dimensions.width, height: dimensions.height }} className="rounded-3xl"> <div style={{ width: dimensions.width, height: dimensions.height }} className="rounded-3xl">
<VideoPreview <VideoPreview
// Dimensions
dimensions={dimensions} dimensions={dimensions}
// Timeline state
currentTime={currentTime} currentTime={currentTime}
totalDuration={totalDuration} totalDuration={totalDuration}
isPlaying={isPlaying} isPlaying={isPlaying}
status={status} status={status}
// Export state
isExporting={isExporting} isExporting={isExporting}
exportProgress={exportProgress} exportProgress={exportProgress}
exportStatus={exportStatus} exportStatus={exportStatus}
// Data
timelineElements={timelineElements} timelineElements={timelineElements}
activeElements={activeElements} activeElements={activeElements}
videoElements={videoElements} videoElements={videoElements}
loadedVideos={loadedVideos} loadedVideos={loadedVideos}
videoStates={videoStates} videoStates={videoStates}
ffmpegCommand={ffmpegCommand} ffmpegCommand={ffmpegCommand}
// Event handlers
handlePlay={handlePlay} handlePlay={handlePlay}
handlePause={handlePause} handlePause={handlePause}
handleReset={handleReset} handleReset={handleReset}
handleSeek={handleSeek} handleSeek={handleSeek}
copyFFmpegCommand={copyFFmpegCommand} copyFFmpegCommand={copyFFmpegCommand}
exportVideo={exportVideo} exportVideo={exportVideo}
// Refs
layerRef={layerRef} layerRef={layerRef}
/> />
</div> </div>

View File

@@ -56,8 +56,6 @@ const VideoPreview = ({
return ( return (
<div> <div>
<div>isPlaying: {isPlaying ? 'true' : 'false'}</div>
<Stage width={dimensions.width} height={dimensions.height} className=""> <Stage width={dimensions.width} height={dimensions.height} className="">
<Layer ref={layerRef}> <Layer ref={layerRef}>
{activeElements.map((element) => { {activeElements.map((element) => {

View File

@@ -4,7 +4,7 @@ import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { useMitt } from '@/plugins/MittContext'; import { useMitt } from '@/plugins/MittContext';
import useVideoEditorStore from '@/stores/VideoEditorStore'; import useVideoEditorStore from '@/stores/VideoEditorStore';
import { Download, Edit3, Pause, Play, Type } from 'lucide-react'; import { Download, Edit3, Play, Square, Type } from 'lucide-react';
const EditorControls = ({ className = '', onEditClick = () => {}, isEditActive = false }) => { const EditorControls = ({ className = '', onEditClick = () => {}, isEditActive = false }) => {
const { videoIsPlaying } = useVideoEditorStore(); const { videoIsPlaying } = useVideoEditorStore();
@@ -14,13 +14,13 @@ const EditorControls = ({ className = '', onEditClick = () => {}, isEditActive =
emitter.emit('video-play'); emitter.emit('video-play');
}; };
const handlePause = () => { const handleReset = () => {
emitter.emit('video-pause'); emitter.emit('video-reset');
}; };
const togglePlayPause = () => { const togglePlayPause = () => {
if (videoIsPlaying) { if (videoIsPlaying) {
handlePause(); handleReset();
} else { } else {
handlePlay(); handlePlay();
} }
@@ -29,7 +29,7 @@ const EditorControls = ({ className = '', onEditClick = () => {}, isEditActive =
return ( return (
<div className={cn('flex items-center justify-center gap-2', className)}> <div className={cn('flex items-center justify-center gap-2', className)}>
<Button onClick={togglePlayPause} variant="ghost" size="icon" className="h-12 w-12 rounded-full border shadow-sm"> <Button onClick={togglePlayPause} variant="ghost" size="icon" className="h-12 w-12 rounded-full border shadow-sm">
{videoIsPlaying ? <Pause className="h-8 w-8" /> : <Play className="h-8 w-8" />} {videoIsPlaying ? <Square className="h-8 w-8" /> : <Play className="h-8 w-8" />}
</Button> </Button>
{/* <Button {/* <Button