lyrics-transcriber 0.36.1__py3-none-any.whl → 0.37.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 +22 -2
- lyrics_transcriber/correction/corrector.py +8 -8
- lyrics_transcriber/correction/handlers/base.py +4 -0
- lyrics_transcriber/correction/handlers/extend_anchor.py +9 -0
- lyrics_transcriber/correction/handlers/no_space_punct_match.py +21 -10
- lyrics_transcriber/correction/handlers/relaxed_word_count_match.py +21 -11
- lyrics_transcriber/correction/handlers/syllables_match.py +4 -4
- lyrics_transcriber/correction/handlers/word_count_match.py +19 -10
- lyrics_transcriber/frontend/dist/assets/index-BNNbsbVN.js +182 -0
- lyrics_transcriber/frontend/dist/index.html +1 -1
- lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +1 -2
- lyrics_transcriber/frontend/src/components/DetailsModal.tsx +76 -70
- lyrics_transcriber/frontend/src/components/EditModal.tsx +10 -2
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +128 -125
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +1 -3
- lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +24 -12
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +8 -15
- lyrics_transcriber/frontend/src/components/WordEditControls.tsx +3 -3
- lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +34 -52
- lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +39 -31
- lyrics_transcriber/frontend/src/components/shared/types.ts +3 -3
- lyrics_transcriber/frontend/src/components/shared/utils/initializeDataWithIds.tsx +146 -0
- lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +23 -24
- lyrics_transcriber/frontend/src/types.ts +25 -15
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
- lyrics_transcriber/output/cdg.py +32 -6
- lyrics_transcriber/output/video.py +17 -7
- lyrics_transcriber/review/server.py +24 -8
- {lyrics_transcriber-0.36.1.dist-info → lyrics_transcriber-0.37.0.dist-info}/METADATA +1 -1
- {lyrics_transcriber-0.36.1.dist-info → lyrics_transcriber-0.37.0.dist-info}/RECORD +33 -33
- {lyrics_transcriber-0.36.1.dist-info → lyrics_transcriber-0.37.0.dist-info}/entry_points.txt +1 -0
- lyrics_transcriber/frontend/dist/assets/index-ztlAYPYT.js +0 -181
- lyrics_transcriber/frontend/src/components/shared/utils/newlineCalculator.ts +0 -37
- {lyrics_transcriber-0.36.1.dist-info → lyrics_transcriber-0.37.0.dist-info}/LICENSE +0 -0
- {lyrics_transcriber-0.36.1.dist-info → lyrics_transcriber-0.37.0.dist-info}/WHEEL +0 -0
@@ -5,7 +5,7 @@
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7
7
|
<title>Lyrics Transcriber Analyzer</title>
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
8
|
+
<script type="module" crossorigin src="/assets/index-BNNbsbVN.js"></script>
|
9
9
|
</head>
|
10
10
|
<body>
|
11
11
|
<div id="root"></div>
|
@@ -73,7 +73,7 @@ interface CorrectionMetricsProps {
|
|
73
73
|
correctedGapCount?: number
|
74
74
|
uncorrectedGapCount?: number
|
75
75
|
uncorrectedGaps?: Array<{
|
76
|
-
position:
|
76
|
+
position: string
|
77
77
|
length: number
|
78
78
|
}>
|
79
79
|
// Correction details
|
@@ -106,7 +106,6 @@ export default function CorrectionMetrics({
|
|
106
106
|
const anchorPercentage = totalWords > 0 ? Math.round((anchorWordCount / totalWords) * 100) : 0
|
107
107
|
const uncorrectedWordCount = uncorrectedGaps?.reduce((sum, gap) => sum + gap.length, 0) ?? 0
|
108
108
|
const uncorrectedPercentage = totalWords > 0 ? Math.round((uncorrectedWordCount / totalWords) * 100) : 0
|
109
|
-
// For corrected percentage, we'll use the total affected words
|
110
109
|
const correctedWordCount = replacedCount + addedCount
|
111
110
|
const correctedPercentage = totalWords > 0 ?
|
112
111
|
Math.round((correctedWordCount / totalWords) * 100) : 0
|
@@ -4,7 +4,8 @@ import {
|
|
4
4
|
DialogContent,
|
5
5
|
IconButton,
|
6
6
|
Grid,
|
7
|
-
Typography
|
7
|
+
Typography,
|
8
|
+
Box
|
8
9
|
} from '@mui/material'
|
9
10
|
import CloseIcon from '@mui/icons-material/Close'
|
10
11
|
import { ModalContent } from './LyricsAnalyzer'
|
@@ -31,12 +32,9 @@ export default function DetailsModal({
|
|
31
32
|
return ''
|
32
33
|
}
|
33
34
|
|
34
|
-
const
|
35
|
-
// Move declaration outside of case block
|
36
|
-
const correction = content.type === 'gap'
|
37
|
-
? content.data.corrections.find(c => c.original_word === content.data.word)
|
38
|
-
: null
|
35
|
+
const isCorrected = content.type === 'gap' && content.data.corrections?.length > 0
|
39
36
|
|
37
|
+
const renderContent = () => {
|
40
38
|
switch (content.type) {
|
41
39
|
case 'anchor':
|
42
40
|
return (
|
@@ -49,27 +47,31 @@ export default function DetailsModal({
|
|
49
47
|
title="Full Text"
|
50
48
|
value={`"${content.data.text}"`}
|
51
49
|
/>
|
52
|
-
<GridItem title="
|
50
|
+
<GridItem title="Word ID" value={content.data.wordId} />
|
53
51
|
<GridItem title="Length" value={`${content.data.length} words`} />
|
54
52
|
<GridItem
|
55
|
-
title="Reference
|
56
|
-
value={
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
<GridItem title="Confidence" value={content.data.confidence.toFixed(3)} />
|
61
|
-
<GridItem
|
62
|
-
title="Phrase Score Details"
|
63
|
-
value={
|
64
|
-
<>
|
65
|
-
<Typography>Type: {content.data.phrase_score.phrase_type}</Typography>
|
66
|
-
<Typography>Natural Break: {content.data.phrase_score.natural_break_score.toFixed(3)}</Typography>
|
67
|
-
<Typography>Length: {content.data.phrase_score.length_score.toFixed(3)}</Typography>
|
68
|
-
<Typography>Total: {content.data.phrase_score.total_score.toFixed(3)}</Typography>
|
69
|
-
</>
|
53
|
+
title="Reference Word IDs"
|
54
|
+
value={content.data.reference_word_ids ?
|
55
|
+
Object.entries(content.data.reference_word_ids).map(([source, ids]) => (
|
56
|
+
`${source}: ${ids?.join(', ') ?? 'No IDs'}`
|
57
|
+
)).join('\n') : 'No reference word IDs'
|
70
58
|
}
|
71
59
|
/>
|
72
|
-
<GridItem title="
|
60
|
+
<GridItem title="Confidence" value={content.data.confidence?.toFixed(3) ?? 'N/A'} />
|
61
|
+
{content.data.phrase_score && (
|
62
|
+
<GridItem
|
63
|
+
title="Phrase Score Details"
|
64
|
+
value={
|
65
|
+
<>
|
66
|
+
<Typography>Type: {content.data.phrase_score.phrase_type}</Typography>
|
67
|
+
<Typography>Natural Break: {content.data.phrase_score.natural_break_score?.toFixed(3) ?? 'N/A'}</Typography>
|
68
|
+
<Typography>Length: {content.data.phrase_score.length_score?.toFixed(3) ?? 'N/A'}</Typography>
|
69
|
+
<Typography>Total: {content.data.phrase_score.total_score?.toFixed(3) ?? 'N/A'}</Typography>
|
70
|
+
</>
|
71
|
+
}
|
72
|
+
/>
|
73
|
+
)}
|
74
|
+
<GridItem title="Total Score" value={content.data.total_score?.toFixed(3) ?? 'N/A'} />
|
73
75
|
</Grid>
|
74
76
|
)
|
75
77
|
|
@@ -80,22 +82,18 @@ export default function DetailsModal({
|
|
80
82
|
title="Selected Word"
|
81
83
|
value={`"${getCurrentWord()}"`}
|
82
84
|
/>
|
83
|
-
<GridItem
|
84
|
-
title="Transcribed Text"
|
85
|
-
value={`"${content.data.text}"`}
|
86
|
-
/>
|
87
85
|
<GridItem
|
88
86
|
title="Current Text"
|
89
|
-
value={`"${content.data.words
|
90
|
-
const wordCorrection = content.data.corrections
|
87
|
+
value={`"${content.data.words?.map(word => {
|
88
|
+
const wordCorrection = content.data.corrections?.find(
|
91
89
|
c => c.original_word === word
|
92
90
|
)
|
93
91
|
return wordCorrection ? wordCorrection.corrected_word : word
|
94
|
-
}).join(' ')}"`}
|
92
|
+
}).join(' ') ?? content.data.word}"`}
|
95
93
|
/>
|
96
94
|
<GridItem
|
97
|
-
title="
|
98
|
-
value={content.data.
|
95
|
+
title="Word ID"
|
96
|
+
value={content.data.wordId}
|
99
97
|
/>
|
100
98
|
<GridItem
|
101
99
|
title="Length"
|
@@ -107,6 +105,10 @@ export default function DetailsModal({
|
|
107
105
|
value={`"${content.data.preceding_anchor.text}"`}
|
108
106
|
/>
|
109
107
|
)}
|
108
|
+
<GridItem
|
109
|
+
title="Transcribed Text"
|
110
|
+
value={`"${content.data.text}"`}
|
111
|
+
/>
|
110
112
|
{content.data.following_anchor && (
|
111
113
|
<GridItem
|
112
114
|
title="Following Anchor"
|
@@ -118,48 +120,51 @@ export default function DetailsModal({
|
|
118
120
|
title="Reference Words"
|
119
121
|
value={
|
120
122
|
<>
|
121
|
-
{
|
122
|
-
<Typography
|
123
|
-
|
124
|
-
|
125
|
-
|
123
|
+
{content.data.reference_words.spotify && (
|
124
|
+
<Typography>Spotify: "{content.data.reference_words.spotify.join(' ')}"</Typography>
|
125
|
+
)}
|
126
|
+
{content.data.reference_words.genius && (
|
127
|
+
<Typography>Genius: "{content.data.reference_words.genius.join(' ')}"</Typography>
|
128
|
+
)}
|
126
129
|
</>
|
127
130
|
}
|
128
131
|
/>
|
129
132
|
)}
|
130
|
-
{
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
<
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
<Typography>
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
133
|
+
{isCorrected && content.data.corrections && (
|
134
|
+
<GridItem
|
135
|
+
title="Correction Details"
|
136
|
+
value={
|
137
|
+
<>
|
138
|
+
{content.data.corrections.map((correction, index) => (
|
139
|
+
<Box key={index} sx={{ mb: 2, p: 1, border: '1px solid #ccc', borderRadius: '4px' }}>
|
140
|
+
<Typography variant="subtitle2" fontWeight="bold">Correction {index + 1}</Typography>
|
141
|
+
<Typography>Original: <strong>"{correction.original_word}"</strong></Typography>
|
142
|
+
<Typography>Corrected: <strong>"{correction.corrected_word}"</strong></Typography>
|
143
|
+
<Typography>Word ID: {correction.word_id}</Typography>
|
144
|
+
<Typography>Confidence: {correction.confidence?.toFixed(3) ?? 'N/A'}</Typography>
|
145
|
+
<Typography>Source: {correction.source}</Typography>
|
146
|
+
<Typography>Reason: {correction.reason}</Typography>
|
147
|
+
{correction.is_deletion && <Typography>Is Deletion: Yes</Typography>}
|
148
|
+
{correction.split_total && (
|
149
|
+
<Typography>Split: {correction.split_index} of {correction.split_total}</Typography>
|
150
|
+
)}
|
151
|
+
{correction.alternatives && Object.entries(correction.alternatives).length > 0 && (
|
152
|
+
<>
|
153
|
+
<Typography>Alternatives:</Typography>
|
154
|
+
<Box sx={{ pl: 2 }}>
|
155
|
+
{Object.entries(correction.alternatives).map(([word, score]) => (
|
156
|
+
<Typography key={word}>
|
157
|
+
"{word}": {score?.toFixed(3) ?? 'N/A'}
|
158
|
+
</Typography>
|
159
|
+
))}
|
160
|
+
</Box>
|
161
|
+
</>
|
162
|
+
)}
|
163
|
+
</Box>
|
164
|
+
))}
|
165
|
+
</>
|
166
|
+
}
|
167
|
+
/>
|
163
168
|
)}
|
164
169
|
</Grid>
|
165
170
|
)
|
@@ -187,6 +192,7 @@ export default function DetailsModal({
|
|
187
192
|
<CloseIcon />
|
188
193
|
</IconButton>
|
189
194
|
<DialogTitle>
|
195
|
+
{content.type === 'gap' && (isCorrected ? 'Corrected ' : 'Uncorrected ')}
|
190
196
|
{content.type.charAt(0).toUpperCase() + content.type.slice(1)} Details - "{getCurrentWord()}"
|
191
197
|
</DialogTitle>
|
192
198
|
<DialogContent dividers>{renderContent()}</DialogContent>
|
@@ -23,6 +23,7 @@ import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'
|
|
23
23
|
import { LyricsSegment, Word } from '../types'
|
24
24
|
import { useState, useEffect } from 'react'
|
25
25
|
import TimelineEditor from './TimelineEditor'
|
26
|
+
import { nanoid } from 'nanoid'
|
26
27
|
|
27
28
|
interface EditModalProps {
|
28
29
|
open: boolean
|
@@ -91,6 +92,7 @@ export default function EditModal({
|
|
91
92
|
// Add at end
|
92
93
|
const lastWord = newWords[newWords.length - 1]
|
93
94
|
newWord = {
|
95
|
+
id: nanoid(),
|
94
96
|
text: '',
|
95
97
|
start_time: lastWord.end_time,
|
96
98
|
end_time: lastWord.end_time + 0.5,
|
@@ -106,6 +108,7 @@ export default function EditModal({
|
|
106
108
|
(nextWord ? nextWord.start_time - 0.5 : 0)
|
107
109
|
|
108
110
|
newWord = {
|
111
|
+
id: nanoid(),
|
109
112
|
text: '',
|
110
113
|
start_time: midTime - 0.25,
|
111
114
|
end_time: midTime + 0.25,
|
@@ -133,12 +136,14 @@ export default function EditModal({
|
|
133
136
|
const newWords = [...editedSegment.words]
|
134
137
|
newWords.splice(index, 1,
|
135
138
|
{
|
139
|
+
id: nanoid(),
|
136
140
|
text: words[0],
|
137
141
|
start_time: word.start_time,
|
138
142
|
end_time: midTime,
|
139
143
|
confidence: 1.0
|
140
144
|
},
|
141
145
|
{
|
146
|
+
id: nanoid(),
|
142
147
|
text: words[1],
|
143
148
|
start_time: midTime,
|
144
149
|
end_time: word.end_time,
|
@@ -157,6 +162,7 @@ export default function EditModal({
|
|
157
162
|
const newWords = [...editedSegment.words]
|
158
163
|
|
159
164
|
newWords.splice(index, 2, {
|
165
|
+
id: nanoid(),
|
160
166
|
text: `${word1.text} ${word2.text}`.trim(),
|
161
167
|
start_time: word1.start_time,
|
162
168
|
end_time: word2.end_time,
|
@@ -208,17 +214,19 @@ export default function EditModal({
|
|
208
214
|
let updatedWords: Word[]
|
209
215
|
|
210
216
|
if (newWords.length === editedSegment.words.length) {
|
211
|
-
// If word count matches, keep original timestamps
|
217
|
+
// If word count matches, keep original timestamps and IDs
|
212
218
|
updatedWords = editedSegment.words.map((word, index) => ({
|
219
|
+
id: word.id, // Keep original ID
|
213
220
|
text: newWords[index],
|
214
221
|
start_time: word.start_time,
|
215
222
|
end_time: word.end_time,
|
216
223
|
confidence: 1.0
|
217
224
|
}))
|
218
225
|
} else {
|
219
|
-
// If word count differs, distribute time evenly
|
226
|
+
// If word count differs, distribute time evenly and generate new IDs
|
220
227
|
const avgWordDuration = segmentDuration / newWords.length
|
221
228
|
updatedWords = newWords.map((text, index) => ({
|
229
|
+
id: nanoid(), // Generate new ID
|
222
230
|
text,
|
223
231
|
start_time: editedSegment.start_time + (index * avgWordDuration),
|
224
232
|
end_time: editedSegment.start_time + ((index + 1) * avgWordDuration),
|