lyrics-transcriber 0.41.0__py3-none-any.whl → 0.43.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.
Files changed (78) hide show
  1. lyrics_transcriber/core/controller.py +30 -52
  2. lyrics_transcriber/correction/anchor_sequence.py +325 -150
  3. lyrics_transcriber/correction/corrector.py +224 -107
  4. lyrics_transcriber/correction/handlers/base.py +28 -10
  5. lyrics_transcriber/correction/handlers/extend_anchor.py +47 -24
  6. lyrics_transcriber/correction/handlers/levenshtein.py +75 -33
  7. lyrics_transcriber/correction/handlers/llm.py +290 -0
  8. lyrics_transcriber/correction/handlers/no_space_punct_match.py +81 -36
  9. lyrics_transcriber/correction/handlers/relaxed_word_count_match.py +46 -26
  10. lyrics_transcriber/correction/handlers/repeat.py +28 -11
  11. lyrics_transcriber/correction/handlers/sound_alike.py +68 -32
  12. lyrics_transcriber/correction/handlers/syllables_match.py +80 -30
  13. lyrics_transcriber/correction/handlers/word_count_match.py +36 -19
  14. lyrics_transcriber/correction/handlers/word_operations.py +68 -22
  15. lyrics_transcriber/correction/text_utils.py +3 -7
  16. lyrics_transcriber/frontend/.yarn/install-state.gz +0 -0
  17. lyrics_transcriber/frontend/.yarn/releases/yarn-4.6.0.cjs +934 -0
  18. lyrics_transcriber/frontend/.yarnrc.yml +3 -0
  19. lyrics_transcriber/frontend/dist/assets/{index-DKnNJHRK.js → index-D0Gr3Ep7.js} +16509 -9038
  20. lyrics_transcriber/frontend/dist/assets/index-D0Gr3Ep7.js.map +1 -0
  21. lyrics_transcriber/frontend/dist/index.html +1 -1
  22. lyrics_transcriber/frontend/package.json +6 -2
  23. lyrics_transcriber/frontend/src/App.tsx +18 -2
  24. lyrics_transcriber/frontend/src/api.ts +103 -6
  25. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +14 -6
  26. lyrics_transcriber/frontend/src/components/DetailsModal.tsx +86 -59
  27. lyrics_transcriber/frontend/src/components/EditModal.tsx +281 -63
  28. lyrics_transcriber/frontend/src/components/FileUpload.tsx +2 -2
  29. lyrics_transcriber/frontend/src/components/Header.tsx +249 -0
  30. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +320 -266
  31. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +120 -0
  32. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +174 -52
  33. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +158 -114
  34. lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +59 -78
  35. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +39 -16
  36. lyrics_transcriber/frontend/src/components/WordEditControls.tsx +4 -10
  37. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +134 -68
  38. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +1 -1
  39. lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +85 -115
  40. lyrics_transcriber/frontend/src/components/shared/types.js +2 -0
  41. lyrics_transcriber/frontend/src/components/shared/types.ts +15 -7
  42. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +67 -0
  43. lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +78 -0
  44. lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +7 -7
  45. lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +121 -0
  46. lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +22 -0
  47. lyrics_transcriber/frontend/src/types/global.d.ts +9 -0
  48. lyrics_transcriber/frontend/src/types.js +2 -0
  49. lyrics_transcriber/frontend/src/types.ts +70 -49
  50. lyrics_transcriber/frontend/src/validation.ts +132 -0
  51. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
  52. lyrics_transcriber/frontend/yarn.lock +3752 -0
  53. lyrics_transcriber/lyrics/base_lyrics_provider.py +75 -12
  54. lyrics_transcriber/lyrics/file_provider.py +6 -5
  55. lyrics_transcriber/lyrics/genius.py +5 -2
  56. lyrics_transcriber/lyrics/spotify.py +58 -21
  57. lyrics_transcriber/output/ass/config.py +16 -5
  58. lyrics_transcriber/output/cdg.py +1 -1
  59. lyrics_transcriber/output/generator.py +22 -8
  60. lyrics_transcriber/output/plain_text.py +15 -10
  61. lyrics_transcriber/output/segment_resizer.py +16 -3
  62. lyrics_transcriber/output/subtitles.py +27 -1
  63. lyrics_transcriber/output/video.py +107 -1
  64. lyrics_transcriber/review/__init__.py +0 -1
  65. lyrics_transcriber/review/server.py +337 -164
  66. lyrics_transcriber/transcribers/audioshake.py +3 -0
  67. lyrics_transcriber/transcribers/base_transcriber.py +11 -3
  68. lyrics_transcriber/transcribers/whisper.py +11 -1
  69. lyrics_transcriber/types.py +151 -105
  70. lyrics_transcriber/utils/word_utils.py +27 -0
  71. {lyrics_transcriber-0.41.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/METADATA +3 -1
  72. {lyrics_transcriber-0.41.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/RECORD +75 -61
  73. {lyrics_transcriber-0.41.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/WHEEL +1 -1
  74. lyrics_transcriber/frontend/dist/assets/index-DKnNJHRK.js.map +0 -1
  75. lyrics_transcriber/frontend/package-lock.json +0 -4260
  76. lyrics_transcriber/frontend/src/components/shared/utils/initializeDataWithIds.tsx +0 -202
  77. {lyrics_transcriber-0.41.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/LICENSE +0 -0
  78. {lyrics_transcriber-0.41.0.dist-info → lyrics_transcriber-0.43.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,249 @@
1
+ import { Box, Button, Typography, useMediaQuery, useTheme, Switch, FormControlLabel, Tooltip, CircularProgress } from '@mui/material'
2
+ import LockIcon from '@mui/icons-material/Lock'
3
+ import UploadFileIcon from '@mui/icons-material/UploadFile'
4
+ import { CorrectionData } from '../types'
5
+ import CorrectionMetrics from './CorrectionMetrics'
6
+ import ModeSelector from './ModeSelector'
7
+ import AudioPlayer from './AudioPlayer'
8
+ import { InteractionMode } from '../types'
9
+ import { ApiClient } from '../api'
10
+ import { findWordById } from './shared/utils/wordUtils'
11
+
12
+ interface HeaderProps {
13
+ isReadOnly: boolean
14
+ onFileLoad: () => void
15
+ data: CorrectionData
16
+ onMetricClick: {
17
+ anchor: () => void
18
+ corrected: () => void
19
+ uncorrected: () => void
20
+ }
21
+ effectiveMode: InteractionMode
22
+ onModeChange: (mode: InteractionMode) => void
23
+ apiClient: ApiClient | null
24
+ audioHash: string
25
+ onTimeUpdate: (time: number) => void
26
+ onHandlerToggle: (handler: string, enabled: boolean) => void
27
+ isUpdatingHandlers: boolean
28
+ onHandlerClick?: (handler: string) => void
29
+ }
30
+
31
+ export default function Header({
32
+ isReadOnly,
33
+ onFileLoad,
34
+ data,
35
+ onMetricClick,
36
+ effectiveMode,
37
+ onModeChange,
38
+ apiClient,
39
+ audioHash,
40
+ onTimeUpdate,
41
+ onHandlerToggle,
42
+ isUpdatingHandlers,
43
+ onHandlerClick
44
+ }: HeaderProps) {
45
+ const theme = useTheme()
46
+ const isMobile = useMediaQuery(theme.breakpoints.down('md'))
47
+
48
+ // Get handlers with their correction counts
49
+ const handlerCounts = data.corrections?.reduce((counts: Record<string, number>, correction) => {
50
+ counts[correction.handler] = (counts[correction.handler] || 0) + 1
51
+ return counts
52
+ }, {}) || {}
53
+
54
+ // Get available handlers from metadata
55
+ const availableHandlers = data.metadata.available_handlers || []
56
+ const enabledHandlers = new Set(data.metadata.enabled_handlers || [])
57
+
58
+ // Create a map of gap IDs to their corrections
59
+ const gapCorrections = data.corrections.reduce((map: Record<string, number>, correction) => {
60
+ // Find the gap that contains this correction's word_id
61
+ const gap = data.gap_sequences.find(g =>
62
+ g.transcribed_word_ids.includes(correction.word_id)
63
+ )
64
+ if (gap) {
65
+ map[gap.id] = (map[gap.id] || 0) + 1
66
+ }
67
+ return map
68
+ }, {})
69
+
70
+ // Calculate metrics
71
+ const correctedGapCount = Object.keys(gapCorrections).length
72
+ const uncorrectedGapCount = data.gap_sequences.length - correctedGapCount
73
+
74
+ const uncorrectedGaps = data.gap_sequences
75
+ .filter(gap => !gapCorrections[gap.id] && gap.transcribed_word_ids.length > 0)
76
+ .map(gap => {
77
+ const firstWord = findWordById(data.corrected_segments, gap.transcribed_word_ids[0])
78
+ return {
79
+ position: firstWord?.id ?? '',
80
+ length: gap.transcribed_word_ids.length
81
+ }
82
+ })
83
+
84
+ // Calculate correction type counts
85
+ const replacedCount = data.corrections.filter(c => !c.is_deletion && !c.split_total).length
86
+ const addedCount = data.corrections.filter(c => c.split_total).length
87
+ const deletedCount = data.corrections.filter(c => c.is_deletion).length
88
+
89
+ return (
90
+ <>
91
+ {isReadOnly && (
92
+ <Box sx={{ display: 'flex', alignItems: 'center', mb: 2, color: 'text.secondary' }}>
93
+ <LockIcon sx={{ mr: 1 }} />
94
+ <Typography variant="body2">
95
+ View Only Mode
96
+ </Typography>
97
+ </Box>
98
+ )}
99
+
100
+ <Box sx={{
101
+ display: 'flex',
102
+ flexDirection: isMobile ? 'column' : 'row',
103
+ gap: 2,
104
+ justifyContent: 'space-between',
105
+ alignItems: isMobile ? 'stretch' : 'center',
106
+ mb: 3
107
+ }}>
108
+ <Typography variant="h4" sx={{ fontSize: isMobile ? '1.75rem' : '2.125rem' }}>
109
+ Lyrics Correction Review
110
+ </Typography>
111
+ {isReadOnly && (
112
+ <Button
113
+ variant="outlined"
114
+ startIcon={<UploadFileIcon />}
115
+ onClick={onFileLoad}
116
+ fullWidth={isMobile}
117
+ >
118
+ Load File
119
+ </Button>
120
+ )}
121
+ </Box>
122
+
123
+ <Box sx={{
124
+ display: 'flex',
125
+ gap: 2,
126
+ mb: 3,
127
+ flexDirection: isMobile ? 'column' : 'row'
128
+ }}>
129
+ <Box sx={{
130
+ display: 'flex',
131
+ flexDirection: 'column',
132
+ gap: 1,
133
+ minWidth: '250px',
134
+ position: 'relative'
135
+ }}>
136
+ <Typography variant="subtitle2" color="text.secondary">
137
+ Correction Handlers
138
+ </Typography>
139
+
140
+ {availableHandlers.map(handler => (
141
+ <Tooltip
142
+ key={handler.id}
143
+ title={handler.description}
144
+ placement="right"
145
+ >
146
+ <FormControlLabel
147
+ control={
148
+ <Switch
149
+ checked={enabledHandlers.has(handler.id)}
150
+ onChange={(e) => onHandlerToggle(handler.id, e.target.checked)}
151
+ size="small"
152
+ disabled={isUpdatingHandlers}
153
+ />
154
+ }
155
+ label={`${handler.name} (${handlerCounts[handler.id] || 0})`}
156
+ onClick={(e) => {
157
+ if ((e.target as HTMLElement).tagName !== 'INPUT') {
158
+ e.preventDefault();
159
+ e.stopPropagation();
160
+ onHandlerClick?.(handler.id);
161
+ }
162
+ }}
163
+ sx={{
164
+ ml: 0,
165
+ '& .MuiFormControlLabel-label': {
166
+ fontSize: '0.875rem',
167
+ cursor: 'pointer'
168
+ }
169
+ }}
170
+ />
171
+ </Tooltip>
172
+ ))}
173
+
174
+ {isUpdatingHandlers && (
175
+ <Box sx={{
176
+ position: 'absolute',
177
+ top: 0,
178
+ left: 0,
179
+ right: 0,
180
+ bottom: 0,
181
+ display: 'flex',
182
+ alignItems: 'center',
183
+ justifyContent: 'center',
184
+ backgroundColor: 'rgba(255, 255, 255, 0.7)',
185
+ borderRadius: 1,
186
+ zIndex: 1
187
+ }}>
188
+ <Box sx={{
189
+ display: 'flex',
190
+ alignItems: 'center',
191
+ gap: 2,
192
+ padding: 2,
193
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
194
+ borderRadius: 1,
195
+ boxShadow: 1
196
+ }}>
197
+ <CircularProgress size={24} />
198
+ <Typography variant="body2" color="text.secondary">
199
+ Updating corrections...
200
+ </Typography>
201
+ </Box>
202
+ </Box>
203
+ )}
204
+ </Box>
205
+ <Box sx={{ flexGrow: 1 }}>
206
+ <CorrectionMetrics
207
+ // Anchor metrics
208
+ anchorCount={data.metadata.anchor_sequences_count}
209
+ multiSourceAnchors={data.anchor_sequences?.filter(anchor =>
210
+ anchor?.reference_word_ids &&
211
+ Object.keys(anchor.reference_word_ids).length > 1
212
+ ).length ?? 0}
213
+ anchorWordCount={data.anchor_sequences?.reduce((sum, anchor) =>
214
+ sum + (anchor.transcribed_word_ids?.length || 0), 0) ?? 0}
215
+ // Updated gap metrics
216
+ correctedGapCount={correctedGapCount}
217
+ uncorrectedGapCount={uncorrectedGapCount}
218
+ uncorrectedGaps={uncorrectedGaps}
219
+ // Updated correction type counts
220
+ replacedCount={replacedCount}
221
+ addedCount={addedCount}
222
+ deletedCount={deletedCount}
223
+ onMetricClick={onMetricClick}
224
+ totalWords={data.metadata.total_words}
225
+ />
226
+ </Box>
227
+ </Box>
228
+
229
+ <Box sx={{
230
+ display: 'flex',
231
+ flexDirection: isMobile ? 'column' : 'row',
232
+ gap: 5,
233
+ alignItems: 'flex-start',
234
+ justifyContent: 'flex-start',
235
+ mb: 3
236
+ }}>
237
+ <ModeSelector
238
+ effectiveMode={effectiveMode}
239
+ onChange={onModeChange}
240
+ />
241
+ <AudioPlayer
242
+ apiClient={apiClient}
243
+ onTimeUpdate={onTimeUpdate}
244
+ audioHash={audioHash}
245
+ />
246
+ </Box>
247
+ </>
248
+ )
249
+ }