mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-11 01:48:53 -05:00
play segments (duplicate of preview)
This commit is contained in:
parent
8390aa3ae0
commit
39fcf3c97c
@ -42,6 +42,8 @@ const App = () => {
|
|||||||
isMobile,
|
isMobile,
|
||||||
videoInitialized,
|
videoInitialized,
|
||||||
setVideoInitialized,
|
setVideoInitialized,
|
||||||
|
isPlayingSegments,
|
||||||
|
handlePlaySegments,
|
||||||
} = useVideoTrimmer();
|
} = useVideoTrimmer();
|
||||||
|
|
||||||
// Function to play from the beginning
|
// Function to play from the beginning
|
||||||
@ -250,9 +252,11 @@ const App = () => {
|
|||||||
onUndo={handleUndo}
|
onUndo={handleUndo}
|
||||||
onRedo={handleRedo}
|
onRedo={handleRedo}
|
||||||
onPreview={handlePreview}
|
onPreview={handlePreview}
|
||||||
|
onPlaySegments={handlePlaySegments}
|
||||||
onPlay={handlePlay}
|
onPlay={handlePlay}
|
||||||
isPreviewMode={isPreviewMode}
|
isPreviewMode={isPreviewMode}
|
||||||
isPlaying={isPlaying}
|
isPlaying={isPlaying}
|
||||||
|
isPlayingSegments={isPlayingSegments}
|
||||||
canUndo={historyPosition > 0}
|
canUndo={historyPosition > 0}
|
||||||
canRedo={historyPosition < history.length - 1}
|
canRedo={historyPosition < history.length - 1}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -6,11 +6,13 @@ interface EditingToolsProps {
|
|||||||
onUndo: () => void;
|
onUndo: () => void;
|
||||||
onRedo: () => void;
|
onRedo: () => void;
|
||||||
onPreview: () => void;
|
onPreview: () => void;
|
||||||
|
onPlaySegments: () => void;
|
||||||
onPlay: () => void;
|
onPlay: () => void;
|
||||||
canUndo: boolean;
|
canUndo: boolean;
|
||||||
canRedo: boolean;
|
canRedo: boolean;
|
||||||
isPreviewMode?: boolean;
|
isPreviewMode?: boolean;
|
||||||
isPlaying?: boolean;
|
isPlaying?: boolean;
|
||||||
|
isPlayingSegments?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditingTools = ({
|
const EditingTools = ({
|
||||||
@ -19,11 +21,13 @@ const EditingTools = ({
|
|||||||
onUndo,
|
onUndo,
|
||||||
onRedo,
|
onRedo,
|
||||||
onPreview,
|
onPreview,
|
||||||
|
onPlaySegments,
|
||||||
onPlay,
|
onPlay,
|
||||||
canUndo,
|
canUndo,
|
||||||
canRedo,
|
canRedo,
|
||||||
isPreviewMode = false,
|
isPreviewMode = false,
|
||||||
isPlaying = false,
|
isPlaying = false,
|
||||||
|
isPlayingSegments = false,
|
||||||
}: EditingToolsProps) => {
|
}: EditingToolsProps) => {
|
||||||
// Handle play button click with iOS fix
|
// Handle play button click with iOS fix
|
||||||
const handlePlay = () => {
|
const handlePlay = () => {
|
||||||
@ -41,6 +45,35 @@ const EditingTools = ({
|
|||||||
<div className="flex-container single-row">
|
<div className="flex-container single-row">
|
||||||
{/* Left side - Play buttons group */}
|
{/* Left side - Play buttons group */}
|
||||||
<div className="button-group play-buttons-group">
|
<div className="button-group play-buttons-group">
|
||||||
|
{/* Play Segments button */}
|
||||||
|
<button
|
||||||
|
className="button segments-button"
|
||||||
|
onClick={onPlaySegments}
|
||||||
|
data-tooltip={isPlayingSegments ? "Stop segments playback" : "Play all segments once"}
|
||||||
|
style={{ fontSize: '0.875rem' }}
|
||||||
|
>
|
||||||
|
{isPlayingSegments ? (
|
||||||
|
<>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<line x1="10" y1="15" x2="10" y2="9" />
|
||||||
|
<line x1="14" y1="15" x2="14" y2="9" />
|
||||||
|
</svg>
|
||||||
|
<span className="full-text">Stop Segments</span>
|
||||||
|
<span className="short-text">Stop</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<polygon points="10 8 16 12 10 16 10 8" />
|
||||||
|
</svg>
|
||||||
|
<span className="full-text">Play Segments</span>
|
||||||
|
<span className="short-text">Segments</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
{/* Play Preview button */}
|
{/* Play Preview button */}
|
||||||
<button
|
<button
|
||||||
className="button preview-button"
|
className="button preview-button"
|
||||||
|
|||||||
@ -42,6 +42,10 @@ const useVideoTrimmer = () => {
|
|||||||
// Track unsaved changes
|
// Track unsaved changes
|
||||||
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
||||||
|
|
||||||
|
// State for playing segments
|
||||||
|
const [isPlayingSegments, setIsPlayingSegments] = useState(false);
|
||||||
|
const [currentSegmentIndex, setCurrentSegmentIndex] = useState(0);
|
||||||
|
|
||||||
// Monitor for history changes
|
// Monitor for history changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (history.length > 0) {
|
if (history.length > 0) {
|
||||||
@ -550,68 +554,149 @@ const useVideoTrimmer = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isPreviewMode || !videoRef.current) return;
|
if (!isPreviewMode || !videoRef.current) return;
|
||||||
|
|
||||||
// logger.debug("Preview mode effect running, previewSegmentIndex:", previewSegmentIndex);
|
|
||||||
|
|
||||||
// Sort segments by start time
|
// Sort segments by start time
|
||||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||||
|
if (orderedSegments.length === 0) return;
|
||||||
|
|
||||||
// Check if we've reached the end of the current segment
|
const video = videoRef.current;
|
||||||
|
|
||||||
|
// Function to handle segment playback
|
||||||
const handleSegmentPlayback = () => {
|
const handleSegmentPlayback = () => {
|
||||||
if (!isPreviewMode || !videoRef.current) return;
|
if (!isPreviewMode || !video) return;
|
||||||
|
|
||||||
const currentSegment = orderedSegments[previewSegmentIndex];
|
const currentSegment = orderedSegments[previewSegmentIndex];
|
||||||
if (!currentSegment) return;
|
if (!currentSegment) return;
|
||||||
|
|
||||||
// Make sure we're inside the current segment
|
const currentTime = video.currentTime;
|
||||||
const currentTime = videoRef.current.currentTime;
|
|
||||||
|
|
||||||
// If the current time is before the segment start, move to the segment start
|
// If we're before the current segment's start, jump to it
|
||||||
if (currentTime < currentSegment.startTime) {
|
if (currentTime < currentSegment.startTime) {
|
||||||
videoRef.current.currentTime = currentSegment.startTime;
|
video.currentTime = currentSegment.startTime;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we've reached the end of the current segment
|
// If we've reached the end of the current segment
|
||||||
if (currentTime >= currentSegment.endTime - 0.05) { // Slightly before the end to ensure smooth transition
|
if (currentTime >= currentSegment.endTime - 0.01) { // Small threshold to ensure smooth transition
|
||||||
// Move to the next segment if available
|
// Move to the next segment if available
|
||||||
if (previewSegmentIndex < orderedSegments.length - 1) {
|
if (previewSegmentIndex < orderedSegments.length - 1) {
|
||||||
|
// Play next segment
|
||||||
const nextSegment = orderedSegments[previewSegmentIndex + 1];
|
const nextSegment = orderedSegments[previewSegmentIndex + 1];
|
||||||
|
video.currentTime = nextSegment.startTime;
|
||||||
// Play through the next segment immediately
|
|
||||||
videoRef.current.currentTime = nextSegment.startTime;
|
|
||||||
setPreviewSegmentIndex(previewSegmentIndex + 1);
|
setPreviewSegmentIndex(previewSegmentIndex + 1);
|
||||||
logger.debug("Moving to next segment in preview:", nextSegment.name,
|
|
||||||
"from", formatDetailedTime(currentSegment.endTime),
|
|
||||||
"to", formatDetailedTime(nextSegment.startTime));
|
|
||||||
|
|
||||||
// Ensure playback continues
|
logger.debug("Preview: Moving to next segment", {
|
||||||
videoRef.current.play().catch(err => {
|
from: formatDetailedTime(currentSegment.endTime),
|
||||||
console.error("Error continuing preview playback:", err);
|
to: formatDetailedTime(nextSegment.startTime),
|
||||||
|
segmentIndex: previewSegmentIndex + 1
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// End of all segments, loop back to the first segment
|
// Loop back to first segment
|
||||||
logger.debug("End of all segments in preview, looping back to first");
|
logger.debug("Preview: Looping back to first segment");
|
||||||
videoRef.current.currentTime = orderedSegments[0].startTime;
|
video.currentTime = orderedSegments[0].startTime;
|
||||||
setPreviewSegmentIndex(0);
|
setPreviewSegmentIndex(0);
|
||||||
|
|
||||||
// Ensure playback continues
|
|
||||||
videoRef.current.play().catch(err => {
|
|
||||||
console.error("Error restarting preview playback:", err);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure playback continues
|
||||||
|
video.play().catch(err => {
|
||||||
|
console.error("Error continuing preview playback:", err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add event listener for timeupdate to check segment boundaries
|
// Add event listener for timeupdate to check segment boundaries
|
||||||
videoRef.current.addEventListener('timeupdate', handleSegmentPlayback);
|
video.addEventListener('timeupdate', handleSegmentPlayback);
|
||||||
|
|
||||||
|
// Start playing if not already playing
|
||||||
|
if (video.paused) {
|
||||||
|
video.currentTime = orderedSegments[previewSegmentIndex].startTime;
|
||||||
|
video.play().catch(err => {
|
||||||
|
console.error("Error starting preview playback:", err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (videoRef.current) {
|
if (video) {
|
||||||
videoRef.current.removeEventListener('timeupdate', handleSegmentPlayback);
|
video.removeEventListener('timeupdate', handleSegmentPlayback);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [isPreviewMode, previewSegmentIndex, clipSegments]);
|
}, [isPreviewMode, previewSegmentIndex, clipSegments]);
|
||||||
|
|
||||||
|
// Handle starting preview mode
|
||||||
|
const handleStartPreview = () => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video || clipSegments.length === 0) return;
|
||||||
|
|
||||||
|
// If preview is already active, do nothing
|
||||||
|
if (isPreviewMode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If normal playback is happening, pause it
|
||||||
|
if (isPlaying) {
|
||||||
|
video.pause();
|
||||||
|
setIsPlaying(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort segments by start time
|
||||||
|
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||||
|
if (orderedSegments.length === 0) return;
|
||||||
|
|
||||||
|
// Set the preview mode flag
|
||||||
|
setIsPreviewMode(true);
|
||||||
|
logger.debug("Entering preview mode");
|
||||||
|
|
||||||
|
// Set the first segment as the current one in the preview sequence
|
||||||
|
setPreviewSegmentIndex(0);
|
||||||
|
|
||||||
|
// Move to the start of the first segment
|
||||||
|
video.currentTime = orderedSegments[0].startTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle playing/stopping preview mode
|
||||||
|
const handlePreview = () => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video || clipSegments.length === 0) return;
|
||||||
|
|
||||||
|
// If preview is already active, turn it off
|
||||||
|
if (isPreviewMode) {
|
||||||
|
setIsPreviewMode(false);
|
||||||
|
|
||||||
|
// Always pause the video when exiting preview mode
|
||||||
|
video.pause();
|
||||||
|
setIsPlaying(false);
|
||||||
|
|
||||||
|
logger.debug("Exiting preview mode - video paused");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort segments by start time
|
||||||
|
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||||
|
if (orderedSegments.length === 0) return;
|
||||||
|
|
||||||
|
// Set the preview mode flag
|
||||||
|
setIsPreviewMode(true);
|
||||||
|
logger.debug("Entering preview mode");
|
||||||
|
|
||||||
|
// Set the first segment as the current one in the preview sequence
|
||||||
|
setPreviewSegmentIndex(0);
|
||||||
|
|
||||||
|
// Start preview mode by playing the first segment
|
||||||
|
video.currentTime = orderedSegments[0].startTime;
|
||||||
|
|
||||||
|
// Start playback
|
||||||
|
video.play()
|
||||||
|
.then(() => {
|
||||||
|
setIsPlaying(true);
|
||||||
|
logger.debug("Preview started successfully");
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error("Error starting preview:", err);
|
||||||
|
setIsPreviewMode(false);
|
||||||
|
setIsPlaying(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Handle trim start change
|
// Handle trim start change
|
||||||
const handleTrimStartChange = (time: number) => {
|
const handleTrimStartChange = (time: number) => {
|
||||||
setTrimStart(time);
|
setTrimStart(time);
|
||||||
@ -725,60 +810,6 @@ const useVideoTrimmer = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle playing the preview of all segments
|
|
||||||
const handlePreview = () => {
|
|
||||||
const video = videoRef.current;
|
|
||||||
if (!video || clipSegments.length === 0) return;
|
|
||||||
|
|
||||||
// If preview is already active, turn it off
|
|
||||||
if (isPreviewMode) {
|
|
||||||
setIsPreviewMode(false);
|
|
||||||
|
|
||||||
// Always pause the video when exiting preview mode
|
|
||||||
video.pause();
|
|
||||||
setIsPlaying(false);
|
|
||||||
|
|
||||||
logger.debug("Exiting preview mode - video paused");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If normal playback is happening, remember that state but pause it
|
|
||||||
const wasPlaying = isPlaying;
|
|
||||||
if (wasPlaying) {
|
|
||||||
video.pause();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort segments by start time to ensure proper playback order
|
|
||||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
|
||||||
if (orderedSegments.length === 0) return;
|
|
||||||
|
|
||||||
// Set the preview mode flag before starting playback
|
|
||||||
setIsPreviewMode(true);
|
|
||||||
logger.debug("Entering preview mode");
|
|
||||||
|
|
||||||
// Set the first segment as the current one in the preview sequence
|
|
||||||
setPreviewSegmentIndex(0);
|
|
||||||
|
|
||||||
// Start preview mode by playing the first segment
|
|
||||||
video.currentTime = orderedSegments[0].startTime;
|
|
||||||
|
|
||||||
// Force a small delay before playing to ensure the preview mode state is set
|
|
||||||
setTimeout(() => {
|
|
||||||
if (videoRef.current) {
|
|
||||||
videoRef.current.play()
|
|
||||||
.then(() => {
|
|
||||||
logger.debug("Preview started successfully");
|
|
||||||
setIsPlaying(true);
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error("Error starting preview:", err);
|
|
||||||
setIsPreviewMode(false); // Reset preview mode if play fails
|
|
||||||
setIsPlaying(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 50); // Short delay to ensure state updates have processed
|
|
||||||
};
|
|
||||||
|
|
||||||
// Handle zoom level change
|
// Handle zoom level change
|
||||||
const handleZoomChange = (level: number) => {
|
const handleZoomChange = (level: number) => {
|
||||||
setZoomLevel(level);
|
setZoomLevel(level);
|
||||||
@ -947,6 +978,75 @@ const useVideoTrimmer = () => {
|
|||||||
// Add videoInitialized state
|
// Add videoInitialized state
|
||||||
const [videoInitialized, setVideoInitialized] = useState(false);
|
const [videoInitialized, setVideoInitialized] = useState(false);
|
||||||
|
|
||||||
|
// Effect to handle segments playback
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isPlayingSegments || !videoRef.current) return;
|
||||||
|
|
||||||
|
const video = videoRef.current;
|
||||||
|
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||||
|
|
||||||
|
const handleSegmentsPlayback = () => {
|
||||||
|
const currentSegment = orderedSegments[currentSegmentIndex];
|
||||||
|
if (!currentSegment) return;
|
||||||
|
|
||||||
|
const currentTime = video.currentTime;
|
||||||
|
|
||||||
|
// If we're before the current segment's start, jump to it
|
||||||
|
if (currentTime < currentSegment.startTime) {
|
||||||
|
video.currentTime = currentSegment.startTime;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've reached the end of the current segment
|
||||||
|
if (currentTime >= currentSegment.endTime - 0.01) {
|
||||||
|
if (currentSegmentIndex < orderedSegments.length - 1) {
|
||||||
|
// Move to next segment
|
||||||
|
const nextSegment = orderedSegments[currentSegmentIndex + 1];
|
||||||
|
video.currentTime = nextSegment.startTime;
|
||||||
|
setCurrentSegmentIndex(currentSegmentIndex + 1);
|
||||||
|
video.play().catch(console.error);
|
||||||
|
} else {
|
||||||
|
// End of all segments
|
||||||
|
video.pause();
|
||||||
|
setIsPlayingSegments(false);
|
||||||
|
setCurrentSegmentIndex(0);
|
||||||
|
video.removeEventListener('timeupdate', handleSegmentsPlayback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
video.addEventListener('timeupdate', handleSegmentsPlayback);
|
||||||
|
|
||||||
|
// Start playing if not already playing
|
||||||
|
if (video.paused && orderedSegments.length > 0) {
|
||||||
|
video.currentTime = orderedSegments[0].startTime;
|
||||||
|
video.play().catch(console.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
video.removeEventListener('timeupdate', handleSegmentsPlayback);
|
||||||
|
};
|
||||||
|
}, [isPlayingSegments, currentSegmentIndex, clipSegments]);
|
||||||
|
|
||||||
|
// Handle play segments
|
||||||
|
const handlePlaySegments = () => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video || clipSegments.length === 0) return;
|
||||||
|
|
||||||
|
if (isPlayingSegments) {
|
||||||
|
// Stop segments playback
|
||||||
|
video.pause();
|
||||||
|
setIsPlayingSegments(false);
|
||||||
|
setCurrentSegmentIndex(0);
|
||||||
|
} else {
|
||||||
|
// Start segments playback
|
||||||
|
setIsPlayingSegments(true);
|
||||||
|
setCurrentSegmentIndex(0);
|
||||||
|
video.currentTime = clipSegments[0].startTime;
|
||||||
|
video.play().catch(console.error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
videoRef,
|
videoRef,
|
||||||
currentTime,
|
currentTime,
|
||||||
@ -955,6 +1055,7 @@ const useVideoTrimmer = () => {
|
|||||||
setIsPlaying,
|
setIsPlaying,
|
||||||
isMuted,
|
isMuted,
|
||||||
isPreviewMode,
|
isPreviewMode,
|
||||||
|
isPlayingSegments,
|
||||||
thumbnails,
|
thumbnails,
|
||||||
trimStart,
|
trimStart,
|
||||||
trimEnd,
|
trimEnd,
|
||||||
@ -973,6 +1074,7 @@ const useVideoTrimmer = () => {
|
|||||||
handleUndo,
|
handleUndo,
|
||||||
handleRedo,
|
handleRedo,
|
||||||
handlePreview,
|
handlePreview,
|
||||||
|
handlePlaySegments,
|
||||||
toggleMute,
|
toggleMute,
|
||||||
handleSave,
|
handleSave,
|
||||||
handleSaveACopy,
|
handleSaveACopy,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user