lyrics-transcriber 0.43.1__py3-none-any.whl → 0.44.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 (49) hide show
  1. lyrics_transcriber/core/controller.py +58 -24
  2. lyrics_transcriber/correction/anchor_sequence.py +22 -8
  3. lyrics_transcriber/correction/corrector.py +47 -3
  4. lyrics_transcriber/correction/handlers/llm.py +15 -12
  5. lyrics_transcriber/correction/handlers/llm_providers.py +60 -0
  6. lyrics_transcriber/frontend/.yarn/install-state.gz +0 -0
  7. lyrics_transcriber/frontend/dist/assets/{index-D0Gr3Ep7.js → index-DVoI6Z16.js} +10799 -7490
  8. lyrics_transcriber/frontend/dist/assets/index-DVoI6Z16.js.map +1 -0
  9. lyrics_transcriber/frontend/dist/index.html +1 -1
  10. lyrics_transcriber/frontend/src/App.tsx +4 -4
  11. lyrics_transcriber/frontend/src/api.ts +37 -0
  12. lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +114 -0
  13. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +14 -10
  14. lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +62 -56
  15. lyrics_transcriber/frontend/src/components/EditModal.tsx +232 -237
  16. lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +467 -0
  17. lyrics_transcriber/frontend/src/components/GlobalSyncEditor.tsx +675 -0
  18. lyrics_transcriber/frontend/src/components/Header.tsx +141 -101
  19. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +146 -80
  20. lyrics_transcriber/frontend/src/components/ModeSelector.tsx +22 -13
  21. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +1 -0
  22. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +29 -12
  23. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +21 -4
  24. lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +29 -15
  25. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +34 -16
  26. lyrics_transcriber/frontend/src/components/WordDivider.tsx +186 -0
  27. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +89 -41
  28. lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +9 -2
  29. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +28 -3
  30. lyrics_transcriber/frontend/src/components/shared/types.ts +17 -2
  31. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +63 -14
  32. lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +192 -0
  33. lyrics_transcriber/frontend/src/hooks/useManualSync.ts +267 -0
  34. lyrics_transcriber/frontend/src/main.tsx +7 -1
  35. lyrics_transcriber/frontend/src/theme.ts +177 -0
  36. lyrics_transcriber/frontend/src/types.ts +1 -1
  37. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
  38. lyrics_transcriber/lyrics/base_lyrics_provider.py +2 -2
  39. lyrics_transcriber/lyrics/user_input_provider.py +44 -0
  40. lyrics_transcriber/output/generator.py +40 -12
  41. lyrics_transcriber/review/server.py +238 -8
  42. {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/METADATA +3 -2
  43. {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/RECORD +46 -40
  44. lyrics_transcriber/frontend/dist/assets/index-D0Gr3Ep7.js.map +0 -1
  45. lyrics_transcriber/frontend/src/components/DetailsModal.tsx +0 -252
  46. lyrics_transcriber/frontend/src/components/WordEditControls.tsx +0 -110
  47. {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/LICENSE +0 -0
  48. {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/WHEEL +0 -0
  49. {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/entry_points.txt +0 -0
@@ -1,252 +0,0 @@
1
- import {
2
- Dialog,
3
- DialogTitle,
4
- DialogContent,
5
- IconButton,
6
- Grid,
7
- Typography,
8
- Box
9
- } from '@mui/material'
10
- import CloseIcon from '@mui/icons-material/Close'
11
- import { ModalContent } from './LyricsAnalyzer'
12
- import { WordCorrection, ReferenceSource, AnchorSequence } from '../types'
13
- import { getWordsFromIds } from './shared/utils/wordUtils'
14
-
15
- interface DetailsModalProps {
16
- open: boolean
17
- content: ModalContent | null
18
- onClose: () => void
19
- allCorrections: WordCorrection[]
20
- referenceLyrics?: Record<string, ReferenceSource>
21
- }
22
-
23
- function formatReferenceWords(content: ModalContent | null, referenceLyrics?: Record<string, ReferenceSource>): string {
24
- if (!content || !referenceLyrics) return ''
25
-
26
- return Object.entries(content.data.reference_word_ids)
27
- .map(([source, wordIds]) => {
28
- const words = getWordsFromIds(
29
- referenceLyrics[source]?.segments ?? [],
30
- wordIds
31
- )
32
- return `${source}: "${words.map(w => w.text).join(' ')}"`
33
- })
34
- .join('\n')
35
- }
36
-
37
- function getAnchorText(anchorId: string | null, anchors: AnchorSequence[], referenceLyrics?: Record<string, ReferenceSource>): string {
38
- if (!anchorId || !referenceLyrics) return anchorId || ''
39
-
40
- const anchor = anchors.find(a => a.id === anchorId)
41
- if (!anchor) return anchorId
42
-
43
- // Get the first source's words as representative text
44
- const firstSource = Object.entries(anchor.reference_word_ids)[0]
45
- if (!firstSource) return anchorId
46
-
47
- const [source, wordIds] = firstSource
48
- const words = getWordsFromIds(
49
- referenceLyrics[source]?.segments ?? [],
50
- wordIds
51
- )
52
- return `${anchorId} ("${words.map(w => w.text).join(' ')}")`
53
- }
54
-
55
- export default function DetailsModal({
56
- open,
57
- content,
58
- onClose,
59
- allCorrections,
60
- referenceLyrics
61
- }: DetailsModalProps) {
62
- if (!content) return null
63
-
64
- const referenceWordsText = formatReferenceWords(content, referenceLyrics)
65
- const relevantCorrections = content.type === 'gap' ? allCorrections.filter(c =>
66
- c.word_id === content.data.wordId ||
67
- c.corrected_word_id === content.data.wordId ||
68
- content.data.transcribed_word_ids.includes(c.word_id)
69
- ) : []
70
- const isCorrected = content.type === 'gap' && relevantCorrections.length > 0
71
-
72
- const getCurrentWord = () => {
73
- if (content.type === 'gap') {
74
- return content.data.word
75
- } else if (content.type === 'anchor') {
76
- return content.data.word ?? ''
77
- }
78
- return ''
79
- }
80
-
81
- const renderContent = () => {
82
- const anchorWords = content.type === 'anchor' ? content.data.transcribed_word_ids?.length ?? 0 : 0
83
-
84
- switch (content.type) {
85
- case 'anchor':
86
- return (
87
- <Grid container spacing={2}>
88
- <GridItem
89
- title="Selected Word"
90
- value={`"${getCurrentWord()}"`}
91
- />
92
- <GridItem
93
- title="Full Text"
94
- value={`"${content.data.word ?? ''}"`}
95
- />
96
- <GridItem title="Word ID" value={content.data.wordId} />
97
- <GridItem
98
- title="Length"
99
- value={`${anchorWords} words`}
100
- />
101
- <GridItem
102
- title="Reference Words"
103
- value={
104
- <pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>{referenceWordsText}</pre>
105
- }
106
- />
107
- <GridItem title="Confidence" value={content.data.confidence?.toFixed(3) ?? 'N/A'} />
108
- {content.data.phrase_score && (
109
- <GridItem
110
- title="Phrase Score Details"
111
- value={
112
- <>
113
- <Typography>Type: {content.data.phrase_score.phrase_type}</Typography>
114
- <Typography>Natural Break: {content.data.phrase_score.natural_break_score?.toFixed(3) ?? 'N/A'}</Typography>
115
- <Typography>Length: {content.data.phrase_score.length_score?.toFixed(3) ?? 'N/A'}</Typography>
116
- <Typography>Total: {content.data.phrase_score.total_score?.toFixed(3) ?? 'N/A'}</Typography>
117
- </>
118
- }
119
- />
120
- )}
121
- <GridItem title="Total Score" value={content.data.total_score?.toFixed(3) ?? 'N/A'} />
122
- </Grid>
123
- )
124
-
125
- case 'gap':
126
- return (
127
- <Grid container spacing={2}>
128
- <GridItem
129
- title="Selected Word"
130
- value={`"${getCurrentWord()}"`}
131
- />
132
- <GridItem
133
- title="Word ID"
134
- value={content.data.wordId}
135
- />
136
- <GridItem
137
- title="Length"
138
- value={`${content.data.transcribed_word_ids?.length ?? 0} words`}
139
- />
140
- {content.data.preceding_anchor_id && (
141
- <GridItem
142
- title="Preceding Anchor"
143
- value={getAnchorText(content.data.preceding_anchor_id, content.data.anchor_sequences, referenceLyrics)}
144
- />
145
- )}
146
- {content.data.following_anchor_id && (
147
- <GridItem
148
- title="Following Anchor"
149
- value={getAnchorText(content.data.following_anchor_id, content.data.anchor_sequences, referenceLyrics)}
150
- />
151
- )}
152
- <GridItem
153
- title="Reference Words"
154
- value={
155
- <pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>{referenceWordsText}</pre>
156
- }
157
- />
158
- {isCorrected && (
159
- <GridItem
160
- title="Correction Details"
161
- value={
162
- <>
163
- {relevantCorrections.map((correction, index) => (
164
- <Box key={index} sx={{ mb: 2, p: 1, border: '1px solid #ccc', borderRadius: '4px' }}>
165
- <Typography variant="subtitle2" fontWeight="bold">Correction {index + 1}</Typography>
166
- <Typography>Original: <strong>"{correction.original_word}"</strong></Typography>
167
- <Typography>Corrected: <strong>"{correction.corrected_word}"</strong></Typography>
168
- <Typography>Word ID: {correction.word_id}</Typography>
169
- <Typography>Confidence: {correction.confidence?.toFixed(3) ?? 'N/A'}</Typography>
170
- <Typography>Handler: {correction.handler}</Typography>
171
- <Typography>Source: {correction.source}</Typography>
172
- <Typography>Reason: {correction.reason}</Typography>
173
- {correction.is_deletion && <Typography>Is Deletion: Yes</Typography>}
174
- {correction.split_total && (
175
- <Typography>Split: {correction.split_index} of {correction.split_total}</Typography>
176
- )}
177
- {correction.alternatives && Object.entries(correction.alternatives).length > 0 && (
178
- <>
179
- <Typography>Alternatives:</Typography>
180
- <Box sx={{ pl: 2 }}>
181
- {Object.entries(correction.alternatives).map(([word, score]) => (
182
- <Typography key={word}>
183
- "{word}": {(score || 0).toFixed(3)}
184
- </Typography>
185
- ))}
186
- </Box>
187
- </>
188
- )}
189
- </Box>
190
- ))}
191
- </>
192
- }
193
- />
194
- )}
195
- </Grid>
196
- )
197
-
198
- default:
199
- return null
200
- }
201
- }
202
-
203
- return (
204
- <Dialog
205
- open={open}
206
- onClose={onClose}
207
- maxWidth="md"
208
- fullWidth
209
- >
210
- <DialogTitle>
211
- {content.type === 'gap' && (
212
- isCorrected ? 'Corrected ' : 'Uncorrected '
213
- )}
214
- {content.type.charAt(0).toUpperCase() + content.type.slice(1)} Details - "{getCurrentWord()}"
215
- <IconButton
216
- aria-label="close"
217
- onClick={onClose}
218
- sx={{ position: 'absolute', right: 8, top: 8 }}
219
- >
220
- <CloseIcon />
221
- </IconButton>
222
- </DialogTitle>
223
- <DialogContent>
224
- {renderContent()}
225
- </DialogContent>
226
- </Dialog>
227
- )
228
- }
229
-
230
- interface GridItemProps {
231
- title: string
232
- value: string | number | React.ReactNode
233
- }
234
-
235
- function GridItem({ title, value }: GridItemProps) {
236
- return (
237
- <>
238
- <Grid item xs={4}>
239
- <Typography variant="subtitle1" fontWeight="bold">
240
- {title}
241
- </Typography>
242
- </Grid>
243
- <Grid item xs={8}>
244
- {typeof value === 'string' || typeof value === 'number' ? (
245
- <Typography>{value}</Typography>
246
- ) : (
247
- value
248
- )}
249
- </Grid>
250
- </>
251
- )
252
- }
@@ -1,110 +0,0 @@
1
- import { Button, Box, TextField } from '@mui/material'
2
- import DeleteIcon from '@mui/icons-material/Delete'
3
- import { useState, useEffect } from 'react'
4
- import { ModalContent } from './LyricsAnalyzer'
5
-
6
- interface WordEditControlsProps {
7
- content: ModalContent
8
- onUpdateCorrection?: (wordId: string, updatedWords: string[]) => void
9
- onClose: () => void
10
- }
11
-
12
- export function useWordEdit(content: ModalContent | null) {
13
- const [editedWord, setEditedWord] = useState('')
14
- const [isEditing, setIsEditing] = useState(false)
15
-
16
- useEffect(() => {
17
- if (content) {
18
- setEditedWord(content.data.word || '')
19
- setIsEditing(false)
20
- }
21
- }, [content])
22
-
23
- return {
24
- editedWord,
25
- setEditedWord,
26
- isEditing,
27
- setIsEditing
28
- }
29
- }
30
-
31
- export default function WordEditControls({ content, onUpdateCorrection, onClose }: WordEditControlsProps) {
32
- const {
33
- editedWord,
34
- setEditedWord,
35
- isEditing,
36
- setIsEditing
37
- } = useWordEdit(content)
38
-
39
- const handleStartEdit = () => {
40
- setEditedWord(content.data.word || '')
41
- setIsEditing(true)
42
- }
43
-
44
- const handleDelete = () => {
45
- if (!onUpdateCorrection) return
46
- onUpdateCorrection(content.data.wordId, [])
47
- onClose()
48
- }
49
-
50
- const handleSaveEdit = () => {
51
- if (onUpdateCorrection) {
52
- onUpdateCorrection(content.data.wordId, [editedWord])
53
- }
54
- onClose()
55
- }
56
-
57
- const handleCancelEdit = () => {
58
- setEditedWord(content.data.word || '')
59
- setIsEditing(false)
60
- }
61
-
62
- const handleWordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
63
- setEditedWord(event.target.value)
64
- }
65
-
66
- return isEditing ? (
67
- <Box>
68
- <TextField
69
- value={editedWord}
70
- onChange={handleWordChange}
71
- fullWidth
72
- label="Edit word"
73
- variant="outlined"
74
- size="small"
75
- sx={{ mb: 1 }}
76
- />
77
- <Box sx={{ display: 'flex', gap: 1 }}>
78
- <Button variant="contained" onClick={handleSaveEdit}>
79
- Save Changes
80
- </Button>
81
- <Button variant="outlined" onClick={handleCancelEdit}>
82
- Cancel
83
- </Button>
84
- <Button
85
- variant="outlined"
86
- color="error"
87
- startIcon={<DeleteIcon />}
88
- onClick={handleDelete}
89
- >
90
- Delete
91
- </Button>
92
- </Box>
93
- </Box>
94
- ) : (
95
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
96
- <Button variant="outlined" size="small" onClick={handleStartEdit}>
97
- Edit
98
- </Button>
99
- <Button
100
- variant="outlined"
101
- size="small"
102
- color="error"
103
- startIcon={<DeleteIcon />}
104
- onClick={handleDelete}
105
- >
106
- Delete
107
- </Button>
108
- </Box>
109
- )
110
- }