import { formatDetailedTime } from './lib/timeUtils'; import logger from './lib/logger'; import VideoPlayer from '@/components/VideoPlayer'; import TimelineControls from '@/components/TimelineControls'; import EditingTools from '@/components/EditingTools'; import ClipSegments from '@/components/ClipSegments'; import MobilePlayPrompt from '@/components/IOSPlayPrompt'; import useVideoChapters from '@/hooks/useVideoChapters'; const App = () => { const { videoRef, currentTime, duration, isPlaying, setIsPlaying, isMuted, thumbnails, trimStart, trimEnd, splitPoints, zoomLevel, clipSegments, selectedSegmentId, hasUnsavedChanges, historyPosition, history, handleTrimStartChange, handleTrimEndChange, handleZoomChange, handleMobileSafeSeek, handleSplit, handleReset, handleUndo, handleRedo, toggleMute, handleSegmentUpdate, handleChapterSave, handleSelectedSegmentChange, isMobile, videoInitialized, setVideoInitialized, isPlayingSegments, handlePlaySegments, } = useVideoChapters(); const handlePlay = () => { if (!videoRef.current) return; const video = videoRef.current; // If already playing, just pause the video if (isPlaying) { video.pause(); setIsPlaying(false); return; } const currentPosition = Number(video.currentTime.toFixed(6)); // Find the next stopping point based on current position let stopTime = duration; let currentSegment = null; let nextSegment = null; // Sort segments by start time to ensure correct order const sortedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime); // First, check if we're inside a segment or exactly at its start/end currentSegment = sortedSegments.find((seg) => { const segStartTime = Number(seg.startTime.toFixed(6)); const segEndTime = Number(seg.endTime.toFixed(6)); // Check if we're inside the segment if (currentPosition > segStartTime && currentPosition < segEndTime) { return true; } // Check if we're exactly at the start if (currentPosition === segStartTime) { return true; } // Check if we're exactly at the end if (currentPosition === segEndTime) { // If we're at the end of a segment, we should look for the next one return false; } return false; }); // If we're not in a segment, find the next segment if (!currentSegment) { nextSegment = sortedSegments.find((seg) => { const segStartTime = Number(seg.startTime.toFixed(6)); return segStartTime > currentPosition; }); } // Determine where to stop based on position if (currentSegment) { // If we're in a segment, stop at its end stopTime = Number(currentSegment.endTime.toFixed(6)); } else if (nextSegment) { // If we're in a cutaway and there's a next segment, stop at its start stopTime = Number(nextSegment.startTime.toFixed(6)); } // Create a boundary checker function with high precision const checkBoundary = () => { if (!video) return; const currentPosition = Number(video.currentTime.toFixed(6)); const timeLeft = Number((stopTime - currentPosition).toFixed(6)); // If we've reached or passed the boundary if (timeLeft <= 0 || currentPosition >= stopTime) { // First pause playback video.pause(); // Force exact position with multiple verification attempts const setExactPosition = () => { if (!video) return; // Set to exact boundary time video.currentTime = stopTime; handleMobileSafeSeek(stopTime); const actualPosition = Number(video.currentTime.toFixed(6)); const difference = Number(Math.abs(actualPosition - stopTime).toFixed(6)); logger.debug('Position verification:', { target: formatDetailedTime(stopTime), actual: formatDetailedTime(actualPosition), difference: difference, }); // If we're not exactly at the target position, try one more time if (difference > 0) { video.currentTime = stopTime; handleMobileSafeSeek(stopTime); } }; // Multiple attempts to ensure precision, with increasing delays setExactPosition(); setTimeout(setExactPosition, 5); // Quick first retry setTimeout(setExactPosition, 10); // Second retry setTimeout(setExactPosition, 20); // Third retry if needed setTimeout(setExactPosition, 50); // Final verification // Remove our boundary checker video.removeEventListener('timeupdate', checkBoundary); setIsPlaying(false); // Log the final position for debugging logger.debug('Stopped at position:', { target: formatDetailedTime(stopTime), actual: formatDetailedTime(video.currentTime), type: currentSegment ? 'segment end' : nextSegment ? 'next segment start' : 'end of video', segment: currentSegment ? { id: currentSegment.id, start: formatDetailedTime(currentSegment.startTime), end: formatDetailedTime(currentSegment.endTime), } : null, nextSegment: nextSegment ? { id: nextSegment.id, start: formatDetailedTime(nextSegment.startTime), end: formatDetailedTime(nextSegment.endTime), } : null, }); return; } }; // Start our boundary checker video.addEventListener('timeupdate', checkBoundary); // Start playing video .play() .then(() => { setIsPlaying(true); setVideoInitialized(true); logger.debug('Playback started:', { from: formatDetailedTime(currentPosition), to: formatDetailedTime(stopTime), currentSegment: currentSegment ? { id: currentSegment.id, start: formatDetailedTime(currentSegment.startTime), end: formatDetailedTime(currentSegment.endTime), } : 'None', nextSegment: nextSegment ? { id: nextSegment.id, start: formatDetailedTime(nextSegment.startTime), end: formatDetailedTime(nextSegment.endTime), } : 'None', }); }) .catch((err) => { console.error('Error playing video:', err); }); }; return (
{/* Video Player */} {/* Editing Tools */} 0} canRedo={historyPosition < history.length - 1} /> {/* Timeline Controls */} {/* Clip Segments */}
); }; export default App;