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.
- lyrics_transcriber/core/controller.py +58 -24
- lyrics_transcriber/correction/anchor_sequence.py +22 -8
- lyrics_transcriber/correction/corrector.py +47 -3
- lyrics_transcriber/correction/handlers/llm.py +15 -12
- lyrics_transcriber/correction/handlers/llm_providers.py +60 -0
- lyrics_transcriber/frontend/.yarn/install-state.gz +0 -0
- lyrics_transcriber/frontend/dist/assets/{index-D0Gr3Ep7.js → index-DVoI6Z16.js} +10799 -7490
- lyrics_transcriber/frontend/dist/assets/index-DVoI6Z16.js.map +1 -0
- lyrics_transcriber/frontend/dist/index.html +1 -1
- lyrics_transcriber/frontend/src/App.tsx +4 -4
- lyrics_transcriber/frontend/src/api.ts +37 -0
- lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +114 -0
- lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +14 -10
- lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +62 -56
- lyrics_transcriber/frontend/src/components/EditModal.tsx +232 -237
- lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +467 -0
- lyrics_transcriber/frontend/src/components/GlobalSyncEditor.tsx +675 -0
- lyrics_transcriber/frontend/src/components/Header.tsx +141 -101
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +146 -80
- lyrics_transcriber/frontend/src/components/ModeSelector.tsx +22 -13
- lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +1 -0
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +29 -12
- lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +21 -4
- lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +29 -15
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +34 -16
- lyrics_transcriber/frontend/src/components/WordDivider.tsx +186 -0
- lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +89 -41
- lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +9 -2
- lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +28 -3
- lyrics_transcriber/frontend/src/components/shared/types.ts +17 -2
- lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +63 -14
- lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +192 -0
- lyrics_transcriber/frontend/src/hooks/useManualSync.ts +267 -0
- lyrics_transcriber/frontend/src/main.tsx +7 -1
- lyrics_transcriber/frontend/src/theme.ts +177 -0
- lyrics_transcriber/frontend/src/types.ts +1 -1
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
- lyrics_transcriber/lyrics/base_lyrics_provider.py +2 -2
- lyrics_transcriber/lyrics/user_input_provider.py +44 -0
- lyrics_transcriber/output/generator.py +40 -12
- lyrics_transcriber/review/server.py +238 -8
- {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/METADATA +3 -2
- {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/RECORD +46 -40
- lyrics_transcriber/frontend/dist/assets/index-D0Gr3Ep7.js.map +0 -1
- lyrics_transcriber/frontend/src/components/DetailsModal.tsx +0 -252
- lyrics_transcriber/frontend/src/components/WordEditControls.tsx +0 -110
- {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/LICENSE +0 -0
- {lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/WHEEL +0 -0
- {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
|
-
}
|
File without changes
|
File without changes
|
{lyrics_transcriber-0.43.1.dist-info → lyrics_transcriber-0.44.0.dist-info}/entry_points.txt
RENAMED
File without changes
|