mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-05 23:18:53 -05:00
fix: Disable Segment Tools and Reset Preview State During Playback
This commit is contained in:
parent
a5acce4ab1
commit
1505a155e6
2
.gitignore
vendored
2
.gitignore
vendored
@ -25,3 +25,5 @@ yt.readme.md
|
||||
frontend-tools/.DS_Store
|
||||
static/video_editor/videos/sample-video-30s.mp4
|
||||
static/video_editor/videos/sample-video-37s.mp4
|
||||
/frontend-tools/video-editor-v2
|
||||
.DS_Store
|
||||
|
||||
3
frontend-tools/video-editor/.gitignore
vendored
3
frontend-tools/video-editor/.gitignore
vendored
@ -10,3 +10,6 @@ client/public/videos/sample-video-30s.mp4
|
||||
client/public/videos/sample-video-37s.mp4
|
||||
videos/sample-video-37s.mp4
|
||||
client/public/videos/sample-video-30s.mp4
|
||||
client/public/videos/sample-video-1.mp4
|
||||
client/public/videos/sample-video-10m.mp4
|
||||
client/public/videos/sample-video-10s.mp4
|
||||
|
||||
@ -16,7 +16,6 @@ const App = () => {
|
||||
isPlaying,
|
||||
setIsPlaying,
|
||||
isMuted,
|
||||
isPreviewMode,
|
||||
thumbnails,
|
||||
trimStart,
|
||||
trimEnd,
|
||||
@ -34,7 +33,6 @@ const App = () => {
|
||||
handleReset,
|
||||
handleUndo,
|
||||
handleRedo,
|
||||
handlePreview,
|
||||
toggleMute,
|
||||
handleSave,
|
||||
handleSaveACopy,
|
||||
@ -251,10 +249,8 @@ const App = () => {
|
||||
onReset={handleReset}
|
||||
onUndo={handleUndo}
|
||||
onRedo={handleRedo}
|
||||
onPreview={handlePreview}
|
||||
onPlaySegments={handlePlaySegments}
|
||||
onPlay={handlePlay}
|
||||
isPreviewMode={isPreviewMode}
|
||||
isPlaying={isPlaying}
|
||||
isPlayingSegments={isPlayingSegments}
|
||||
canUndo={historyPosition > 0}
|
||||
@ -279,7 +275,6 @@ const App = () => {
|
||||
onSave={handleSave}
|
||||
onSaveACopy={handleSaveACopy}
|
||||
onSaveSegments={handleSaveSegments}
|
||||
isPreviewMode={isPreviewMode}
|
||||
hasUnsavedChanges={hasUnsavedChanges}
|
||||
isIOSUninitialized={isMobile && !videoInitialized}
|
||||
isPlaying={isPlaying}
|
||||
|
||||
@ -6,12 +6,10 @@ interface EditingToolsProps {
|
||||
onReset: () => void;
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
onPreview: () => void;
|
||||
onPlaySegments: () => void;
|
||||
onPlay: () => void;
|
||||
canUndo: boolean;
|
||||
canRedo: boolean;
|
||||
isPreviewMode?: boolean;
|
||||
isPlaying?: boolean;
|
||||
isPlayingSegments?: boolean;
|
||||
}
|
||||
@ -21,12 +19,10 @@ const EditingTools = ({
|
||||
onReset,
|
||||
onUndo,
|
||||
onRedo,
|
||||
onPreview,
|
||||
onPlaySegments,
|
||||
onPlay,
|
||||
canUndo,
|
||||
canRedo,
|
||||
isPreviewMode = false,
|
||||
isPlaying = false,
|
||||
isPlayingSegments = false,
|
||||
}: EditingToolsProps) => {
|
||||
@ -116,8 +112,8 @@ const EditingTools = ({
|
||||
)}
|
||||
</button> */}
|
||||
|
||||
{/* Standard Play button (only shown when not in preview mode or segments playback) */}
|
||||
{!isPreviewMode && (!isPlayingSegments || !isSmallScreen) && (
|
||||
{/* Standard Play button (only shown when not in segments playback on small screens) */}
|
||||
{(!isPlayingSegments || !isSmallScreen) && (
|
||||
<button
|
||||
className={`button play-button ${isPlayingSegments ? 'greyed-out' : ''}`}
|
||||
onClick={handlePlay}
|
||||
@ -125,7 +121,7 @@ const EditingTools = ({
|
||||
style={{ fontSize: '0.875rem' }}
|
||||
disabled={isPlayingSegments}
|
||||
>
|
||||
{isPlaying ? (
|
||||
{isPlaying && !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" />
|
||||
|
||||
@ -37,7 +37,7 @@ const IOSVideoPlayer = ({
|
||||
}
|
||||
} else {
|
||||
// Fallback to sample video if needed
|
||||
setVideoUrl("/videos/sample-video-37s.mp4");
|
||||
setVideoUrl("/videos/sample-video-10m.mp4");
|
||||
}
|
||||
}, [videoRef]);
|
||||
|
||||
|
||||
@ -43,7 +43,6 @@ interface TimelineControlsProps {
|
||||
onSave?: () => void;
|
||||
onSaveACopy?: () => void;
|
||||
onSaveSegments?: () => void;
|
||||
isPreviewMode?: boolean;
|
||||
hasUnsavedChanges?: boolean;
|
||||
isIOSUninitialized?: boolean;
|
||||
isPlaying: boolean;
|
||||
@ -116,7 +115,6 @@ const TimelineControls = ({
|
||||
onSave,
|
||||
onSaveACopy,
|
||||
onSaveSegments,
|
||||
isPreviewMode,
|
||||
hasUnsavedChanges = false,
|
||||
isIOSUninitialized = false,
|
||||
isPlaying,
|
||||
@ -572,12 +570,11 @@ const TimelineControls = ({
|
||||
useEffect(() => {
|
||||
// Skip if no video or no active segment
|
||||
const video = videoRef.current;
|
||||
if (!video || !activeSegment || !isPlayingSegment || isPreviewMode) {
|
||||
if (!video || !activeSegment || !isPlayingSegment) {
|
||||
// Log why we're skipping
|
||||
if (!video) logger.debug("Skipping segment boundary check - no video element");
|
||||
else if (!activeSegment) logger.debug("Skipping segment boundary check - no active segment");
|
||||
else if (!isPlayingSegment) logger.debug("Skipping segment boundary check - not in segment playback mode");
|
||||
else if (isPreviewMode) logger.debug("Skipping segment boundary check in preview mode");
|
||||
else if (!activeSegment) logger.debug("Skipping segment boundary check - no active segment");
|
||||
else if (!isPlayingSegment) logger.debug("Skipping segment boundary check - not in segment playback mode");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -664,7 +661,7 @@ const TimelineControls = ({
|
||||
video.removeEventListener('timeupdate', handleTimeUpdate);
|
||||
logger.debug("Segment boundary check DEACTIVATED");
|
||||
};
|
||||
}, [activeSegment, isPlayingSegment, isPreviewMode, continuePastBoundary, clipSegments]);
|
||||
}, [activeSegment, isPlayingSegment, continuePastBoundary, clipSegments]);
|
||||
|
||||
// Update display time and check for transitions between segments and empty spaces
|
||||
useEffect(() => {
|
||||
@ -1231,10 +1228,8 @@ const TimelineControls = ({
|
||||
setIsPlayingSegment(false);
|
||||
});
|
||||
}
|
||||
// Resume playback in two cases (but not during segments playback):
|
||||
// 1. If it was playing before (regular playback)
|
||||
// 2. If we're in preview mode (regardless of previous playing state)
|
||||
else if ((wasPlaying || isPreviewMode) && !isPlayingSegments) {
|
||||
// Resume playback if it was playing before (but not during segments playback)
|
||||
else if (wasPlaying && !isPlayingSegments) {
|
||||
logger.debug("Resuming playback after timeline click");
|
||||
videoRef.current.play()
|
||||
.then(() => {
|
||||
@ -1581,17 +1576,7 @@ const TimelineControls = ({
|
||||
else if (!wasInsideSegmentBefore && isInsideSegment) {
|
||||
logger.debug("Playhead moved INTO segment during drag - can start playback");
|
||||
setActiveSegment(segment);
|
||||
// In preview mode, we automatically start playing when playhead enters segment
|
||||
if (isPreviewMode) {
|
||||
videoRef.current.play()
|
||||
.then(() => {
|
||||
setIsPlayingSegment(true);
|
||||
logger.debug("Started playback after dragging segment to include playhead");
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error starting playback:", err);
|
||||
});
|
||||
}
|
||||
// Removed automatic playback - user needs to manually start playback
|
||||
}
|
||||
// Another special case: playhead was inside segment before, but now is also inside but at a different position
|
||||
else if (wasInsideSegmentBefore && isInsideSegment &&
|
||||
@ -1738,8 +1723,8 @@ const TimelineControls = ({
|
||||
console.error("Error continuing segments playback after segment click:", err);
|
||||
});
|
||||
}
|
||||
// If video was playing before OR we're in preview mode, ensure it continues playing (but not in segments mode)
|
||||
else if ((wasPlaying || isPreviewMode) && !isPlayingSegments) {
|
||||
// If video was playing before, ensure it continues playing (but not in segments mode)
|
||||
else if (wasPlaying && !isPlayingSegments) {
|
||||
// Set current segment as active segment for boundary checking
|
||||
setActiveSegment(segment);
|
||||
// Reset the continuePastBoundary flag when clicking on a segment to ensure boundaries work
|
||||
@ -1754,18 +1739,7 @@ const TimelineControls = ({
|
||||
console.error("Error resuming playback after segment click:", err);
|
||||
});
|
||||
}
|
||||
// Always continue playback in preview mode, even if video was paused when clicking (but not in segments mode)
|
||||
else if (isPreviewMode && !isPlayingSegments) {
|
||||
setActiveSegment(segment);
|
||||
videoRef.current.play()
|
||||
.then(() => {
|
||||
setIsPlayingSegment(true);
|
||||
logger.debug("Continued preview playback after segment click");
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error continuing preview playback:", err);
|
||||
});
|
||||
}
|
||||
// Removed preview mode playback - preview functionality replaced by segments playback
|
||||
}
|
||||
|
||||
// Calculate tooltip position directly above click point
|
||||
@ -2448,15 +2422,16 @@ const TimelineControls = ({
|
||||
{/* First row with time adjustment buttons */}
|
||||
<div className="tooltip-row">
|
||||
<button
|
||||
className="tooltip-time-btn"
|
||||
data-tooltip="Seek -50ms (click or hold)"
|
||||
{...handleContinuousTimeAdjustment(-0.05)}
|
||||
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek -50ms (click or hold)"}
|
||||
disabled={isPlayingSegments}
|
||||
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(-0.05) : {})}
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
WebkitTouchCallout: 'none',
|
||||
touchAction: 'manipulation',
|
||||
cursor: 'pointer',
|
||||
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||
WebkitTapHighlightColor: 'transparent'
|
||||
}}
|
||||
>
|
||||
@ -2464,15 +2439,16 @@ const TimelineControls = ({
|
||||
</button>
|
||||
<div className="tooltip-time-display">{formatDetailedTime(displayTime)}</div>
|
||||
<button
|
||||
className="tooltip-time-btn"
|
||||
data-tooltip="Seek +50ms (click or hold)"
|
||||
{...handleContinuousTimeAdjustment(0.05)}
|
||||
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek +50ms (click or hold)"}
|
||||
disabled={isPlayingSegments}
|
||||
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(0.05) : {})}
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
WebkitTouchCallout: 'none',
|
||||
touchAction: 'manipulation',
|
||||
cursor: 'pointer',
|
||||
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||
WebkitTapHighlightColor: 'transparent'
|
||||
}}
|
||||
>
|
||||
@ -2483,8 +2459,9 @@ const TimelineControls = ({
|
||||
{/* Second row with action buttons */}
|
||||
<div className="tooltip-row tooltip-actions">
|
||||
<button
|
||||
className="tooltip-action-btn delete"
|
||||
data-tooltip="Delete segment"
|
||||
className={`tooltip-action-btn delete ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Delete segment"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// Call the delete segment function with the current segment ID
|
||||
@ -2505,8 +2482,9 @@ const TimelineControls = ({
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className="tooltip-action-btn scissors"
|
||||
data-tooltip="Split segment at current position"
|
||||
className={`tooltip-action-btn scissors ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Split segment at current position"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// Call the split segment function with the current segment ID and time
|
||||
@ -2530,8 +2508,9 @@ const TimelineControls = ({
|
||||
</svg>
|
||||
</button>
|
||||
<button
|
||||
className="tooltip-action-btn play-from-start"
|
||||
data-tooltip="Play segment from beginning"
|
||||
className={`tooltip-action-btn play-from-start ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Play segment from beginning"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -2626,8 +2605,9 @@ const TimelineControls = ({
|
||||
|
||||
{/* Play/Pause button for empty space - Same as main play/pause button */}
|
||||
<button
|
||||
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'}`}
|
||||
data-tooltip={isPlaying ? "Pause playback" : "Play from current position"}
|
||||
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'} ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : (isPlaying ? "Pause playback" : "Play from current position")}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -2654,8 +2634,9 @@ const TimelineControls = ({
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="tooltip-action-btn set-in"
|
||||
data-tooltip="Set start point at current position"
|
||||
className={`tooltip-action-btn set-in ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Set start point at current position"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -2692,8 +2673,9 @@ const TimelineControls = ({
|
||||
<img src={segmentStartIcon} alt="Set start point" style={{width: '24px', height: '24px'}} />
|
||||
</button>
|
||||
<button
|
||||
className="tooltip-action-btn set-out"
|
||||
data-tooltip="Set end point at current position"
|
||||
className={`tooltip-action-btn set-out ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Set end point at current position"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -2745,17 +2727,35 @@ const TimelineControls = ({
|
||||
{/* First row with time adjustment buttons - same as segment tooltip */}
|
||||
<div className="tooltip-row">
|
||||
<button
|
||||
className="tooltip-time-btn"
|
||||
data-tooltip="Seek -50ms (click or hold)"
|
||||
{...handleContinuousTimeAdjustment(-0.05)}
|
||||
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek -50ms (click or hold)"}
|
||||
disabled={isPlayingSegments}
|
||||
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(-0.05) : {})}
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
WebkitTouchCallout: 'none',
|
||||
touchAction: 'manipulation',
|
||||
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||
WebkitTapHighlightColor: 'transparent'
|
||||
}}
|
||||
>
|
||||
-50ms
|
||||
</button>
|
||||
<div className="tooltip-time-display">{formatDetailedTime(clickedTime)}</div>
|
||||
<button
|
||||
className="tooltip-time-btn"
|
||||
data-tooltip="Seek +50ms (click or hold)"
|
||||
{...handleContinuousTimeAdjustment(0.05)}
|
||||
className={`tooltip-time-btn ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Seek +50ms (click or hold)"}
|
||||
disabled={isPlayingSegments}
|
||||
{...(!isPlayingSegments ? handleContinuousTimeAdjustment(0.05) : {})}
|
||||
style={{
|
||||
userSelect: 'none',
|
||||
WebkitUserSelect: 'none',
|
||||
WebkitTouchCallout: 'none',
|
||||
touchAction: 'manipulation',
|
||||
cursor: isPlayingSegments ? 'not-allowed' : 'pointer',
|
||||
WebkitTapHighlightColor: 'transparent'
|
||||
}}
|
||||
>
|
||||
+50ms
|
||||
</button>
|
||||
@ -2765,9 +2765,9 @@ const TimelineControls = ({
|
||||
<div className="tooltip-row tooltip-actions">
|
||||
{/* New segment button - Moved to first position */}
|
||||
<button
|
||||
className={`tooltip-action-btn new-segment ${availableSegmentDuration < 0.5 ? 'disabled' : ''}`}
|
||||
data-tooltip={availableSegmentDuration < 0.5 ? 'Not enough space for new segment' : 'Create new segment'}
|
||||
disabled={availableSegmentDuration < 0.5}
|
||||
className={`tooltip-action-btn new-segment ${availableSegmentDuration < 0.5 || isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? 'Disabled during preview' : (availableSegmentDuration < 0.5 ? 'Not enough space for new segment' : 'Create new segment')}
|
||||
disabled={availableSegmentDuration < 0.5 || isPlayingSegments}
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -2831,8 +2831,9 @@ const TimelineControls = ({
|
||||
|
||||
{/* Go to start button - play from beginning of cutaway (until next segment) */}
|
||||
<button
|
||||
className="tooltip-action-btn play-from-start"
|
||||
data-tooltip="Play from beginning of cutaway"
|
||||
className={`tooltip-action-btn play-from-start ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Play from beginning of cutaway"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -3283,8 +3284,9 @@ const TimelineControls = ({
|
||||
|
||||
{/* Play/Pause button for empty space - Same as main play/pause button */}
|
||||
<button
|
||||
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'}`}
|
||||
data-tooltip={isPlaying ? "Pause playback" : "Play from here until next segment"}
|
||||
className={`tooltip-action-btn ${isPlaying ? 'pause' : 'play'} ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : (isPlaying ? "Pause playback" : "Play from here until next segment")}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -3312,8 +3314,9 @@ const TimelineControls = ({
|
||||
|
||||
{/* Segment end adjustment button (always shown) */}
|
||||
<button
|
||||
className="tooltip-action-btn segment-end"
|
||||
data-tooltip="Adjust end of previous segment"
|
||||
className={`tooltip-action-btn segment-end ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Adjust end of previous segment"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@ -3489,8 +3492,9 @@ const TimelineControls = ({
|
||||
|
||||
{/* Segment start adjustment button (always shown) */}
|
||||
<button
|
||||
className="tooltip-action-btn segment-start"
|
||||
data-tooltip="Adjust start of next segment"
|
||||
className={`tooltip-action-btn segment-start ${isPlayingSegments ? 'disabled' : ''}`}
|
||||
data-tooltip={isPlayingSegments ? "Disabled during preview" : "Adjust start of next segment"}
|
||||
disabled={isPlayingSegments}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ const VideoPlayer: React.FC<VideoPlayerProps> = ({
|
||||
|
||||
const sampleVideoUrl = typeof window !== 'undefined' &&
|
||||
(window as any).MEDIA_DATA?.videoUrl ||
|
||||
"/videos/sample-video-37s.mp4";
|
||||
"/videos/sample-video-10m.mp4";
|
||||
|
||||
// Detect iOS device
|
||||
useEffect(() => {
|
||||
|
||||
@ -21,9 +21,7 @@ const useVideoTrimmer = () => {
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
const [isMuted, setIsMuted] = useState(false);
|
||||
|
||||
// Preview mode state for playing only segments
|
||||
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
||||
const [previewSegmentIndex, setPreviewSegmentIndex] = useState(0);
|
||||
// Removed preview mode - replaced by isPlayingSegments
|
||||
|
||||
// Timeline state
|
||||
const [thumbnails, setThumbnails] = useState<string[]>([]);
|
||||
@ -146,18 +144,12 @@ const useVideoTrimmer = () => {
|
||||
};
|
||||
|
||||
const handlePlay = () => {
|
||||
// Only update isPlaying if we're not in preview mode
|
||||
if (!isPreviewMode) {
|
||||
setIsPlaying(true);
|
||||
setVideoInitialized(true);
|
||||
}
|
||||
setIsPlaying(true);
|
||||
setVideoInitialized(true);
|
||||
};
|
||||
|
||||
const handlePause = () => {
|
||||
// Only update isPlaying if we're not in preview mode
|
||||
if (!isPreviewMode) {
|
||||
setIsPlaying(false);
|
||||
}
|
||||
setIsPlaying(false);
|
||||
};
|
||||
|
||||
const handleEnded = () => {
|
||||
@ -180,7 +172,7 @@ const useVideoTrimmer = () => {
|
||||
video.removeEventListener('pause', handlePause);
|
||||
video.removeEventListener('ended', handleEnded);
|
||||
};
|
||||
}, [isPreviewMode]);
|
||||
}, []);
|
||||
|
||||
// Play/pause video
|
||||
const playPauseVideo = () => {
|
||||
@ -226,9 +218,6 @@ const useVideoTrimmer = () => {
|
||||
// Track if the video was playing before seeking
|
||||
const wasPlaying = !video.paused;
|
||||
|
||||
// Store current preview mode state to preserve it
|
||||
const wasInPreviewMode = isPreviewMode;
|
||||
|
||||
// Update the video position
|
||||
video.currentTime = time;
|
||||
setCurrentTime(time);
|
||||
@ -239,36 +228,12 @@ const useVideoTrimmer = () => {
|
||||
window.lastSeekedPosition = time;
|
||||
}
|
||||
|
||||
// Find segment at this position for preview mode playback
|
||||
if (wasInPreviewMode) {
|
||||
const segmentAtPosition = clipSegments.find(
|
||||
seg => time >= seg.startTime && time <= seg.endTime
|
||||
);
|
||||
|
||||
if (segmentAtPosition) {
|
||||
// Update the active segment index in preview mode
|
||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||
const newSegmentIndex = orderedSegments.findIndex(seg => seg.id === segmentAtPosition.id);
|
||||
if (newSegmentIndex !== -1) {
|
||||
setPreviewSegmentIndex(newSegmentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resume playback in two scenarios:
|
||||
// 1. If it was playing before (regular mode)
|
||||
// 2. If we're in preview mode (regardless of previous state)
|
||||
if (wasPlaying || wasInPreviewMode) {
|
||||
// Ensure preview mode stays on if it was on before
|
||||
if (wasInPreviewMode) {
|
||||
setIsPreviewMode(true);
|
||||
}
|
||||
|
||||
// Resume playback if it was playing before
|
||||
if (wasPlaying) {
|
||||
// Play immediately without delay
|
||||
video.play()
|
||||
.then(() => {
|
||||
setIsPlaying(true); // Update state to reflect we're playing
|
||||
// "Resumed playback after seeking in " + (wasInPreviewMode ? "preview" : "regular") + " mode"
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Error resuming playback:", err);
|
||||
@ -550,152 +515,9 @@ const useVideoTrimmer = () => {
|
||||
};
|
||||
}, [clipSegments, duration]);
|
||||
|
||||
// Preview mode effect to handle playing only segments
|
||||
useEffect(() => {
|
||||
if (!isPreviewMode || !videoRef.current) return;
|
||||
|
||||
// Sort segments by start time
|
||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||
if (orderedSegments.length === 0) return;
|
||||
|
||||
const video = videoRef.current;
|
||||
|
||||
// Function to handle segment playback
|
||||
const handleSegmentPlayback = () => {
|
||||
if (!isPreviewMode || !video) return;
|
||||
|
||||
const currentSegment = orderedSegments[previewSegmentIndex];
|
||||
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) { // Small threshold to ensure smooth transition
|
||||
// Move to the next segment if available
|
||||
if (previewSegmentIndex < orderedSegments.length - 1) {
|
||||
// Play next segment
|
||||
const nextSegment = orderedSegments[previewSegmentIndex + 1];
|
||||
video.currentTime = nextSegment.startTime;
|
||||
setPreviewSegmentIndex(previewSegmentIndex + 1);
|
||||
|
||||
logger.debug("Preview: Moving to next segment", {
|
||||
from: formatDetailedTime(currentSegment.endTime),
|
||||
to: formatDetailedTime(nextSegment.startTime),
|
||||
segmentIndex: previewSegmentIndex + 1
|
||||
});
|
||||
|
||||
} else {
|
||||
// Loop back to first segment
|
||||
logger.debug("Preview: Looping back to first segment");
|
||||
video.currentTime = orderedSegments[0].startTime;
|
||||
setPreviewSegmentIndex(0);
|
||||
}
|
||||
|
||||
// Ensure playback continues
|
||||
video.play().catch(err => {
|
||||
console.error("Error continuing preview playback:", err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Add event listener for timeupdate to check segment boundaries
|
||||
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 () => {
|
||||
if (video) {
|
||||
video.removeEventListener('timeupdate', handleSegmentPlayback);
|
||||
}
|
||||
};
|
||||
}, [isPreviewMode, previewSegmentIndex, clipSegments]);
|
||||
// Removed preview mode effect - functionality replaced by isPlayingSegments
|
||||
|
||||
// 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);
|
||||
});
|
||||
};
|
||||
// Removed preview mode functions - replaced by handlePlaySegments
|
||||
|
||||
// Handle trim start change
|
||||
const handleTrimStartChange = (time: number) => {
|
||||
@ -820,16 +642,6 @@ const useVideoTrimmer = () => {
|
||||
const video = videoRef.current;
|
||||
if (!video) return;
|
||||
|
||||
// If in preview mode, exit it before toggling normal play
|
||||
if (isPreviewMode) {
|
||||
setIsPreviewMode(false);
|
||||
// Don't immediately start playing when exiting preview mode
|
||||
// Just update the state and return
|
||||
setIsPlaying(false);
|
||||
video.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPlaying) {
|
||||
// Pause the video
|
||||
video.pause();
|
||||
@ -1067,10 +879,7 @@ const useVideoTrimmer = () => {
|
||||
setIsPlayingSegments(true);
|
||||
setCurrentSegmentIndex(0);
|
||||
|
||||
// Exit preview mode if active
|
||||
if (isPreviewMode) {
|
||||
setIsPreviewMode(false);
|
||||
}
|
||||
// Start segments playback
|
||||
|
||||
// Sort segments by start time
|
||||
const orderedSegments = [...clipSegments].sort((a, b) => a.startTime - b.startTime);
|
||||
@ -1095,7 +904,6 @@ const useVideoTrimmer = () => {
|
||||
isPlaying,
|
||||
setIsPlaying,
|
||||
isMuted,
|
||||
isPreviewMode,
|
||||
isPlayingSegments,
|
||||
thumbnails,
|
||||
trimStart,
|
||||
@ -1114,7 +922,6 @@ const useVideoTrimmer = () => {
|
||||
handleReset,
|
||||
handleUndo,
|
||||
handleRedo,
|
||||
handlePreview,
|
||||
handlePlaySegments,
|
||||
toggleMute,
|
||||
handleSave,
|
||||
|
||||
@ -201,18 +201,7 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Style for the preview mode message that replaces the play button */
|
||||
.preview-mode-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: rgba(59, 130, 246, 0.1);
|
||||
color: #3b82f6;
|
||||
padding: 6px 12px;
|
||||
border-radius: 4px;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
/* Preview mode styles removed - replaced by segments playback mode */
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
@ -226,13 +215,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.preview-mode-message svg {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
margin-right: 0.5rem;
|
||||
color: #3b82f6;
|
||||
}
|
||||
|
||||
/* Add responsive button text class */
|
||||
.button-text {
|
||||
margin-left: 0.25rem;
|
||||
@ -340,11 +322,7 @@
|
||||
padding: 0.25rem;
|
||||
}
|
||||
|
||||
/* Smaller preview mode message */
|
||||
.preview-mode-message {
|
||||
font-size: 0.8rem;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
/* Removed preview mode message styles */
|
||||
|
||||
.divider {
|
||||
margin: 0 0.25rem;
|
||||
|
||||
@ -814,43 +814,7 @@
|
||||
100% { opacity: 0.7; transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Preview mode styles */
|
||||
.preview-mode .tooltip-action-btn {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.preview-mode .tooltip-time-btn {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Timeline preview mode styles */
|
||||
.timeline-container-card.preview-mode {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.timeline-container-card.preview-mode .timeline-marker-head,
|
||||
.timeline-container-card.preview-mode .timeline-marker-drag,
|
||||
.timeline-container-card.preview-mode .clip-segment,
|
||||
.timeline-container-card.preview-mode .clip-segment-handle,
|
||||
.timeline-container-card.preview-mode .time-button,
|
||||
.timeline-container-card.preview-mode .zoom-button,
|
||||
.timeline-container-card.preview-mode .save-button,
|
||||
.timeline-container-card.preview-mode .save-copy-button,
|
||||
.timeline-container-card.preview-mode .save-segments-button {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.timeline-container-card.preview-mode .clip-segment:hover {
|
||||
box-shadow: none;
|
||||
border-color: rgba(0, 0, 0, 0.15);
|
||||
background-color: inherit !important;
|
||||
}
|
||||
/* Preview mode styles removed - replaced by segments playback mode */
|
||||
|
||||
/* Segments playback mode styles - minimal functional styling */
|
||||
.segments-playback-mode .tooltip-time-btn {
|
||||
|
||||
@ -227,6 +227,43 @@
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
/* Ensure pause button is properly styled when disabled */
|
||||
.tooltip-action-btn.pause.disabled {
|
||||
color: #9ca3af !important;
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.tooltip-action-btn.pause.disabled:hover {
|
||||
background-color: #f3f4f6 !important;
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
/* Ensure play button is properly styled when disabled */
|
||||
.tooltip-action-btn.play.disabled {
|
||||
color: #9ca3af !important;
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.tooltip-action-btn.play.disabled:hover {
|
||||
background-color: #f3f4f6 !important;
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
/* Ensure time adjustment buttons are properly styled when disabled */
|
||||
.tooltip-time-btn.disabled {
|
||||
opacity: 0.5 !important;
|
||||
cursor: not-allowed !important;
|
||||
background-color: #f3f4f6 !important;
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
.tooltip-time-btn.disabled:hover {
|
||||
background-color: #f3f4f6 !important;
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
/* Additional mobile optimizations */
|
||||
@media (max-width: 768px) {
|
||||
.two-row-tooltip {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user