karaoke-gen 0.71.27__py3-none-any.whl → 0.75.16__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 (39) hide show
  1. karaoke_gen/__init__.py +32 -1
  2. karaoke_gen/audio_fetcher.py +476 -56
  3. karaoke_gen/audio_processor.py +11 -3
  4. karaoke_gen/file_handler.py +192 -0
  5. karaoke_gen/instrumental_review/__init__.py +45 -0
  6. karaoke_gen/instrumental_review/analyzer.py +408 -0
  7. karaoke_gen/instrumental_review/editor.py +322 -0
  8. karaoke_gen/instrumental_review/models.py +171 -0
  9. karaoke_gen/instrumental_review/server.py +475 -0
  10. karaoke_gen/instrumental_review/static/index.html +1506 -0
  11. karaoke_gen/instrumental_review/waveform.py +409 -0
  12. karaoke_gen/karaoke_finalise/karaoke_finalise.py +62 -1
  13. karaoke_gen/karaoke_gen.py +114 -1
  14. karaoke_gen/lyrics_processor.py +81 -4
  15. karaoke_gen/utils/bulk_cli.py +3 -0
  16. karaoke_gen/utils/cli_args.py +9 -2
  17. karaoke_gen/utils/gen_cli.py +379 -2
  18. karaoke_gen/utils/remote_cli.py +1126 -77
  19. {karaoke_gen-0.71.27.dist-info → karaoke_gen-0.75.16.dist-info}/METADATA +7 -1
  20. {karaoke_gen-0.71.27.dist-info → karaoke_gen-0.75.16.dist-info}/RECORD +38 -26
  21. lyrics_transcriber/correction/anchor_sequence.py +226 -350
  22. lyrics_transcriber/frontend/package.json +1 -1
  23. lyrics_transcriber/frontend/src/components/Header.tsx +38 -12
  24. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +17 -3
  25. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +185 -0
  26. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +704 -0
  27. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +80 -0
  28. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +905 -0
  29. lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +127 -0
  30. lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +190 -542
  31. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
  32. lyrics_transcriber/frontend/web_assets/assets/{index-DdJTDWH3.js → index-COYImAcx.js} +1722 -489
  33. lyrics_transcriber/frontend/web_assets/assets/index-COYImAcx.js.map +1 -0
  34. lyrics_transcriber/frontend/web_assets/index.html +1 -1
  35. lyrics_transcriber/review/server.py +5 -5
  36. lyrics_transcriber/frontend/web_assets/assets/index-DdJTDWH3.js.map +0 -1
  37. {karaoke_gen-0.71.27.dist-info → karaoke_gen-0.75.16.dist-info}/WHEEL +0 -0
  38. {karaoke_gen-0.71.27.dist-info → karaoke_gen-0.75.16.dist-info}/entry_points.txt +0 -0
  39. {karaoke_gen-0.71.27.dist-info → karaoke_gen-0.75.16.dist-info}/licenses/LICENSE +0 -0
@@ -2,7 +2,7 @@
2
2
  "name": "lyrics-transcriber-frontend",
3
3
  "private": true,
4
4
  "homepage": "https://nomadkaraoke.github.io/lyrics-transcriber-frontend",
5
- "version": "0.80.0",
5
+ "version": "0.82.0",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "dev": "vite",
@@ -1,4 +1,4 @@
1
- import { Box, Button, Typography, useMediaQuery, useTheme, Switch, FormControlLabel, Tooltip, Paper, IconButton } from '@mui/material'
1
+ import { Box, Button, Typography, useMediaQuery, useTheme, Switch, FormControlLabel, Tooltip, Paper, IconButton, Chip } from '@mui/material'
2
2
  import LockIcon from '@mui/icons-material/Lock'
3
3
  import UploadFileIcon from '@mui/icons-material/UploadFile'
4
4
  import FindReplaceIcon from '@mui/icons-material/FindReplace'
@@ -7,6 +7,7 @@ import UndoIcon from '@mui/icons-material/Undo'
7
7
  import RedoIcon from '@mui/icons-material/Redo'
8
8
  import TimerIcon from '@mui/icons-material/Timer'
9
9
  import RestoreIcon from '@mui/icons-material/Restore'
10
+ import RateReviewIcon from '@mui/icons-material/RateReview'
10
11
  import { CorrectionData, InteractionMode } from '../types'
11
12
  import CorrectionMetrics from './CorrectionMetrics'
12
13
  import AgenticCorrectionMetrics from './AgenticCorrectionMetrics'
@@ -41,6 +42,8 @@ interface HeaderProps {
41
42
  onRedo: () => void
42
43
  canUndo: boolean
43
44
  canRedo: boolean
45
+ annotationsEnabled?: boolean
46
+ onAnnotationsToggle?: (enabled: boolean) => void
44
47
  }
45
48
 
46
49
  export default function Header({
@@ -65,6 +68,8 @@ export default function Header({
65
68
  onRedo,
66
69
  canUndo,
67
70
  canRedo,
71
+ annotationsEnabled = true,
72
+ onAnnotationsToggle,
68
73
  }: HeaderProps) {
69
74
  const theme = useTheme()
70
75
  const isMobile = useMediaQuery(theme.breakpoints.down('md'))
@@ -135,17 +140,38 @@ export default function Header({
135
140
  <Typography variant="h4" sx={{ fontSize: isMobile ? '1.3rem' : '1.5rem' }}>
136
141
  Nomad Karaoke: Lyrics Transcription Review
137
142
  </Typography>
138
- {isReadOnly && (
139
- <Button
140
- variant="outlined"
141
- size="small"
142
- startIcon={<UploadFileIcon />}
143
- onClick={onFileLoad}
144
- fullWidth={isMobile}
145
- >
146
- Load File
147
- </Button>
148
- )}
143
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
144
+ {!isReadOnly && onAnnotationsToggle && (
145
+ <Tooltip title={annotationsEnabled
146
+ ? "Click to disable annotation prompts when editing"
147
+ : "Click to enable annotation prompts when editing"
148
+ }>
149
+ <Chip
150
+ icon={<RateReviewIcon />}
151
+ label={annotationsEnabled ? "Feedback On" : "Feedback Off"}
152
+ onClick={() => onAnnotationsToggle(!annotationsEnabled)}
153
+ color={annotationsEnabled ? "primary" : "default"}
154
+ variant={annotationsEnabled ? "filled" : "outlined"}
155
+ size="small"
156
+ sx={{
157
+ cursor: 'pointer',
158
+ '& .MuiChip-icon': { fontSize: '1rem' }
159
+ }}
160
+ />
161
+ </Tooltip>
162
+ )}
163
+ {isReadOnly && (
164
+ <Button
165
+ variant="outlined"
166
+ size="small"
167
+ startIcon={<UploadFileIcon />}
168
+ onClick={onFileLoad}
169
+ fullWidth={isMobile}
170
+ >
171
+ Load File
172
+ </Button>
173
+ )}
174
+ </Box>
149
175
  </Box>
150
176
 
151
177
  <Box sx={{
@@ -200,6 +200,8 @@ interface MemoizedHeaderProps {
200
200
  canUndo: boolean
201
201
  canRedo: boolean
202
202
  onUnCorrectAll: () => void
203
+ annotationsEnabled: boolean
204
+ onAnnotationsToggle: (enabled: boolean) => void
203
205
  }
204
206
 
205
207
  // Create a memoized Header component
@@ -224,7 +226,9 @@ const MemoizedHeader = memo(function MemoizedHeader({
224
226
  onRedo,
225
227
  canUndo,
226
228
  canRedo,
227
- onUnCorrectAll
229
+ onUnCorrectAll,
230
+ annotationsEnabled,
231
+ onAnnotationsToggle
228
232
  }: MemoizedHeaderProps) {
229
233
  return (
230
234
  <Header
@@ -249,6 +253,8 @@ const MemoizedHeader = memo(function MemoizedHeader({
249
253
  canUndo={canUndo}
250
254
  canRedo={canRedo}
251
255
  onUnCorrectAll={onUnCorrectAll}
256
+ annotationsEnabled={annotationsEnabled}
257
+ onAnnotationsToggle={onAnnotationsToggle}
252
258
  />
253
259
  );
254
260
  });
@@ -295,13 +301,18 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
295
301
  wordIdsAffected: string[]
296
302
  gapId?: string
297
303
  } | null>(null)
298
- const [annotationsEnabled] = useState(() => {
304
+ const [annotationsEnabled, setAnnotationsEnabled] = useState(() => {
299
305
  // Check localStorage for user preference
300
306
  const saved = localStorage.getItem('annotationsEnabled')
301
307
  return saved !== null ? saved === 'true' : true // Default: enabled
302
- // TODO: Add UI toggle to enable/disable via setAnnotationsEnabled
303
308
  })
304
309
 
310
+ // Persist annotation preference to localStorage
311
+ const handleAnnotationsToggle = useCallback((enabled: boolean) => {
312
+ setAnnotationsEnabled(enabled)
313
+ localStorage.setItem('annotationsEnabled', String(enabled))
314
+ }, [])
315
+
305
316
  // Correction detail card state
306
317
  const [correctionDetailOpen, setCorrectionDetailOpen] = useState(false)
307
318
  const [selectedCorrection, setSelectedCorrection] = useState<{
@@ -1164,6 +1175,8 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
1164
1175
  canUndo={canUndo}
1165
1176
  canRedo={canRedo}
1166
1177
  onUnCorrectAll={handleUnCorrectAll}
1178
+ annotationsEnabled={annotationsEnabled}
1179
+ onAnnotationsToggle={handleAnnotationsToggle}
1167
1180
  />
1168
1181
 
1169
1182
  <Grid container direction={isMobile ? 'column' : 'row'}>
@@ -1313,6 +1326,7 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
1313
1326
  onPlaySegment={handlePlaySegment}
1314
1327
  currentTime={currentAudioTime}
1315
1328
  setModalSpacebarHandler={handleSetModalSpacebarHandler}
1329
+ existingSegments={data.corrected_segments}
1316
1330
  />
1317
1331
 
1318
1332
  {pendingAnnotation && (
@@ -0,0 +1,185 @@
1
+ import { memo } from 'react'
2
+ import {
3
+ Box,
4
+ Button,
5
+ Divider,
6
+ Stack
7
+ } from '@mui/material'
8
+ import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'
9
+ import StopIcon from '@mui/icons-material/Stop'
10
+ import PauseIcon from '@mui/icons-material/Pause'
11
+ import PlayArrowIcon from '@mui/icons-material/PlayArrow'
12
+ import ClearAllIcon from '@mui/icons-material/ClearAll'
13
+ import EditNoteIcon from '@mui/icons-material/EditNote'
14
+ import BlockIcon from '@mui/icons-material/Block'
15
+ import EditIcon from '@mui/icons-material/Edit'
16
+ import DeleteIcon from '@mui/icons-material/Delete'
17
+
18
+ interface SyncControlsProps {
19
+ // Main buttons
20
+ isManualSyncing: boolean
21
+ isPaused: boolean
22
+ onStartSync: () => void
23
+ onPauseSync: () => void
24
+ onResumeSync: () => void
25
+ onClearSync: () => void
26
+ onEditLyrics: () => void
27
+
28
+ // Playback controls
29
+ onPlay: () => void
30
+ onStop: () => void
31
+ isPlaying: boolean
32
+
33
+ // Word action buttons
34
+ hasSelectedWords: boolean
35
+ selectedWordCount: number
36
+ onUnsyncFromCursor: () => void
37
+ onEditSelectedWord: () => void
38
+ onDeleteSelected: () => void
39
+
40
+ // Additional state
41
+ canUnsyncFromCursor: boolean
42
+ }
43
+
44
+ const SyncControls = memo(function SyncControls({
45
+ isManualSyncing,
46
+ isPaused,
47
+ onStartSync,
48
+ onPauseSync,
49
+ onResumeSync,
50
+ onClearSync,
51
+ onEditLyrics,
52
+ onPlay,
53
+ onStop,
54
+ isPlaying,
55
+ hasSelectedWords,
56
+ selectedWordCount,
57
+ onUnsyncFromCursor,
58
+ onEditSelectedWord,
59
+ onDeleteSelected,
60
+ canUnsyncFromCursor
61
+ }: SyncControlsProps) {
62
+ return (
63
+ <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1.5 }}>
64
+ {/* Row 1: Playback & Sync Mode Controls */}
65
+ <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap">
66
+ {/* Playback controls */}
67
+ <Button
68
+ variant="outlined"
69
+ color="primary"
70
+ onClick={onPlay}
71
+ startIcon={<PlayArrowIcon />}
72
+ size="small"
73
+ disabled={isPlaying}
74
+ >
75
+ Play
76
+ </Button>
77
+ <Button
78
+ variant="outlined"
79
+ color="error"
80
+ onClick={onStop}
81
+ startIcon={<StopIcon />}
82
+ size="small"
83
+ disabled={!isPlaying}
84
+ >
85
+ Stop
86
+ </Button>
87
+
88
+ <Divider orientation="vertical" flexItem sx={{ mx: 0.5 }} />
89
+
90
+ {/* Sync Mode controls */}
91
+ {isManualSyncing ? (
92
+ <>
93
+ <Button
94
+ variant="contained"
95
+ color="error"
96
+ onClick={onStartSync}
97
+ startIcon={<StopIcon />}
98
+ size="small"
99
+ >
100
+ Stop Sync
101
+ </Button>
102
+ <Button
103
+ variant="outlined"
104
+ color={isPaused ? 'success' : 'warning'}
105
+ onClick={isPaused ? onResumeSync : onPauseSync}
106
+ startIcon={isPaused ? <PlayArrowIcon /> : <PauseIcon />}
107
+ size="small"
108
+ >
109
+ {isPaused ? 'Resume' : 'Pause'}
110
+ </Button>
111
+ </>
112
+ ) : (
113
+ <Button
114
+ variant="contained"
115
+ color="primary"
116
+ onClick={onStartSync}
117
+ startIcon={<PlayCircleOutlineIcon />}
118
+ size="small"
119
+ >
120
+ Start Sync
121
+ </Button>
122
+ )}
123
+ </Stack>
124
+
125
+ {/* Row 2: Editing & Word Actions */}
126
+ <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap">
127
+ <Button
128
+ variant="outlined"
129
+ color="warning"
130
+ onClick={onClearSync}
131
+ startIcon={<ClearAllIcon />}
132
+ size="small"
133
+ disabled={isManualSyncing && !isPaused}
134
+ >
135
+ Clear Sync
136
+ </Button>
137
+
138
+ <Button
139
+ variant="outlined"
140
+ onClick={onEditLyrics}
141
+ startIcon={<EditNoteIcon />}
142
+ size="small"
143
+ disabled={isManualSyncing && !isPaused}
144
+ >
145
+ Edit Lyrics
146
+ </Button>
147
+
148
+ <Divider orientation="vertical" flexItem sx={{ mx: 0.5 }} />
149
+
150
+ <Button
151
+ variant="outlined"
152
+ onClick={onUnsyncFromCursor}
153
+ startIcon={<BlockIcon />}
154
+ size="small"
155
+ disabled={!canUnsyncFromCursor || (isManualSyncing && !isPaused)}
156
+ >
157
+ Unsync from Cursor
158
+ </Button>
159
+
160
+ <Button
161
+ variant="outlined"
162
+ onClick={onEditSelectedWord}
163
+ startIcon={<EditIcon />}
164
+ size="small"
165
+ disabled={!hasSelectedWords || selectedWordCount !== 1}
166
+ >
167
+ Edit Word
168
+ </Button>
169
+
170
+ <Button
171
+ variant="outlined"
172
+ color="error"
173
+ onClick={onDeleteSelected}
174
+ startIcon={<DeleteIcon />}
175
+ size="small"
176
+ disabled={!hasSelectedWords || (isManualSyncing && !isPaused)}
177
+ >
178
+ Delete{hasSelectedWords && selectedWordCount > 0 ? ` (${selectedWordCount})` : ''}
179
+ </Button>
180
+ </Stack>
181
+ </Box>
182
+ )
183
+ })
184
+
185
+ export default SyncControls