karaoke-gen 0.75.16__py3-none-any.whl → 0.76.20__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 (47) hide show
  1. karaoke_gen/audio_fetcher.py +984 -33
  2. karaoke_gen/audio_processor.py +4 -0
  3. karaoke_gen/instrumental_review/static/index.html +37 -14
  4. karaoke_gen/karaoke_finalise/karaoke_finalise.py +25 -1
  5. karaoke_gen/karaoke_gen.py +208 -39
  6. karaoke_gen/lyrics_processor.py +111 -31
  7. karaoke_gen/utils/__init__.py +26 -0
  8. karaoke_gen/utils/cli_args.py +15 -6
  9. karaoke_gen/utils/gen_cli.py +30 -5
  10. karaoke_gen/utils/remote_cli.py +301 -20
  11. {karaoke_gen-0.75.16.dist-info → karaoke_gen-0.76.20.dist-info}/METADATA +107 -5
  12. {karaoke_gen-0.75.16.dist-info → karaoke_gen-0.76.20.dist-info}/RECORD +47 -43
  13. lyrics_transcriber/core/controller.py +76 -2
  14. lyrics_transcriber/frontend/index.html +5 -1
  15. lyrics_transcriber/frontend/package-lock.json +4553 -0
  16. lyrics_transcriber/frontend/package.json +4 -1
  17. lyrics_transcriber/frontend/playwright.config.ts +69 -0
  18. lyrics_transcriber/frontend/public/nomad-karaoke-logo.svg +5 -0
  19. lyrics_transcriber/frontend/src/App.tsx +94 -63
  20. lyrics_transcriber/frontend/src/api.ts +25 -10
  21. lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +55 -21
  22. lyrics_transcriber/frontend/src/components/AppHeader.tsx +65 -0
  23. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +5 -5
  24. lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +9 -9
  25. lyrics_transcriber/frontend/src/components/EditModal.tsx +1 -1
  26. lyrics_transcriber/frontend/src/components/EditWordList.tsx +1 -1
  27. lyrics_transcriber/frontend/src/components/Header.tsx +34 -48
  28. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +22 -21
  29. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +1 -1
  30. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +1 -1
  31. lyrics_transcriber/frontend/src/components/WordDivider.tsx +3 -3
  32. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +2 -2
  33. lyrics_transcriber/frontend/src/components/shared/constants.ts +15 -5
  34. lyrics_transcriber/frontend/src/main.tsx +1 -7
  35. lyrics_transcriber/frontend/src/theme.ts +337 -135
  36. lyrics_transcriber/frontend/vite.config.ts +5 -0
  37. lyrics_transcriber/frontend/web_assets/assets/{index-COYImAcx.js → index-BECn1o8Q.js} +38 -22
  38. lyrics_transcriber/frontend/web_assets/assets/{index-COYImAcx.js.map → index-BECn1o8Q.js.map} +1 -1
  39. lyrics_transcriber/frontend/web_assets/index.html +1 -1
  40. lyrics_transcriber/frontend/yarn.lock +1005 -1046
  41. lyrics_transcriber/output/countdown_processor.py +39 -0
  42. lyrics_transcriber/review/server.py +1 -1
  43. lyrics_transcriber/transcribers/audioshake.py +96 -7
  44. lyrics_transcriber/types.py +14 -12
  45. {karaoke_gen-0.75.16.dist-info → karaoke_gen-0.76.20.dist-info}/WHEEL +0 -0
  46. {karaoke_gen-0.75.16.dist-info → karaoke_gen-0.76.20.dist-info}/entry_points.txt +0 -0
  47. {karaoke_gen-0.75.16.dist-info → karaoke_gen-0.76.20.dist-info}/licenses/LICENSE +0 -0
@@ -30,7 +30,7 @@ const SegmentTimeline = styled(Box)({
30
30
  const TimelineRuler = styled(Box)({
31
31
  position: 'relative',
32
32
  height: '20px',
33
- borderBottom: '1px solid #ccc',
33
+ borderBottom: '1px solid #2a2a2a', // slate-700 for dark mode
34
34
  marginBottom: '4px'
35
35
  })
36
36
 
@@ -38,14 +38,14 @@ const TimelineMark = styled(Box)({
38
38
  position: 'absolute',
39
39
  width: '1px',
40
40
  height: '8px',
41
- backgroundColor: '#999',
41
+ backgroundColor: '#666666', // slate-500 for dark mode
42
42
  bottom: 0
43
43
  })
44
44
 
45
45
  const TimelineLabel = styled(Typography)({
46
46
  position: 'absolute',
47
47
  fontSize: '0.65rem',
48
- color: '#666',
48
+ color: '#888888', // slate-400 for dark mode
49
49
  bottom: '10px',
50
50
  transform: 'translateX(-50%)',
51
51
  whiteSpace: 'nowrap'
@@ -58,7 +58,7 @@ const WordsBar = styled(Box)({
58
58
  alignItems: 'stretch',
59
59
  minWidth: '100%',
60
60
  touchAction: 'pan-y', // Better mobile scrolling
61
- backgroundColor: '#f5f5f5',
61
+ backgroundColor: '#0f0f0f', // slate-900 for dark mode
62
62
  borderRadius: '4px',
63
63
  marginBottom: '8px'
64
64
  })
@@ -84,7 +84,7 @@ const WordBar = styled(Box, {
84
84
  ? COLORS.corrected
85
85
  : isGap
86
86
  ? COLORS.uncorrectedGap
87
- : '#e0e0e0',
87
+ : '#2a2a2a', // slate-700 for dark mode default
88
88
  border: isLong ? '2px solid #f44336' : 'none',
89
89
  boxShadow: isLong ? '0 0 4px rgba(244, 67, 54, 0.5)' : 'none',
90
90
  '&:hover': {
@@ -101,13 +101,13 @@ const WordBar = styled(Box, {
101
101
 
102
102
  const OriginalWordLabel = styled(Typography)({
103
103
  fontSize: '0.65rem',
104
- color: '#888',
104
+ color: '#888888', // slate-400 for dark mode
105
105
  lineHeight: 1.1,
106
106
  marginBottom: '3px',
107
107
  textDecoration: 'line-through',
108
108
  opacity: 0.85,
109
109
  fontWeight: 500,
110
- backgroundColor: 'rgba(255, 255, 255, 0.8)',
110
+ backgroundColor: 'rgba(15, 23, 42, 0.8)', // slate-900 with opacity for dark mode
111
111
  padding: '1px 3px',
112
112
  borderRadius: '2px'
113
113
  })
@@ -226,7 +226,7 @@ export default function DurationTimelineView({
226
226
  whiteSpace: 'nowrap',
227
227
  width: '100%',
228
228
  textAlign: 'center',
229
- color: correction ? '#1b5e20' : 'inherit'
229
+ color: correction ? '#4ade80' : '#e5e5e5' // green-400 or slate-50 for dark mode
230
230
  }}
231
231
  >
232
232
  {word.text}
@@ -235,7 +235,7 @@ export default function DurationTimelineView({
235
235
  <Typography
236
236
  sx={{
237
237
  fontSize: '0.6rem',
238
- color: 'rgba(0,0,0,0.6)',
238
+ color: 'rgba(248, 250, 252, 0.6)', // slate-50 with opacity for dark mode
239
239
  lineHeight: 1,
240
240
  marginTop: '3px',
241
241
  fontWeight: 600
@@ -624,7 +624,7 @@ export default function EditModal({
624
624
  position: 'absolute',
625
625
  top: 0,
626
626
  left: 0,
627
- backgroundColor: 'rgba(255, 255, 255, 0.9)',
627
+ backgroundColor: 'rgba(30, 41, 59, 0.95)', // slate-800 with opacity for dark mode
628
628
  zIndex: 10
629
629
  }}>
630
630
  <CircularProgress size={60} thickness={4} />
@@ -312,7 +312,7 @@ export default function EditWordList({
312
312
  width: '8px',
313
313
  },
314
314
  '&::-webkit-scrollbar-thumb': {
315
- backgroundColor: 'rgba(0,0,0,0.2)',
315
+ backgroundColor: 'rgba(248, 250, 252, 0.2)', // slate-50 for dark mode
316
316
  borderRadius: '4px',
317
317
  },
318
318
  scrollbarWidth: 'thin',
@@ -1,4 +1,4 @@
1
- import { Box, Button, Typography, useMediaQuery, useTheme, Switch, FormControlLabel, Tooltip, Paper, IconButton, Chip } from '@mui/material'
1
+ import { Box, Button, Typography, useMediaQuery, useTheme, Switch, FormControlLabel, Tooltip, Paper, IconButton } 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'
@@ -129,50 +129,19 @@ export default function Header({
129
129
  </Box>
130
130
  )}
131
131
 
132
- <Box sx={{
133
- display: 'flex',
134
- flexDirection: isMobile ? 'column' : 'row',
135
- gap: 1,
136
- justifyContent: 'space-between',
137
- alignItems: isMobile ? 'stretch' : 'center',
138
- mb: 1
139
- }}>
140
- <Typography variant="h4" sx={{ fontSize: isMobile ? '1.3rem' : '1.5rem' }}>
141
- Nomad Karaoke: Lyrics Transcription Review
142
- </Typography>
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
- )}
132
+ {isReadOnly && (
133
+ <Box sx={{ display: 'flex', justifyContent: 'flex-end', mb: 1 }}>
134
+ <Button
135
+ variant="outlined"
136
+ size="small"
137
+ startIcon={<UploadFileIcon />}
138
+ onClick={onFileLoad}
139
+ fullWidth={isMobile}
140
+ >
141
+ Load File
142
+ </Button>
174
143
  </Box>
175
- </Box>
144
+ )}
176
145
 
177
146
  <Box sx={{
178
147
  display: 'flex',
@@ -387,10 +356,10 @@ export default function Header({
387
356
  Timing Offset
388
357
  </Button>
389
358
  {timingOffsetMs !== 0 && (
390
- <Typography
391
- variant="body2"
392
- sx={{
393
- ml: 1,
359
+ <Typography
360
+ variant="body2"
361
+ sx={{
362
+ ml: 1,
394
363
  fontWeight: 'bold',
395
364
  color: theme.palette.secondary.main
396
365
  }}
@@ -400,6 +369,23 @@ export default function Header({
400
369
  )}
401
370
  </Box>
402
371
  )}
372
+ {!isReadOnly && onAnnotationsToggle && (
373
+ <Tooltip title={annotationsEnabled
374
+ ? "Click to disable annotation prompts when editing"
375
+ : "Click to enable annotation prompts when editing"
376
+ }>
377
+ <Button
378
+ variant="outlined"
379
+ size="small"
380
+ onClick={() => onAnnotationsToggle(!annotationsEnabled)}
381
+ startIcon={<RateReviewIcon />}
382
+ color={annotationsEnabled ? "primary" : "inherit"}
383
+ sx={{ minWidth: 'fit-content', height: '32px' }}
384
+ >
385
+ {annotationsEnabled ? "Feedback On" : "Feedback Off"}
386
+ </Button>
387
+ </Tooltip>
388
+ )}
403
389
  <AudioPlayer
404
390
  apiClient={apiClient}
405
391
  onTimeUpdate={onTimeUpdate}
@@ -33,16 +33,17 @@ const CANVAS_PADDING = 8
33
33
  const TEXT_ABOVE_BLOCK = 14
34
34
  const RESIZE_HANDLE_SIZE = 8
35
35
  const RESIZE_HANDLE_HITAREA = 12
36
- const PLAYHEAD_COLOR = '#ffffff'
37
- const WORD_BLOCK_COLOR = '#d32f2f'
38
- const WORD_BLOCK_SELECTED_COLOR = '#b71c1c'
39
- const WORD_BLOCK_CURRENT_COLOR = '#f44336'
40
- const WORD_TEXT_CURRENT_COLOR = '#d32f2f'
41
- const UPCOMING_WORD_BG = '#fff9c4'
42
- const UPCOMING_WORD_TEXT = '#000000'
43
- const TIME_BAR_BG = '#f5f5f5'
44
- const TIME_BAR_TEXT = '#666666'
45
- const TIMELINE_BG = '#e0e0e0'
36
+ // Dark theme colors matching karaoke-gen
37
+ const PLAYHEAD_COLOR = '#f97316' // orange-500 for better visibility
38
+ const WORD_BLOCK_COLOR = '#dc2626' // red-600 for dark mode
39
+ const WORD_BLOCK_SELECTED_COLOR = '#b91c1c' // red-700 for dark mode
40
+ const WORD_BLOCK_CURRENT_COLOR = '#ef4444' // red-500 for dark mode
41
+ const WORD_TEXT_CURRENT_COLOR = '#fca5a5' // red-300 for dark mode
42
+ const UPCOMING_WORD_BG = '#2a2a2a' // slate-700 for dark mode
43
+ const UPCOMING_WORD_TEXT = '#e5e5e5' // slate-50 for dark mode
44
+ const TIME_BAR_BG = '#1a1a1a' // slate-800 for dark mode
45
+ const TIME_BAR_TEXT = '#888888' // slate-400 for dark mode
46
+ const TIMELINE_BG = '#0f0f0f' // slate-900 for dark mode
46
47
 
47
48
  // Drag modes
48
49
  type DragMode = 'none' | 'selection' | 'resize' | 'move'
@@ -268,7 +269,7 @@ const TimelineCanvas = memo(function TimelineCanvas({
268
269
  const x = timeToX(t)
269
270
 
270
271
  ctx.beginPath()
271
- ctx.strokeStyle = '#999999'
272
+ ctx.strokeStyle = '#64748b' // slate-500 for dark mode
272
273
  ctx.lineWidth = 1
273
274
  ctx.moveTo(x, TIME_BAR_HEIGHT - 6)
274
275
  ctx.lineTo(x, TIME_BAR_HEIGHT)
@@ -280,7 +281,7 @@ const TimelineCanvas = memo(function TimelineCanvas({
280
281
  }
281
282
 
282
283
  ctx.beginPath()
283
- ctx.strokeStyle = '#cccccc'
284
+ ctx.strokeStyle = '#2a2a2a' // slate-700 for dark mode
284
285
  ctx.lineWidth = 1
285
286
  ctx.moveTo(0, TIME_BAR_HEIGHT)
286
287
  ctx.lineTo(canvasWidth, TIME_BAR_HEIGHT)
@@ -314,20 +315,20 @@ const TimelineCanvas = memo(function TimelineCanvas({
314
315
 
315
316
  // Draw selection border
316
317
  if (isSelected) {
317
- ctx.strokeStyle = '#ffffff'
318
+ ctx.strokeStyle = '#f97316' // orange-500 for dark mode selection
318
319
  ctx.lineWidth = 2
319
320
  ctx.strokeRect(bounds.startX, bounds.y, bounds.blockWidth, WORD_BLOCK_HEIGHT)
320
-
321
- // Draw resize handle (white dot on right edge) for selected words when hovered
321
+
322
+ // Draw resize handle (orange dot on right edge) for selected words when hovered
322
323
  if (isHovered || selectedWordIds.size === 1) {
323
324
  const handleX = bounds.startX + bounds.blockWidth - RESIZE_HANDLE_SIZE / 2
324
325
  const handleY = bounds.y + WORD_BLOCK_HEIGHT / 2
325
-
326
+
326
327
  ctx.beginPath()
327
- ctx.fillStyle = '#ffffff'
328
+ ctx.fillStyle = '#f97316' // orange-500 for dark mode
328
329
  ctx.arc(handleX, handleY, RESIZE_HANDLE_SIZE / 2, 0, Math.PI * 2)
329
330
  ctx.fill()
330
- ctx.strokeStyle = '#666666'
331
+ ctx.strokeStyle = '#0f0f0f' // slate-900 for dark mode
331
332
  ctx.lineWidth = 1
332
333
  ctx.stroke()
333
334
  }
@@ -368,7 +369,7 @@ const TimelineCanvas = memo(function TimelineCanvas({
368
369
 
369
370
  if (textStartX < canvasWidth - 10) {
370
371
  const isCurrent = word.id === currentWordId
371
- ctx.fillStyle = isCurrent ? WORD_TEXT_CURRENT_COLOR : '#333333'
372
+ ctx.fillStyle = isCurrent ? WORD_TEXT_CURRENT_COLOR : '#f8fafc' // slate-50 for dark mode
372
373
  ctx.fillText(word.text, textStartX, textY)
373
374
  rightmostTextEnd = textStartX + textWidth
374
375
  }
@@ -405,7 +406,7 @@ const TimelineCanvas = memo(function TimelineCanvas({
405
406
 
406
407
  ctx.beginPath()
407
408
  ctx.fillStyle = PLAYHEAD_COLOR
408
- ctx.strokeStyle = '#333333'
409
+ ctx.strokeStyle = '#0f0f0f' // slate-900 for dark mode
409
410
  ctx.lineWidth = 1
410
411
  ctx.moveTo(playheadX - 6, 2)
411
412
  ctx.lineTo(playheadX + 6, 2)
@@ -422,7 +423,7 @@ const TimelineCanvas = memo(function TimelineCanvas({
422
423
  ctx.stroke()
423
424
 
424
425
  ctx.beginPath()
425
- ctx.strokeStyle = 'rgba(0,0,0,0.4)'
426
+ ctx.strokeStyle = 'rgba(0,0,0,0.6)' // Darker shadow for dark mode visibility
426
427
  ctx.lineWidth = 1
427
428
  ctx.moveTo(playheadX + 1, TIME_BAR_HEIGHT)
428
429
  ctx.lineTo(playheadX + 1, height)
@@ -221,7 +221,7 @@ export default function ReferenceView({
221
221
  width: '100%',
222
222
  mb: 0,
223
223
  '&:hover': {
224
- backgroundColor: 'rgba(0, 0, 0, 0.03)'
224
+ backgroundColor: 'rgba(248, 250, 252, 0.04)' // slate-50 hover for dark mode
225
225
  }
226
226
  }}
227
227
  >
@@ -182,7 +182,7 @@ export default function TranscriptionView({
182
182
  width: '100%',
183
183
  mb: 0,
184
184
  '&:hover': {
185
- backgroundColor: 'rgba(0, 0, 0, 0.03)'
185
+ backgroundColor: 'rgba(248, 250, 252, 0.04)' // slate-50 hover for dark mode
186
186
  }
187
187
  }}>
188
188
  <SegmentControls>
@@ -18,7 +18,7 @@ interface WordDividerProps {
18
18
  }
19
19
 
20
20
  const buttonTextStyle = {
21
- color: 'rgba(0, 0, 0, 0.6)',
21
+ color: 'rgba(248, 250, 252, 0.8)', // slate-50 with opacity for dark mode
22
22
  fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
23
23
  fontWeight: 400,
24
24
  fontSize: '0.7rem',
@@ -58,7 +58,7 @@ export default function WordDivider({
58
58
  height: '20px',
59
59
  my: -0.5,
60
60
  width: '50%',
61
- backgroundColor: '#fff',
61
+ backgroundColor: '#1a1a1a', // slate-800 for dark mode
62
62
  ...sx
63
63
  }}
64
64
  >
@@ -66,7 +66,7 @@ export default function WordDivider({
66
66
  display: 'flex',
67
67
  alignItems: 'center',
68
68
  gap: 1,
69
- backgroundColor: '#fff',
69
+ backgroundColor: '#1a1a1a', // slate-800 for dark mode
70
70
  padding: '0 8px',
71
71
  zIndex: 1
72
72
  }}>
@@ -41,14 +41,14 @@ export const WordComponent = React.memo(function Word({
41
41
  borderRadius: '2px',
42
42
  color: isCurrentlyPlaying ? '#ffffff' : 'inherit',
43
43
  textDecoration: correction ? 'underline dotted' : 'none',
44
- textDecorationColor: correction ? '#666' : 'inherit',
44
+ textDecorationColor: correction ? '#666666' : 'inherit', // slate-500 for dark mode
45
45
  textUnderlineOffset: '2px',
46
46
  fontSize: '0.85rem',
47
47
  lineHeight: 1.2
48
48
  }}
49
49
  sx={{
50
50
  '&:hover': {
51
- backgroundColor: '#e0e0e0'
51
+ backgroundColor: 'rgba(248, 250, 252, 0.08)' // slate-50 hover for dark mode
52
52
  }
53
53
  }}
54
54
  onClick={onClick}
@@ -1,11 +1,21 @@
1
1
  import { keyframes } from '@mui/system'
2
2
 
3
+ // Dark theme colors matching karaoke-gen globals.css
3
4
  export const COLORS = {
4
- anchor: '#e3f2fd', // Pale blue
5
- corrected: '#e8f5e9', // Pale green
6
- uncorrectedGap: '#fff3e0', // Pale orange
7
- highlighted: '#ffeb3b', // or any color you prefer for highlighting
8
- playing: '#1976d2', // Blue
5
+ anchor: 'rgba(59, 130, 246, 0.25)', // Blue tint for dark mode
6
+ corrected: 'rgba(34, 197, 94, 0.25)', // Green tint for dark mode
7
+ uncorrectedGap: 'rgba(249, 115, 22, 0.25)', // Orange tint for dark mode
8
+ highlighted: 'rgba(251, 191, 36, 0.4)', // Amber highlight for dark mode
9
+ playing: '#3b82f6', // Blue-500
10
+ // Text colors (matching karaoke-gen)
11
+ textPrimary: '#e5e5e5', // matches karaoke-gen --text
12
+ textSecondary: '#888888', // matches karaoke-gen --text-muted
13
+ textMuted: '#666666',
14
+ // Background colors (matching karaoke-gen globals.css)
15
+ background: '#0f0f0f', // matches karaoke-gen --bg
16
+ backgroundPaper: '#1a1a1a', // matches karaoke-gen --card
17
+ backgroundElevated: '#252525', // matches karaoke-gen --secondary
18
+ border: '#2a2a2a', // matches karaoke-gen --card-border
9
19
  } as const
10
20
 
11
21
  export const flashAnimation = keyframes`
@@ -1,8 +1,5 @@
1
1
  import ReactDOM from 'react-dom/client'
2
- import { ThemeProvider } from '@mui/material/styles'
3
- import CssBaseline from '@mui/material/CssBaseline'
4
2
  import App from './App'
5
- import theme from './theme'
6
3
  // Import version from package.json
7
4
  import packageJson from '../package.json'
8
5
 
@@ -10,8 +7,5 @@ import packageJson from '../package.json'
10
7
  console.log(`🎵 Lyrics Transcriber Frontend v${packageJson.version}`)
11
8
 
12
9
  ReactDOM.createRoot(document.getElementById('root')!).render(
13
- <ThemeProvider theme={theme}>
14
- <CssBaseline />
15
- <App />
16
- </ThemeProvider>
10
+ <App />
17
11
  )