lyrics-transcriber 0.68.0__py3-none-any.whl → 0.69.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +210 -0
- lyrics_transcriber/frontend/package.json +1 -1
- lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +4 -2
- lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +257 -134
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +35 -237
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +27 -3
- lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +688 -0
- lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +29 -18
- lyrics_transcriber/frontend/src/hooks/useManualSync.ts +198 -30
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
- lyrics_transcriber/frontend/web_assets/assets/{index-D7BQUJXK.js → index-izP9z1oB.js} +985 -327
- lyrics_transcriber/frontend/web_assets/assets/index-izP9z1oB.js.map +1 -0
- lyrics_transcriber/frontend/web_assets/index.html +1 -1
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/METADATA +1 -1
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/RECORD +18 -16
- lyrics_transcriber/frontend/web_assets/assets/index-D7BQUJXK.js.map +0 -1
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/LICENSE +0 -0
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/WHEEL +0 -0
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/entry_points.txt +0 -0
@@ -7,6 +7,7 @@ interface TimelineEditorProps {
|
|
7
7
|
startTime: number
|
8
8
|
endTime: number
|
9
9
|
onWordUpdate: (index: number, updates: Partial<Word>) => void
|
10
|
+
onUnsyncWord?: (index: number) => void
|
10
11
|
currentTime?: number
|
11
12
|
onPlaySegment?: (time: number) => void
|
12
13
|
showPlaybackIndicator?: boolean
|
@@ -114,7 +115,7 @@ const TimelineCursor = styled(Box)(({ theme }) => ({
|
|
114
115
|
zIndex: 1, // Ensure it's above other elements
|
115
116
|
}))
|
116
117
|
|
117
|
-
export default function TimelineEditor({ words, startTime, endTime, onWordUpdate, currentTime = 0, onPlaySegment, showPlaybackIndicator = true }: TimelineEditorProps) {
|
118
|
+
export default function TimelineEditor({ words, startTime, endTime, onWordUpdate, onUnsyncWord, currentTime = 0, onPlaySegment, showPlaybackIndicator = true }: TimelineEditorProps) {
|
118
119
|
const containerRef = useRef<HTMLDivElement>(null)
|
119
120
|
const [dragState, setDragState] = useState<{
|
120
121
|
wordIndex: number
|
@@ -157,29 +158,21 @@ export default function TimelineEditor({ words, startTime, endTime, onWordUpdate
|
|
157
158
|
const position = ((time - startTime) / duration) * 100
|
158
159
|
return Math.max(0, Math.min(100, position))
|
159
160
|
}
|
160
|
-
|
161
161
|
const generateTimelineMarks = () => {
|
162
162
|
const marks = []
|
163
163
|
const startSecond = Math.floor(startTime)
|
164
164
|
const endSecond = Math.ceil(endTime)
|
165
165
|
|
166
|
-
// Generate marks for each
|
167
|
-
for (let time = startSecond; time <= endSecond; time
|
166
|
+
// Generate marks for each second
|
167
|
+
for (let time = startSecond; time <= endSecond; time++) {
|
168
168
|
if (time >= startTime && time <= endTime) {
|
169
169
|
const position = timeToPosition(time)
|
170
|
-
const isFullSecond = Math.abs(time - Math.round(time)) < 0.001
|
171
|
-
|
172
170
|
marks.push(
|
173
171
|
<Box key={time}>
|
174
|
-
<TimelineMark
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
{isFullSecond && (
|
179
|
-
<TimelineLabel sx={{ left: `${position}%` }}>
|
180
|
-
{Math.round(time)}s
|
181
|
-
</TimelineLabel>
|
182
|
-
)}
|
172
|
+
<TimelineMark sx={{ left: `${position}%` }} />
|
173
|
+
<TimelineLabel sx={{ left: `${position}%` }}>
|
174
|
+
{time}s
|
175
|
+
</TimelineLabel>
|
183
176
|
</Box>
|
184
177
|
)
|
185
178
|
}
|
@@ -276,6 +269,24 @@ export default function TimelineEditor({ words, startTime, endTime, onWordUpdate
|
|
276
269
|
setDragState(null)
|
277
270
|
}
|
278
271
|
|
272
|
+
const handleContextMenu = (e: React.MouseEvent, wordIndex: number) => {
|
273
|
+
e.preventDefault()
|
274
|
+
e.stopPropagation()
|
275
|
+
|
276
|
+
// Only unsync synced words
|
277
|
+
const word = words[wordIndex]
|
278
|
+
if (word.start_time === null || word.end_time === null) return
|
279
|
+
|
280
|
+
// Directly unsync the word without showing a menu
|
281
|
+
if (onUnsyncWord) {
|
282
|
+
console.log('TimelineEditor - Right-click unsync word', {
|
283
|
+
wordIndex,
|
284
|
+
wordText: word.text
|
285
|
+
})
|
286
|
+
onUnsyncWord(wordIndex)
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
279
290
|
const isWordHighlighted = (word: Word): boolean => {
|
280
291
|
if (!currentTime || word.start_time === null || word.end_time === null) return false
|
281
292
|
return currentTime >= word.start_time && currentTime <= word.end_time
|
@@ -319,13 +330,12 @@ export default function TimelineEditor({ words, startTime, endTime, onWordUpdate
|
|
319
330
|
)}
|
320
331
|
|
321
332
|
{words.map((word, index) => {
|
322
|
-
//
|
333
|
+
// Only render synced words on the timeline
|
323
334
|
if (word.start_time === null || word.end_time === null) return null;
|
324
|
-
|
335
|
+
|
325
336
|
const leftPosition = timeToPosition(word.start_time)
|
326
337
|
const rightPosition = timeToPosition(word.end_time)
|
327
338
|
const width = rightPosition - leftPosition
|
328
|
-
// Remove the visual padding that creates gaps
|
329
339
|
const adjustedWidth = width
|
330
340
|
|
331
341
|
return (
|
@@ -341,6 +351,7 @@ export default function TimelineEditor({ words, startTime, endTime, onWordUpdate
|
|
341
351
|
e.stopPropagation()
|
342
352
|
handleMouseDown(e, index, 'move')
|
343
353
|
}}
|
354
|
+
onContextMenu={(e) => handleContextMenu(e, index)}
|
344
355
|
>
|
345
356
|
<ResizeHandle
|
346
357
|
className="left"
|
@@ -10,7 +10,7 @@ interface UseManualSyncProps {
|
|
10
10
|
|
11
11
|
// Constants for tap detection
|
12
12
|
const TAP_THRESHOLD_MS = 200 // If spacebar is pressed for less than this time, it's considered a tap
|
13
|
-
const DEFAULT_WORD_DURATION =
|
13
|
+
const DEFAULT_WORD_DURATION = 0.5 // Default duration in seconds when tapping (500ms)
|
14
14
|
const OVERLAP_BUFFER = 0.01 // Buffer to prevent word overlap (10ms)
|
15
15
|
|
16
16
|
export default function useManualSync({
|
@@ -20,12 +20,16 @@ export default function useManualSync({
|
|
20
20
|
updateSegment
|
21
21
|
}: UseManualSyncProps) {
|
22
22
|
const [isManualSyncing, setIsManualSyncing] = useState(false)
|
23
|
+
const [isPaused, setIsPaused] = useState(false)
|
23
24
|
const [syncWordIndex, setSyncWordIndex] = useState<number>(-1)
|
24
25
|
const currentTimeRef = useRef(currentTime)
|
25
26
|
const [isSpacebarPressed, setIsSpacebarPressed] = useState(false)
|
26
27
|
const wordStartTimeRef = useRef<number | null>(null)
|
27
28
|
const wordsRef = useRef<Word[]>([])
|
28
29
|
const spacebarPressTimeRef = useRef<number | null>(null)
|
30
|
+
|
31
|
+
// Use ref to track if we need to update segment to avoid calling it too frequently
|
32
|
+
const needsSegmentUpdateRef = useRef(false)
|
29
33
|
|
30
34
|
// Keep currentTimeRef up to date
|
31
35
|
useEffect(() => {
|
@@ -39,14 +43,70 @@ export default function useManualSync({
|
|
39
43
|
}
|
40
44
|
}, [editedSegment])
|
41
45
|
|
46
|
+
// Debounced segment update to batch multiple word changes
|
47
|
+
useEffect(() => {
|
48
|
+
if (needsSegmentUpdateRef.current) {
|
49
|
+
needsSegmentUpdateRef.current = false
|
50
|
+
updateSegment(wordsRef.current)
|
51
|
+
}
|
52
|
+
}, [updateSegment, syncWordIndex]) // Only update when syncWordIndex changes
|
53
|
+
|
42
54
|
const cleanupManualSync = useCallback(() => {
|
43
55
|
setIsManualSyncing(false)
|
56
|
+
setIsPaused(false)
|
44
57
|
setSyncWordIndex(-1)
|
45
58
|
setIsSpacebarPressed(false)
|
46
59
|
wordStartTimeRef.current = null
|
47
60
|
spacebarPressTimeRef.current = null
|
61
|
+
needsSegmentUpdateRef.current = false
|
62
|
+
|
63
|
+
// Stop audio playback when cleaning up manual sync
|
64
|
+
if (window.toggleAudioPlayback && window.isAudioPlaying) {
|
65
|
+
window.toggleAudioPlayback()
|
66
|
+
}
|
48
67
|
}, [])
|
49
68
|
|
69
|
+
const pauseManualSync = useCallback(() => {
|
70
|
+
if (isManualSyncing && !isPaused) {
|
71
|
+
console.log('useManualSync - Pausing manual sync')
|
72
|
+
setIsPaused(true)
|
73
|
+
// Pause audio playback
|
74
|
+
if (window.toggleAudioPlayback && window.isAudioPlaying) {
|
75
|
+
window.toggleAudioPlayback()
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}, [isManualSyncing, isPaused])
|
79
|
+
|
80
|
+
const resumeManualSync = useCallback(() => {
|
81
|
+
if (isManualSyncing && isPaused) {
|
82
|
+
console.log('useManualSync - Resuming manual sync')
|
83
|
+
setIsPaused(false)
|
84
|
+
|
85
|
+
// Find the first unsynced word and resume from there
|
86
|
+
if (editedSegment) {
|
87
|
+
const firstUnsyncedIndex = editedSegment.words.findIndex(word =>
|
88
|
+
word.start_time === null || word.end_time === null
|
89
|
+
)
|
90
|
+
|
91
|
+
if (firstUnsyncedIndex !== -1 && firstUnsyncedIndex !== syncWordIndex) {
|
92
|
+
console.log('useManualSync - Resuming from first unsynced word', {
|
93
|
+
previousIndex: syncWordIndex,
|
94
|
+
newIndex: firstUnsyncedIndex,
|
95
|
+
wordText: editedSegment.words[firstUnsyncedIndex]?.text
|
96
|
+
})
|
97
|
+
setSyncWordIndex(firstUnsyncedIndex)
|
98
|
+
} else {
|
99
|
+
console.log('useManualSync - Resuming from current position', { syncWordIndex })
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
// Resume audio playback if we have an onPlaySegment function
|
104
|
+
if (onPlaySegment && currentTimeRef.current !== undefined) {
|
105
|
+
onPlaySegment(currentTimeRef.current)
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}, [isManualSyncing, isPaused, onPlaySegment, editedSegment, syncWordIndex])
|
109
|
+
|
50
110
|
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
51
111
|
if (e.code !== 'Space') return
|
52
112
|
|
@@ -60,14 +120,7 @@ export default function useManualSync({
|
|
60
120
|
e.preventDefault()
|
61
121
|
e.stopPropagation()
|
62
122
|
|
63
|
-
if (isManualSyncing && editedSegment && !isSpacebarPressed) {
|
64
|
-
const currentWord = syncWordIndex < editedSegment.words.length ? editedSegment.words[syncWordIndex] : null
|
65
|
-
console.log('useManualSync - Recording word start time', {
|
66
|
-
wordIndex: syncWordIndex,
|
67
|
-
wordText: currentWord?.text,
|
68
|
-
time: currentTimeRef.current
|
69
|
-
})
|
70
|
-
|
123
|
+
if (isManualSyncing && editedSegment && !isSpacebarPressed && !isPaused) {
|
71
124
|
setIsSpacebarPressed(true)
|
72
125
|
|
73
126
|
// Record the start time of the current word
|
@@ -80,18 +133,86 @@ export default function useManualSync({
|
|
80
133
|
if (syncWordIndex < editedSegment.words.length) {
|
81
134
|
const newWords = [...wordsRef.current]
|
82
135
|
const currentWord = newWords[syncWordIndex]
|
136
|
+
const currentStartTime = currentTimeRef.current
|
83
137
|
|
84
138
|
// Set the start time for the current word
|
85
|
-
currentWord.start_time =
|
139
|
+
currentWord.start_time = currentStartTime
|
140
|
+
|
141
|
+
// Handle the end time of the previous word (if it exists)
|
142
|
+
if (syncWordIndex > 0) {
|
143
|
+
const previousWord = newWords[syncWordIndex - 1]
|
144
|
+
if (previousWord.start_time !== null) {
|
145
|
+
const timeSincePreviousStart = currentStartTime - previousWord.start_time
|
146
|
+
|
147
|
+
// Only adjust previous word's end time if:
|
148
|
+
// 1. It doesn't have an end time set yet (was never released), OR
|
149
|
+
// 2. The current start would overlap with existing end time
|
150
|
+
const needsAdjustment = previousWord.end_time === null ||
|
151
|
+
(previousWord.end_time !== null && previousWord.end_time > currentStartTime)
|
152
|
+
|
153
|
+
if (needsAdjustment) {
|
154
|
+
if (timeSincePreviousStart > 1.0) {
|
155
|
+
// Gap of over 1 second - set previous word's end time to 500ms after its start
|
156
|
+
previousWord.end_time = previousWord.start_time + 0.5
|
157
|
+
console.log('useManualSync - Gap detected, setting previous word end time to +500ms', {
|
158
|
+
previousWordIndex: syncWordIndex - 1,
|
159
|
+
previousWordText: previousWord.text,
|
160
|
+
previousStartTime: previousWord.start_time,
|
161
|
+
previousEndTime: previousWord.end_time,
|
162
|
+
gap: timeSincePreviousStart.toFixed(2) + 's',
|
163
|
+
reason: 'gap > 1s'
|
164
|
+
})
|
165
|
+
} else {
|
166
|
+
// Normal flow - set previous word's end time to current word's start time minus 5ms
|
167
|
+
previousWord.end_time = currentStartTime - 0.005
|
168
|
+
console.log('useManualSync - Setting previous word end time to current start - 5ms', {
|
169
|
+
previousWordIndex: syncWordIndex - 1,
|
170
|
+
previousWordText: previousWord.text,
|
171
|
+
previousEndTime: previousWord.end_time,
|
172
|
+
currentStartTime: currentStartTime,
|
173
|
+
gap: timeSincePreviousStart.toFixed(2) + 's',
|
174
|
+
reason: 'normal flow'
|
175
|
+
})
|
176
|
+
}
|
177
|
+
} else {
|
178
|
+
console.log('useManualSync - Preserving previous word timing (manually set)', {
|
179
|
+
previousWordIndex: syncWordIndex - 1,
|
180
|
+
previousWordText: previousWord.text,
|
181
|
+
previousStartTime: previousWord.start_time,
|
182
|
+
previousEndTime: previousWord.end_time,
|
183
|
+
preservedDuration: previousWord.end_time !== null ?
|
184
|
+
(previousWord.end_time - previousWord.start_time).toFixed(2) + 's' : 'N/A',
|
185
|
+
reason: 'already timed correctly'
|
186
|
+
})
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
console.log('useManualSync - Recording word start time', {
|
192
|
+
wordIndex: syncWordIndex,
|
193
|
+
wordText: currentWord?.text,
|
194
|
+
time: currentStartTime
|
195
|
+
})
|
86
196
|
|
87
197
|
// Update our ref
|
88
198
|
wordsRef.current = newWords
|
89
199
|
|
90
|
-
//
|
91
|
-
|
200
|
+
// Mark that we need to update the segment
|
201
|
+
needsSegmentUpdateRef.current = true
|
92
202
|
}
|
93
203
|
} else if (!isManualSyncing && editedSegment && onPlaySegment) {
|
94
|
-
console.log('useManualSync - Handling segment playback'
|
204
|
+
console.log('useManualSync - Handling segment playback', {
|
205
|
+
editedSegmentId: editedSegment.id,
|
206
|
+
isGlobalReplacement: editedSegment.id === 'global-replacement'
|
207
|
+
})
|
208
|
+
|
209
|
+
// For global replacement segments, don't handle general playback
|
210
|
+
// since we want the user to use Manual Sync instead
|
211
|
+
if (editedSegment.id === 'global-replacement') {
|
212
|
+
console.log('useManualSync - Ignoring playback for global replacement - please use Manual Sync')
|
213
|
+
return
|
214
|
+
}
|
215
|
+
|
95
216
|
// Toggle segment playback when not in manual sync mode
|
96
217
|
const startTime = editedSegment.start_time ?? 0
|
97
218
|
const endTime = editedSegment.end_time ?? 0
|
@@ -104,7 +225,7 @@ export default function useManualSync({
|
|
104
225
|
onPlaySegment(startTime)
|
105
226
|
}
|
106
227
|
}
|
107
|
-
}, [isManualSyncing, editedSegment, syncWordIndex, onPlaySegment,
|
228
|
+
}, [isManualSyncing, editedSegment, syncWordIndex, onPlaySegment, isSpacebarPressed, isPaused])
|
108
229
|
|
109
230
|
const handleKeyUp = useCallback((e: KeyboardEvent) => {
|
110
231
|
if (e.code !== 'Space') return
|
@@ -120,7 +241,7 @@ export default function useManualSync({
|
|
120
241
|
e.preventDefault()
|
121
242
|
e.stopPropagation()
|
122
243
|
|
123
|
-
if (isManualSyncing && editedSegment && isSpacebarPressed) {
|
244
|
+
if (isManualSyncing && editedSegment && isSpacebarPressed && !isPaused) {
|
124
245
|
const currentWord = syncWordIndex < editedSegment.words.length ? editedSegment.words[syncWordIndex] : null
|
125
246
|
const pressDuration = spacebarPressTimeRef.current ? Date.now() - spacebarPressTimeRef.current : 0
|
126
247
|
const isTap = pressDuration < TAP_THRESHOLD_MS
|
@@ -132,6 +253,7 @@ export default function useManualSync({
|
|
132
253
|
endTime: currentTimeRef.current,
|
133
254
|
pressDuration: `${pressDuration}ms`,
|
134
255
|
isTap,
|
256
|
+
tapThreshold: TAP_THRESHOLD_MS,
|
135
257
|
duration: currentWord ? (currentTimeRef.current - (wordStartTimeRef.current || 0)).toFixed(2) + 's' : 'N/A'
|
136
258
|
})
|
137
259
|
|
@@ -147,12 +269,20 @@ export default function useManualSync({
|
|
147
269
|
const defaultEndTime = (wordStartTimeRef.current || currentTimeRef.current) + DEFAULT_WORD_DURATION
|
148
270
|
currentWord.end_time = defaultEndTime
|
149
271
|
console.log('useManualSync - Tap detected, setting default duration', {
|
272
|
+
wordText: currentWord.text,
|
273
|
+
startTime: wordStartTimeRef.current,
|
150
274
|
defaultEndTime,
|
151
275
|
duration: DEFAULT_WORD_DURATION
|
152
276
|
})
|
153
277
|
} else {
|
154
278
|
// For a hold, use the current time as the end time
|
155
279
|
currentWord.end_time = currentTimeRef.current
|
280
|
+
console.log('useManualSync - Hold detected, using actual timing', {
|
281
|
+
wordText: currentWord.text,
|
282
|
+
startTime: wordStartTimeRef.current,
|
283
|
+
endTime: currentTimeRef.current,
|
284
|
+
actualDuration: (currentTimeRef.current - (wordStartTimeRef.current || 0)).toFixed(2) + 's'
|
285
|
+
})
|
156
286
|
}
|
157
287
|
|
158
288
|
// Update our ref
|
@@ -176,11 +306,11 @@ export default function useManualSync({
|
|
176
306
|
setSyncWordIndex(syncWordIndex + 1)
|
177
307
|
}
|
178
308
|
|
179
|
-
//
|
180
|
-
|
309
|
+
// Mark that we need to update the segment
|
310
|
+
needsSegmentUpdateRef.current = true
|
181
311
|
}
|
182
312
|
}
|
183
|
-
}, [isManualSyncing, editedSegment, syncWordIndex,
|
313
|
+
}, [isManualSyncing, editedSegment, syncWordIndex, isSpacebarPressed, isPaused])
|
184
314
|
|
185
315
|
// Add a handler for when the next word starts to adjust previous word's end time if needed
|
186
316
|
useEffect(() => {
|
@@ -208,11 +338,11 @@ export default function useManualSync({
|
|
208
338
|
// Update our ref
|
209
339
|
wordsRef.current = newWords
|
210
340
|
|
211
|
-
//
|
212
|
-
|
341
|
+
// Mark that we need to update the segment
|
342
|
+
needsSegmentUpdateRef.current = true
|
213
343
|
}
|
214
344
|
}
|
215
|
-
}, [syncWordIndex, isManualSyncing, editedSegment
|
345
|
+
}, [syncWordIndex, isManualSyncing, editedSegment])
|
216
346
|
|
217
347
|
// Combine the key handlers into a single function for external use
|
218
348
|
const handleSpacebar = useCallback((e: KeyboardEvent) => {
|
@@ -234,32 +364,70 @@ export default function useManualSync({
|
|
234
364
|
// Make sure we have the latest words
|
235
365
|
wordsRef.current = [...editedSegment.words]
|
236
366
|
|
367
|
+
// Find the first unsynced word to start from
|
368
|
+
const firstUnsyncedIndex = editedSegment.words.findIndex(word =>
|
369
|
+
word.start_time === null || word.end_time === null
|
370
|
+
)
|
371
|
+
|
372
|
+
const startIndex = firstUnsyncedIndex !== -1 ? firstUnsyncedIndex : 0
|
373
|
+
|
374
|
+
console.log('useManualSync - Starting manual sync', {
|
375
|
+
totalWords: editedSegment.words.length,
|
376
|
+
startingFromIndex: startIndex,
|
377
|
+
startingWord: editedSegment.words[startIndex]?.text
|
378
|
+
})
|
379
|
+
|
237
380
|
setIsManualSyncing(true)
|
238
|
-
setSyncWordIndex(
|
381
|
+
setSyncWordIndex(startIndex)
|
239
382
|
setIsSpacebarPressed(false)
|
240
383
|
wordStartTimeRef.current = null
|
241
384
|
spacebarPressTimeRef.current = null
|
385
|
+
needsSegmentUpdateRef.current = false
|
242
386
|
// Start playing 3 seconds before segment start
|
243
387
|
onPlaySegment((editedSegment.start_time ?? 0) - 3)
|
244
388
|
}, [isManualSyncing, editedSegment, onPlaySegment, cleanupManualSync])
|
245
389
|
|
246
|
-
// Auto-stop sync if we go past the end time
|
390
|
+
// Auto-stop sync if we go past the end time (but not for global replacement segments)
|
247
391
|
useEffect(() => {
|
248
|
-
if (!editedSegment) return
|
392
|
+
if (!editedSegment || !isManualSyncing) return
|
249
393
|
|
250
|
-
|
394
|
+
// Don't auto-stop for global replacement segments - let user manually finish
|
395
|
+
if (editedSegment.id === 'global-replacement') {
|
396
|
+
console.log('useManualSync - Skipping auto-stop for global replacement segment')
|
397
|
+
return
|
398
|
+
}
|
251
399
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
400
|
+
// Set up an interval to check if we should auto-stop
|
401
|
+
const checkAutoStop = () => {
|
402
|
+
const endTime = editedSegment.end_time ?? 0
|
403
|
+
|
404
|
+
if (window.isAudioPlaying && currentTimeRef.current > endTime) {
|
405
|
+
console.log('useManualSync - Auto-stopping: current time exceeded end time', {
|
406
|
+
currentTime: currentTimeRef.current,
|
407
|
+
endTime,
|
408
|
+
segmentId: editedSegment.id
|
409
|
+
})
|
410
|
+
window.toggleAudioPlayback?.()
|
411
|
+
cleanupManualSync()
|
412
|
+
}
|
413
|
+
}
|
414
|
+
|
415
|
+
// Check immediately and then every 100ms
|
416
|
+
checkAutoStop()
|
417
|
+
const intervalId = setInterval(checkAutoStop, 100)
|
418
|
+
|
419
|
+
return () => {
|
420
|
+
clearInterval(intervalId)
|
256
421
|
}
|
257
|
-
}, [isManualSyncing, editedSegment,
|
422
|
+
}, [isManualSyncing, editedSegment, cleanupManualSync])
|
258
423
|
|
259
424
|
return {
|
260
425
|
isManualSyncing,
|
426
|
+
isPaused,
|
261
427
|
syncWordIndex,
|
262
428
|
startManualSync,
|
429
|
+
pauseManualSync,
|
430
|
+
resumeManualSync,
|
263
431
|
cleanupManualSync,
|
264
432
|
handleSpacebar,
|
265
433
|
isSpacebarPressed
|
@@ -1 +1 @@
|
|
1
|
-
{"root":["./src/App.tsx","./src/api.ts","./src/main.tsx","./src/theme.ts","./src/types.ts","./src/validation.ts","./src/vite-env.d.ts","./src/components/AddLyricsModal.tsx","./src/components/AudioPlayer.tsx","./src/components/CorrectionMetrics.tsx","./src/components/EditActionBar.tsx","./src/components/EditModal.tsx","./src/components/EditTimelineSection.tsx","./src/components/EditWordList.tsx","./src/components/FileUpload.tsx","./src/components/FindReplaceModal.tsx","./src/components/Header.tsx","./src/components/LyricsAnalyzer.tsx","./src/components/ModeSelector.tsx","./src/components/PreviewVideoSection.tsx","./src/components/ReferenceView.tsx","./src/components/ReviewChangesModal.tsx","./src/components/SegmentDetailsModal.tsx","./src/components/TimelineEditor.tsx","./src/components/TimingOffsetModal.tsx","./src/components/TranscriptionView.tsx","./src/components/WordDivider.tsx","./src/components/shared/constants.ts","./src/components/shared/styles.ts","./src/components/shared/types.ts","./src/components/shared/components/HighlightedText.tsx","./src/components/shared/components/SourceSelector.tsx","./src/components/shared/components/Word.tsx","./src/components/shared/hooks/useWordClick.ts","./src/components/shared/utils/keyboardHandlers.ts","./src/components/shared/utils/localStorage.ts","./src/components/shared/utils/referenceLineCalculator.ts","./src/components/shared/utils/segmentOperations.ts","./src/components/shared/utils/timingUtils.ts","./src/components/shared/utils/wordUtils.ts","./src/hooks/useManualSync.ts","./src/types/global.d.ts"],"version":"5.6.3"}
|
1
|
+
{"root":["./src/App.tsx","./src/api.ts","./src/main.tsx","./src/theme.ts","./src/types.ts","./src/validation.ts","./src/vite-env.d.ts","./src/components/AddLyricsModal.tsx","./src/components/AudioPlayer.tsx","./src/components/CorrectionMetrics.tsx","./src/components/EditActionBar.tsx","./src/components/EditModal.tsx","./src/components/EditTimelineSection.tsx","./src/components/EditWordList.tsx","./src/components/FileUpload.tsx","./src/components/FindReplaceModal.tsx","./src/components/Header.tsx","./src/components/LyricsAnalyzer.tsx","./src/components/ModeSelector.tsx","./src/components/PreviewVideoSection.tsx","./src/components/ReferenceView.tsx","./src/components/ReplaceAllLyricsModal.tsx","./src/components/ReviewChangesModal.tsx","./src/components/SegmentDetailsModal.tsx","./src/components/TimelineEditor.tsx","./src/components/TimingOffsetModal.tsx","./src/components/TranscriptionView.tsx","./src/components/WordDivider.tsx","./src/components/shared/constants.ts","./src/components/shared/styles.ts","./src/components/shared/types.ts","./src/components/shared/components/HighlightedText.tsx","./src/components/shared/components/SourceSelector.tsx","./src/components/shared/components/Word.tsx","./src/components/shared/hooks/useWordClick.ts","./src/components/shared/utils/keyboardHandlers.ts","./src/components/shared/utils/localStorage.ts","./src/components/shared/utils/referenceLineCalculator.ts","./src/components/shared/utils/segmentOperations.ts","./src/components/shared/utils/timingUtils.ts","./src/components/shared/utils/wordUtils.ts","./src/hooks/useManualSync.ts","./src/types/global.d.ts"],"version":"5.6.3"}
|