From bd786c0fe67feb83b605bef26a38911bb162b0c0 Mon Sep 17 00:00:00 2001 From: Yiannis Christodoulou Date: Mon, 13 Oct 2025 01:45:18 +0300 Subject: [PATCH] Generate chapter names based on segment order Introduces a helper function to assign chapter titles like 'Chapter 1', 'Chapter 2', etc., based on the chronological position of each segment. Updates all segment creation and splitting logic in TimelineControls and useVideoChapters to use this naming convention for improved clarity and consistency. --- .../src/components/TimelineControls.tsx | 29 +++++++++++++------ .../client/src/hooks/useVideoChapters.tsx | 24 +++++++++++---- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/frontend-tools/chapters-editor/client/src/components/TimelineControls.tsx b/frontend-tools/chapters-editor/client/src/components/TimelineControls.tsx index 032977b5..c2f3404c 100644 --- a/frontend-tools/chapters-editor/client/src/components/TimelineControls.tsx +++ b/frontend-tools/chapters-editor/client/src/components/TimelineControls.tsx @@ -125,6 +125,17 @@ const TimelineControls = ({ onPlayPause, // Add this prop isPlayingSegments = false, }: TimelineControlsProps) => { + // Helper function to generate proper chapter name based on chronological position + const generateChapterName = (newSegmentStartTime: number, existingSegments: Segment[]): string => { + // Create a temporary array with all segments including the new one + const allSegments = [...existingSegments, { startTime: newSegmentStartTime } as Segment]; + // Sort by start time to find chronological position + const sortedSegments = allSegments.sort((a, b) => a.startTime - b.startTime); + // Find the index of our new segment + const chapterIndex = sortedSegments.findIndex(seg => seg.startTime === newSegmentStartTime); + return `Chapter ${chapterIndex + 1}`; + }; + const timelineRef = useRef(null); const leftHandleRef = useRef(null); const rightHandleRef = useRef(null); @@ -2939,10 +2950,10 @@ const TimelineControls = ({ const segmentStartTime = clickedTime; const segmentEndTime = segmentStartTime + availableSegmentDuration; - // Create the new segment with a generic name + // Create the new segment with proper chapter name const newSegment: Segment = { id: Date.now(), - chapterTitle: `segment`, + chapterTitle: generateChapterName(segmentStartTime, clipSegments), startTime: segmentStartTime, endTime: segmentEndTime, }; @@ -3253,7 +3264,7 @@ const TimelineControls = ({ // We're in a gap, create a new segment from gap start to clicked time const newSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: generateChapterName(gapStart, clipSegments), startTime: gapStart, endTime: clickedTime, }; @@ -3286,7 +3297,7 @@ const TimelineControls = ({ // Create a new segment from start of video to clicked time const newSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: generateChapterName(0, clipSegments), startTime: 0, endTime: clickedTime, }; @@ -3349,7 +3360,7 @@ const TimelineControls = ({ // No segments exist; create a new segment from start to clicked time const newSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: generateChapterName(0, clipSegments), startTime: 0, endTime: clickedTime, }; @@ -3467,7 +3478,7 @@ const TimelineControls = ({ // We're in a gap, create a new segment from clicked time to gap end const newSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: generateChapterName(clickedTime, clipSegments), startTime: clickedTime, endTime: gapEnd, }; @@ -3500,7 +3511,7 @@ const TimelineControls = ({ // Create a new segment from clicked time to first segment start const newSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: generateChapterName(clickedTime, clipSegments), startTime: clickedTime, endTime: sortedByStart[0].startTime, }; @@ -3534,7 +3545,7 @@ const TimelineControls = ({ // Create a new segment from clicked time to end of video const newSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: generateChapterName(clickedTime, clipSegments), startTime: clickedTime, endTime: duration, }; @@ -3597,7 +3608,7 @@ const TimelineControls = ({ // No segments exist; create a new segment from clicked time to end const newSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: generateChapterName(clickedTime, clipSegments), startTime: clickedTime, endTime: duration, }; diff --git a/frontend-tools/chapters-editor/client/src/hooks/useVideoChapters.tsx b/frontend-tools/chapters-editor/client/src/hooks/useVideoChapters.tsx index 69c0203b..dbc3b9fb 100644 --- a/frontend-tools/chapters-editor/client/src/hooks/useVideoChapters.tsx +++ b/frontend-tools/chapters-editor/client/src/hooks/useVideoChapters.tsx @@ -13,6 +13,17 @@ interface EditorState { } const useVideoChapters = () => { + // Helper function to generate proper chapter name based on chronological position + const generateChapterName = (newSegmentStartTime: number, existingSegments: Segment[]): string => { + // Create a temporary array with all segments including the new one + const allSegments = [...existingSegments, { startTime: newSegmentStartTime } as Segment]; + // Sort by start time to find chronological position + const sortedSegments = allSegments.sort((a, b) => a.startTime - b.startTime); + // Find the index of our new segment + const chapterIndex = sortedSegments.findIndex(seg => seg.startTime === newSegmentStartTime); + return `Chapter ${chapterIndex + 1}`; + }; + // Helper function to parse time string (HH:MM:SS.mmm) to seconds const parseTimeToSeconds = (timeString: string): number => { const parts = timeString.split(':'); @@ -447,16 +458,19 @@ const useVideoChapters = () => { newSegments.splice(segmentIndex, 1); + // Remove the original segment first to get accurate positioning for new segments + const segmentsWithoutOriginal = newSegments; + const firstHalf: Segment = { id: Date.now(), - chapterTitle: `${segmentToSplit.chapterTitle}-A`, + chapterTitle: generateChapterName(segmentToSplit.startTime, segmentsWithoutOriginal), startTime: segmentToSplit.startTime, endTime: timeToSplit, }; const secondHalf: Segment = { id: Date.now() + 1, - chapterTitle: `${segmentToSplit.chapterTitle}-B`, + chapterTitle: generateChapterName(timeToSplit, [...segmentsWithoutOriginal, firstHalf]), startTime: timeToSplit, endTime: segmentToSplit.endTime, }; @@ -488,7 +502,7 @@ const useVideoChapters = () => { // Create a new default segment that spans the entire video const defaultSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: 'Chapter 1', startTime: 0, endTime: videoRef.current.duration, }; @@ -549,7 +563,7 @@ const useVideoChapters = () => { if (startTime < endTime) { newSegments.push({ id: Date.now() + i, - chapterTitle: `Segment ${i + 1}`, + chapterTitle: `Chapter ${i + 1}`, startTime, endTime, }); @@ -574,7 +588,7 @@ const useVideoChapters = () => { const defaultSegment: Segment = { id: Date.now(), - chapterTitle: 'segment', + chapterTitle: 'Chapter 1', startTime: 0, endTime: duration, };