karaoke-gen 0.75.54__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.

Potentially problematic release.


This version of karaoke-gen might be problematic. Click here for more details.

Files changed (287) hide show
  1. karaoke_gen/__init__.py +38 -0
  2. karaoke_gen/audio_fetcher.py +1614 -0
  3. karaoke_gen/audio_processor.py +790 -0
  4. karaoke_gen/config.py +83 -0
  5. karaoke_gen/file_handler.py +387 -0
  6. karaoke_gen/instrumental_review/__init__.py +45 -0
  7. karaoke_gen/instrumental_review/analyzer.py +408 -0
  8. karaoke_gen/instrumental_review/editor.py +322 -0
  9. karaoke_gen/instrumental_review/models.py +171 -0
  10. karaoke_gen/instrumental_review/server.py +475 -0
  11. karaoke_gen/instrumental_review/static/index.html +1529 -0
  12. karaoke_gen/instrumental_review/waveform.py +409 -0
  13. karaoke_gen/karaoke_finalise/__init__.py +1 -0
  14. karaoke_gen/karaoke_finalise/karaoke_finalise.py +1833 -0
  15. karaoke_gen/karaoke_gen.py +1026 -0
  16. karaoke_gen/lyrics_processor.py +474 -0
  17. karaoke_gen/metadata.py +160 -0
  18. karaoke_gen/pipeline/__init__.py +87 -0
  19. karaoke_gen/pipeline/base.py +215 -0
  20. karaoke_gen/pipeline/context.py +230 -0
  21. karaoke_gen/pipeline/executors/__init__.py +21 -0
  22. karaoke_gen/pipeline/executors/local.py +159 -0
  23. karaoke_gen/pipeline/executors/remote.py +257 -0
  24. karaoke_gen/pipeline/stages/__init__.py +27 -0
  25. karaoke_gen/pipeline/stages/finalize.py +202 -0
  26. karaoke_gen/pipeline/stages/render.py +165 -0
  27. karaoke_gen/pipeline/stages/screens.py +139 -0
  28. karaoke_gen/pipeline/stages/separation.py +191 -0
  29. karaoke_gen/pipeline/stages/transcription.py +191 -0
  30. karaoke_gen/resources/AvenirNext-Bold.ttf +0 -0
  31. karaoke_gen/resources/Montserrat-Bold.ttf +0 -0
  32. karaoke_gen/resources/Oswald-Bold.ttf +0 -0
  33. karaoke_gen/resources/Oswald-SemiBold.ttf +0 -0
  34. karaoke_gen/resources/Zurich_Cn_BT_Bold.ttf +0 -0
  35. karaoke_gen/style_loader.py +531 -0
  36. karaoke_gen/utils/__init__.py +18 -0
  37. karaoke_gen/utils/bulk_cli.py +492 -0
  38. karaoke_gen/utils/cli_args.py +432 -0
  39. karaoke_gen/utils/gen_cli.py +978 -0
  40. karaoke_gen/utils/remote_cli.py +3268 -0
  41. karaoke_gen/video_background_processor.py +351 -0
  42. karaoke_gen/video_generator.py +424 -0
  43. karaoke_gen-0.75.54.dist-info/METADATA +718 -0
  44. karaoke_gen-0.75.54.dist-info/RECORD +287 -0
  45. karaoke_gen-0.75.54.dist-info/WHEEL +4 -0
  46. karaoke_gen-0.75.54.dist-info/entry_points.txt +5 -0
  47. karaoke_gen-0.75.54.dist-info/licenses/LICENSE +21 -0
  48. lyrics_transcriber/__init__.py +10 -0
  49. lyrics_transcriber/cli/__init__.py +0 -0
  50. lyrics_transcriber/cli/cli_main.py +285 -0
  51. lyrics_transcriber/core/__init__.py +0 -0
  52. lyrics_transcriber/core/config.py +50 -0
  53. lyrics_transcriber/core/controller.py +594 -0
  54. lyrics_transcriber/correction/__init__.py +0 -0
  55. lyrics_transcriber/correction/agentic/__init__.py +9 -0
  56. lyrics_transcriber/correction/agentic/adapter.py +71 -0
  57. lyrics_transcriber/correction/agentic/agent.py +313 -0
  58. lyrics_transcriber/correction/agentic/feedback/aggregator.py +12 -0
  59. lyrics_transcriber/correction/agentic/feedback/collector.py +17 -0
  60. lyrics_transcriber/correction/agentic/feedback/retention.py +24 -0
  61. lyrics_transcriber/correction/agentic/feedback/store.py +76 -0
  62. lyrics_transcriber/correction/agentic/handlers/__init__.py +24 -0
  63. lyrics_transcriber/correction/agentic/handlers/ambiguous.py +44 -0
  64. lyrics_transcriber/correction/agentic/handlers/background_vocals.py +68 -0
  65. lyrics_transcriber/correction/agentic/handlers/base.py +51 -0
  66. lyrics_transcriber/correction/agentic/handlers/complex_multi_error.py +46 -0
  67. lyrics_transcriber/correction/agentic/handlers/extra_words.py +74 -0
  68. lyrics_transcriber/correction/agentic/handlers/no_error.py +42 -0
  69. lyrics_transcriber/correction/agentic/handlers/punctuation.py +44 -0
  70. lyrics_transcriber/correction/agentic/handlers/registry.py +60 -0
  71. lyrics_transcriber/correction/agentic/handlers/repeated_section.py +44 -0
  72. lyrics_transcriber/correction/agentic/handlers/sound_alike.py +126 -0
  73. lyrics_transcriber/correction/agentic/models/__init__.py +5 -0
  74. lyrics_transcriber/correction/agentic/models/ai_correction.py +31 -0
  75. lyrics_transcriber/correction/agentic/models/correction_session.py +30 -0
  76. lyrics_transcriber/correction/agentic/models/enums.py +38 -0
  77. lyrics_transcriber/correction/agentic/models/human_feedback.py +30 -0
  78. lyrics_transcriber/correction/agentic/models/learning_data.py +26 -0
  79. lyrics_transcriber/correction/agentic/models/observability_metrics.py +28 -0
  80. lyrics_transcriber/correction/agentic/models/schemas.py +46 -0
  81. lyrics_transcriber/correction/agentic/models/utils.py +19 -0
  82. lyrics_transcriber/correction/agentic/observability/__init__.py +5 -0
  83. lyrics_transcriber/correction/agentic/observability/langfuse_integration.py +35 -0
  84. lyrics_transcriber/correction/agentic/observability/metrics.py +46 -0
  85. lyrics_transcriber/correction/agentic/observability/performance.py +19 -0
  86. lyrics_transcriber/correction/agentic/prompts/__init__.py +2 -0
  87. lyrics_transcriber/correction/agentic/prompts/classifier.py +227 -0
  88. lyrics_transcriber/correction/agentic/providers/__init__.py +6 -0
  89. lyrics_transcriber/correction/agentic/providers/base.py +36 -0
  90. lyrics_transcriber/correction/agentic/providers/circuit_breaker.py +145 -0
  91. lyrics_transcriber/correction/agentic/providers/config.py +73 -0
  92. lyrics_transcriber/correction/agentic/providers/constants.py +24 -0
  93. lyrics_transcriber/correction/agentic/providers/health.py +28 -0
  94. lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +212 -0
  95. lyrics_transcriber/correction/agentic/providers/model_factory.py +209 -0
  96. lyrics_transcriber/correction/agentic/providers/response_cache.py +218 -0
  97. lyrics_transcriber/correction/agentic/providers/response_parser.py +111 -0
  98. lyrics_transcriber/correction/agentic/providers/retry_executor.py +127 -0
  99. lyrics_transcriber/correction/agentic/router.py +35 -0
  100. lyrics_transcriber/correction/agentic/workflows/__init__.py +5 -0
  101. lyrics_transcriber/correction/agentic/workflows/consensus_workflow.py +24 -0
  102. lyrics_transcriber/correction/agentic/workflows/correction_graph.py +59 -0
  103. lyrics_transcriber/correction/agentic/workflows/feedback_workflow.py +24 -0
  104. lyrics_transcriber/correction/anchor_sequence.py +919 -0
  105. lyrics_transcriber/correction/corrector.py +760 -0
  106. lyrics_transcriber/correction/feedback/__init__.py +2 -0
  107. lyrics_transcriber/correction/feedback/schemas.py +107 -0
  108. lyrics_transcriber/correction/feedback/store.py +236 -0
  109. lyrics_transcriber/correction/handlers/__init__.py +0 -0
  110. lyrics_transcriber/correction/handlers/base.py +52 -0
  111. lyrics_transcriber/correction/handlers/extend_anchor.py +149 -0
  112. lyrics_transcriber/correction/handlers/levenshtein.py +189 -0
  113. lyrics_transcriber/correction/handlers/llm.py +293 -0
  114. lyrics_transcriber/correction/handlers/llm_providers.py +60 -0
  115. lyrics_transcriber/correction/handlers/no_space_punct_match.py +154 -0
  116. lyrics_transcriber/correction/handlers/relaxed_word_count_match.py +85 -0
  117. lyrics_transcriber/correction/handlers/repeat.py +88 -0
  118. lyrics_transcriber/correction/handlers/sound_alike.py +259 -0
  119. lyrics_transcriber/correction/handlers/syllables_match.py +252 -0
  120. lyrics_transcriber/correction/handlers/word_count_match.py +80 -0
  121. lyrics_transcriber/correction/handlers/word_operations.py +187 -0
  122. lyrics_transcriber/correction/operations.py +352 -0
  123. lyrics_transcriber/correction/phrase_analyzer.py +435 -0
  124. lyrics_transcriber/correction/text_utils.py +30 -0
  125. lyrics_transcriber/frontend/.gitignore +23 -0
  126. lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +935 -0
  127. lyrics_transcriber/frontend/.yarnrc.yml +3 -0
  128. lyrics_transcriber/frontend/README.md +50 -0
  129. lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +210 -0
  130. lyrics_transcriber/frontend/__init__.py +25 -0
  131. lyrics_transcriber/frontend/eslint.config.js +28 -0
  132. lyrics_transcriber/frontend/index.html +18 -0
  133. lyrics_transcriber/frontend/package.json +42 -0
  134. lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
  135. lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
  136. lyrics_transcriber/frontend/public/apple-touch-icon.png +0 -0
  137. lyrics_transcriber/frontend/public/favicon-16x16.png +0 -0
  138. lyrics_transcriber/frontend/public/favicon-32x32.png +0 -0
  139. lyrics_transcriber/frontend/public/favicon.ico +0 -0
  140. lyrics_transcriber/frontend/public/nomad-karaoke-logo.png +0 -0
  141. lyrics_transcriber/frontend/src/App.tsx +214 -0
  142. lyrics_transcriber/frontend/src/api.ts +254 -0
  143. lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +77 -0
  144. lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +114 -0
  145. lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +204 -0
  146. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +180 -0
  147. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +167 -0
  148. lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +359 -0
  149. lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +281 -0
  150. lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +162 -0
  151. lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +257 -0
  152. lyrics_transcriber/frontend/src/components/EditActionBar.tsx +68 -0
  153. lyrics_transcriber/frontend/src/components/EditModal.tsx +702 -0
  154. lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +496 -0
  155. lyrics_transcriber/frontend/src/components/EditWordList.tsx +379 -0
  156. lyrics_transcriber/frontend/src/components/FileUpload.tsx +77 -0
  157. lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +467 -0
  158. lyrics_transcriber/frontend/src/components/Header.tsx +413 -0
  159. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +1387 -0
  160. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +185 -0
  161. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +704 -0
  162. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +80 -0
  163. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +905 -0
  164. lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +51 -0
  165. lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +127 -0
  166. lyrics_transcriber/frontend/src/components/ModeSelector.tsx +67 -0
  167. lyrics_transcriber/frontend/src/components/ModelSelector.tsx +23 -0
  168. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +144 -0
  169. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +268 -0
  170. lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +336 -0
  171. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +354 -0
  172. lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +64 -0
  173. lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +376 -0
  174. lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +131 -0
  175. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +256 -0
  176. lyrics_transcriber/frontend/src/components/WordDivider.tsx +187 -0
  177. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +379 -0
  178. lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +56 -0
  179. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +87 -0
  180. lyrics_transcriber/frontend/src/components/shared/constants.ts +20 -0
  181. lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +180 -0
  182. lyrics_transcriber/frontend/src/components/shared/styles.ts +13 -0
  183. lyrics_transcriber/frontend/src/components/shared/types.js +2 -0
  184. lyrics_transcriber/frontend/src/components/shared/types.ts +129 -0
  185. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +177 -0
  186. lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +78 -0
  187. lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +75 -0
  188. lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +360 -0
  189. lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +110 -0
  190. lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +22 -0
  191. lyrics_transcriber/frontend/src/hooks/useManualSync.ts +435 -0
  192. lyrics_transcriber/frontend/src/main.tsx +17 -0
  193. lyrics_transcriber/frontend/src/theme.ts +177 -0
  194. lyrics_transcriber/frontend/src/types/global.d.ts +9 -0
  195. lyrics_transcriber/frontend/src/types.js +2 -0
  196. lyrics_transcriber/frontend/src/types.ts +199 -0
  197. lyrics_transcriber/frontend/src/validation.ts +132 -0
  198. lyrics_transcriber/frontend/src/vite-env.d.ts +1 -0
  199. lyrics_transcriber/frontend/tsconfig.app.json +26 -0
  200. lyrics_transcriber/frontend/tsconfig.json +25 -0
  201. lyrics_transcriber/frontend/tsconfig.node.json +23 -0
  202. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -0
  203. lyrics_transcriber/frontend/update_version.js +11 -0
  204. lyrics_transcriber/frontend/vite.config.d.ts +2 -0
  205. lyrics_transcriber/frontend/vite.config.js +10 -0
  206. lyrics_transcriber/frontend/vite.config.ts +11 -0
  207. lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
  208. lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
  209. lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
  210. lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js +43288 -0
  211. lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js.map +1 -0
  212. lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
  213. lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
  214. lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
  215. lyrics_transcriber/frontend/web_assets/index.html +18 -0
  216. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
  217. lyrics_transcriber/frontend/yarn.lock +3752 -0
  218. lyrics_transcriber/lyrics/__init__.py +0 -0
  219. lyrics_transcriber/lyrics/base_lyrics_provider.py +211 -0
  220. lyrics_transcriber/lyrics/file_provider.py +95 -0
  221. lyrics_transcriber/lyrics/genius.py +384 -0
  222. lyrics_transcriber/lyrics/lrclib.py +231 -0
  223. lyrics_transcriber/lyrics/musixmatch.py +156 -0
  224. lyrics_transcriber/lyrics/spotify.py +290 -0
  225. lyrics_transcriber/lyrics/user_input_provider.py +44 -0
  226. lyrics_transcriber/output/__init__.py +0 -0
  227. lyrics_transcriber/output/ass/__init__.py +21 -0
  228. lyrics_transcriber/output/ass/ass.py +2088 -0
  229. lyrics_transcriber/output/ass/ass_specs.txt +732 -0
  230. lyrics_transcriber/output/ass/config.py +180 -0
  231. lyrics_transcriber/output/ass/constants.py +23 -0
  232. lyrics_transcriber/output/ass/event.py +94 -0
  233. lyrics_transcriber/output/ass/formatters.py +132 -0
  234. lyrics_transcriber/output/ass/lyrics_line.py +265 -0
  235. lyrics_transcriber/output/ass/lyrics_screen.py +252 -0
  236. lyrics_transcriber/output/ass/section_detector.py +89 -0
  237. lyrics_transcriber/output/ass/section_screen.py +106 -0
  238. lyrics_transcriber/output/ass/style.py +187 -0
  239. lyrics_transcriber/output/cdg.py +619 -0
  240. lyrics_transcriber/output/cdgmaker/__init__.py +0 -0
  241. lyrics_transcriber/output/cdgmaker/cdg.py +262 -0
  242. lyrics_transcriber/output/cdgmaker/composer.py +2260 -0
  243. lyrics_transcriber/output/cdgmaker/config.py +151 -0
  244. lyrics_transcriber/output/cdgmaker/images/instrumental.png +0 -0
  245. lyrics_transcriber/output/cdgmaker/images/intro.png +0 -0
  246. lyrics_transcriber/output/cdgmaker/pack.py +507 -0
  247. lyrics_transcriber/output/cdgmaker/render.py +346 -0
  248. lyrics_transcriber/output/cdgmaker/transitions/centertexttoplogobottomtext.png +0 -0
  249. lyrics_transcriber/output/cdgmaker/transitions/circlein.png +0 -0
  250. lyrics_transcriber/output/cdgmaker/transitions/circleout.png +0 -0
  251. lyrics_transcriber/output/cdgmaker/transitions/fizzle.png +0 -0
  252. lyrics_transcriber/output/cdgmaker/transitions/largecentertexttoplogo.png +0 -0
  253. lyrics_transcriber/output/cdgmaker/transitions/rectangle.png +0 -0
  254. lyrics_transcriber/output/cdgmaker/transitions/spiral.png +0 -0
  255. lyrics_transcriber/output/cdgmaker/transitions/topleftmusicalnotes.png +0 -0
  256. lyrics_transcriber/output/cdgmaker/transitions/wipein.png +0 -0
  257. lyrics_transcriber/output/cdgmaker/transitions/wipeleft.png +0 -0
  258. lyrics_transcriber/output/cdgmaker/transitions/wipeout.png +0 -0
  259. lyrics_transcriber/output/cdgmaker/transitions/wiperight.png +0 -0
  260. lyrics_transcriber/output/cdgmaker/utils.py +132 -0
  261. lyrics_transcriber/output/countdown_processor.py +306 -0
  262. lyrics_transcriber/output/fonts/AvenirNext-Bold.ttf +0 -0
  263. lyrics_transcriber/output/fonts/DMSans-VariableFont_opsz,wght.ttf +0 -0
  264. lyrics_transcriber/output/fonts/DMSerifDisplay-Regular.ttf +0 -0
  265. lyrics_transcriber/output/fonts/Oswald-SemiBold.ttf +0 -0
  266. lyrics_transcriber/output/fonts/Zurich_Cn_BT_Bold.ttf +0 -0
  267. lyrics_transcriber/output/fonts/arial.ttf +0 -0
  268. lyrics_transcriber/output/fonts/georgia.ttf +0 -0
  269. lyrics_transcriber/output/fonts/verdana.ttf +0 -0
  270. lyrics_transcriber/output/generator.py +257 -0
  271. lyrics_transcriber/output/lrc_to_cdg.py +61 -0
  272. lyrics_transcriber/output/lyrics_file.py +102 -0
  273. lyrics_transcriber/output/plain_text.py +96 -0
  274. lyrics_transcriber/output/segment_resizer.py +431 -0
  275. lyrics_transcriber/output/subtitles.py +397 -0
  276. lyrics_transcriber/output/video.py +544 -0
  277. lyrics_transcriber/review/__init__.py +0 -0
  278. lyrics_transcriber/review/server.py +676 -0
  279. lyrics_transcriber/storage/__init__.py +0 -0
  280. lyrics_transcriber/storage/dropbox.py +225 -0
  281. lyrics_transcriber/transcribers/__init__.py +0 -0
  282. lyrics_transcriber/transcribers/audioshake.py +379 -0
  283. lyrics_transcriber/transcribers/base_transcriber.py +157 -0
  284. lyrics_transcriber/transcribers/whisper.py +330 -0
  285. lyrics_transcriber/types.py +650 -0
  286. lyrics_transcriber/utils/__init__.py +0 -0
  287. lyrics_transcriber/utils/word_utils.py +27 -0
@@ -0,0 +1,379 @@
1
+ import { Typography, Box } from '@mui/material'
2
+ import { WordComponent } from './Word'
3
+ import { useWordClick } from '../hooks/useWordClick'
4
+ import {
5
+ AnchorSequence,
6
+ GapSequence,
7
+ HighlightInfo,
8
+ InteractionMode,
9
+ LyricsSegment,
10
+ Word,
11
+ WordCorrection
12
+ } from '../../../types'
13
+ import { ModalContent } from '../../LyricsAnalyzer'
14
+ import type { FlashType, LinePosition, TranscriptionWordPosition, WordClickInfo } from '../types'
15
+ import React from 'react'
16
+ import ContentCopyIcon from '@mui/icons-material/ContentCopy'
17
+ import IconButton from '@mui/material/IconButton'
18
+ import { getWordsFromIds } from '../utils/wordUtils'
19
+
20
+ export interface HighlightedTextProps {
21
+ text?: string
22
+ segments?: LyricsSegment[]
23
+ wordPositions: TranscriptionWordPosition[]
24
+ anchors: AnchorSequence[]
25
+ highlightInfo: HighlightInfo | null
26
+ mode: InteractionMode
27
+ onElementClick: (content: ModalContent) => void
28
+ onWordClick?: (info: WordClickInfo) => void
29
+ flashingType: FlashType
30
+ isReference?: boolean
31
+ currentSource?: string
32
+ preserveSegments?: boolean
33
+ linePositions?: LinePosition[]
34
+ currentTime?: number
35
+ referenceCorrections?: Map<string, string>
36
+ gaps?: GapSequence[]
37
+ flashingHandler?: string | null
38
+ corrections?: WordCorrection[]
39
+ }
40
+
41
+ export function HighlightedText({
42
+ text,
43
+ segments,
44
+ wordPositions = [] as TranscriptionWordPosition[],
45
+ anchors,
46
+ highlightInfo,
47
+ mode,
48
+ onElementClick,
49
+ onWordClick,
50
+ flashingType,
51
+ isReference,
52
+ currentSource = '',
53
+ preserveSegments = false,
54
+ linePositions = [],
55
+ currentTime = 0,
56
+ referenceCorrections = new Map(),
57
+ gaps = [],
58
+ flashingHandler,
59
+ corrections = [],
60
+ }: HighlightedTextProps) {
61
+ const { handleWordClick } = useWordClick({
62
+ mode,
63
+ onElementClick,
64
+ onWordClick,
65
+ isReference,
66
+ currentSource,
67
+ gaps,
68
+ anchors,
69
+ corrections
70
+ })
71
+
72
+ const shouldWordFlash = (wordPos: TranscriptionWordPosition | { word: string; id: string }): boolean => {
73
+ if (!flashingType) {
74
+ return false;
75
+ }
76
+
77
+ if ('type' in wordPos) {
78
+ // Add handler-specific flashing
79
+ if (flashingType === 'handler' && flashingHandler) {
80
+ console.log('Checking handler flash for word:', wordPos.word.text);
81
+ console.log('Current flashingHandler:', flashingHandler);
82
+ console.log('Word ID:', wordPos.word.id);
83
+
84
+ const shouldFlash = corrections.some(correction =>
85
+ correction.handler === flashingHandler &&
86
+ (correction.corrected_word_id === wordPos.word.id ||
87
+ correction.word_id === wordPos.word.id)
88
+ );
89
+
90
+ console.log('Should flash:', shouldFlash);
91
+ return shouldFlash;
92
+ }
93
+
94
+ const gap = wordPos.sequence as GapSequence
95
+ const isCorrected = (
96
+ // Check corrections array for this word
97
+ corrections.some(correction =>
98
+ (correction.word_id === wordPos.word.id ||
99
+ correction.corrected_word_id === wordPos.word.id) &&
100
+ gap.transcribed_word_ids.includes(correction.word_id)
101
+ ) ||
102
+ // Also check if marked as corrected in wordPos
103
+ wordPos.isCorrected
104
+ )
105
+
106
+ return Boolean(
107
+ (flashingType === 'anchor' && wordPos.type === 'anchor') ||
108
+ (flashingType === 'corrected' && isCorrected) ||
109
+ (flashingType === 'uncorrected' && wordPos.type === 'gap' && !isCorrected) ||
110
+ (flashingType === 'word' && (
111
+ // For anchors
112
+ (highlightInfo?.type === 'anchor' && wordPos.type === 'anchor' &&
113
+ (isReference && currentSource && highlightInfo.sequence
114
+ ? getWordsFromIds(segments || [],
115
+ (highlightInfo.sequence as AnchorSequence).reference_word_ids[currentSource] || []
116
+ ).some(w => w.id === wordPos.word.id)
117
+ : getWordsFromIds(segments || [],
118
+ (highlightInfo.sequence as AnchorSequence).transcribed_word_ids
119
+ ).some(w => w.id === wordPos.word.id)
120
+ )) ||
121
+ // For gaps
122
+ (highlightInfo?.type === 'gap' && wordPos.type === 'gap' &&
123
+ (isReference && currentSource && highlightInfo.sequence
124
+ ? getWordsFromIds(segments || [],
125
+ (highlightInfo.sequence as GapSequence).reference_word_ids[currentSource] || []
126
+ ).some(w => w.id === wordPos.word.id)
127
+ : getWordsFromIds(segments || [],
128
+ (highlightInfo.sequence as GapSequence).transcribed_word_ids
129
+ ).some(w => w.id === wordPos.word.id))
130
+ ) ||
131
+ // For corrections
132
+ (highlightInfo?.type === 'correction' && isReference && currentSource &&
133
+ highlightInfo.correction?.reference_positions?.[currentSource]?.toString() === wordPos.word.id)
134
+ ))
135
+ )
136
+ }
137
+ return false
138
+ }
139
+
140
+ const shouldHighlightWord = (wordPos: TranscriptionWordPosition | { word: string; id: string }): boolean => {
141
+ // Don't highlight words in reference view
142
+ if (isReference) return false
143
+
144
+ if ('type' in wordPos && currentTime !== undefined && 'start_time' in wordPos.word) {
145
+ const word = wordPos.word as Word
146
+ return word.start_time !== null &&
147
+ word.end_time !== null &&
148
+ currentTime >= word.start_time &&
149
+ currentTime <= word.end_time
150
+ }
151
+ return false
152
+ }
153
+
154
+ const handleCopyLine = (text: string) => {
155
+ navigator.clipboard.writeText(text);
156
+ };
157
+
158
+ const renderContent = () => {
159
+ if (wordPositions && !segments) {
160
+ return wordPositions.map((wordPos, index) => (
161
+ <React.Fragment key={wordPos.word.id}>
162
+ <WordComponent
163
+ key={`${wordPos.word.id}-${index}`}
164
+ word={wordPos.word.text}
165
+ shouldFlash={shouldWordFlash(wordPos)}
166
+ isAnchor={wordPos.type === 'anchor'}
167
+ isCorrectedGap={wordPos.isCorrected}
168
+ isUncorrectedGap={wordPos.type === 'gap' && !wordPos.isCorrected}
169
+ isCurrentlyPlaying={shouldHighlightWord(wordPos)}
170
+ onClick={() => handleWordClick(
171
+ wordPos.word.text,
172
+ wordPos.word.id,
173
+ wordPos.type === 'anchor' ? wordPos.sequence as AnchorSequence : undefined,
174
+ wordPos.type === 'gap' ? wordPos.sequence as GapSequence : undefined
175
+ )}
176
+ correction={(() => {
177
+ const correction = corrections?.find(c =>
178
+ c.corrected_word_id === wordPos.word.id ||
179
+ c.word_id === wordPos.word.id
180
+ );
181
+ return correction ? {
182
+ originalWord: correction.original_word,
183
+ handler: correction.handler,
184
+ confidence: correction.confidence,
185
+ source: correction.source,
186
+ reason: correction.reason
187
+ } : null;
188
+ })()}
189
+ />
190
+ {index < wordPositions.length - 1 && ' '}
191
+ </React.Fragment>
192
+ ))
193
+ } else if (segments) {
194
+ return segments.map((segment) => (
195
+ <Box key={segment.id} sx={{
196
+ display: 'flex',
197
+ alignItems: 'flex-start',
198
+ mb: 0
199
+ }}>
200
+ <Box sx={{ flex: 1 }}>
201
+ {segment.words.map((word, wordIndex) => {
202
+ const wordPos = wordPositions.find((pos: TranscriptionWordPosition) =>
203
+ pos.word.id === word.id
204
+ );
205
+
206
+ const anchor = wordPos?.type === 'anchor' ? anchors?.find(a =>
207
+ (a.reference_word_ids[currentSource] || []).includes(word.id)
208
+ ) : undefined;
209
+
210
+ const hasCorrection = referenceCorrections.has(word.id);
211
+ const isUncorrectedGap = wordPos?.type === 'gap' && !hasCorrection;
212
+
213
+ const sequence = wordPos?.type === 'gap' ? wordPos.sequence as GapSequence : undefined;
214
+
215
+ // Find correction information for the tooltip
216
+ const correction = corrections?.find(c =>
217
+ c.corrected_word_id === word.id ||
218
+ c.word_id === word.id
219
+ );
220
+
221
+ const correctionInfo = correction ? {
222
+ originalWord: correction.original_word,
223
+ handler: correction.handler,
224
+ confidence: correction.confidence,
225
+ source: correction.source,
226
+ reason: correction.reason
227
+ } : null;
228
+
229
+ return (
230
+ <React.Fragment key={word.id}>
231
+ <WordComponent
232
+ word={word.text}
233
+ shouldFlash={shouldWordFlash(wordPos || { word: word.text, id: word.id })}
234
+ isAnchor={Boolean(anchor)}
235
+ isCorrectedGap={hasCorrection}
236
+ isUncorrectedGap={isUncorrectedGap}
237
+ isCurrentlyPlaying={shouldHighlightWord(wordPos || { word: word.text, id: word.id })}
238
+ onClick={() => handleWordClick(word.text, word.id, anchor, sequence)}
239
+ correction={correctionInfo}
240
+ />
241
+ {wordIndex < segment.words.length - 1 && ' '}
242
+ </React.Fragment>
243
+ );
244
+ })}
245
+ </Box>
246
+ </Box>
247
+ ));
248
+ } else if (text) {
249
+ const lines = text.split('\n')
250
+ let wordCount = 0
251
+
252
+ return lines.map((line, lineIndex) => {
253
+ const currentLinePosition = linePositions?.find(pos => pos.position === wordCount)
254
+ if (currentLinePosition?.isEmpty) {
255
+ wordCount++
256
+ return (
257
+ <Box key={`empty-${lineIndex}`} sx={{
258
+ display: 'flex',
259
+ alignItems: 'flex-start',
260
+ mb: 0,
261
+ lineHeight: 1
262
+ }}>
263
+ <Typography
264
+ component="span"
265
+ sx={{
266
+ color: 'text.secondary',
267
+ width: '2em',
268
+ minWidth: '2em',
269
+ textAlign: 'right',
270
+ marginRight: 1,
271
+ userSelect: 'none',
272
+ fontFamily: 'monospace',
273
+ paddingTop: '1px',
274
+ fontSize: '0.8rem',
275
+ lineHeight: 1
276
+ }}
277
+ >
278
+ {currentLinePosition.lineNumber}
279
+ </Typography>
280
+ <Box sx={{ width: '18px' }} />
281
+ <Box sx={{ flex: 1, height: '1em' }} />
282
+ </Box>
283
+ )
284
+ }
285
+
286
+ const words = line.split(' ')
287
+ const lineWords: React.ReactNode[] = []
288
+
289
+ words.forEach((word, wordIndex) => {
290
+ if (word === '') return null
291
+ if (/^\s+$/.test(word)) {
292
+ return lineWords.push(<span key={`space-${lineIndex}-${wordIndex}`}> </span>)
293
+ }
294
+
295
+ const wordId = `${currentSource}-word-${wordCount}`
296
+ wordCount++
297
+
298
+ const anchor = currentSource ? anchors?.find(a =>
299
+ a.reference_word_ids[currentSource]?.includes(wordId)
300
+ ) : undefined
301
+
302
+ const hasCorrection = referenceCorrections.has(wordId)
303
+
304
+ lineWords.push(
305
+ <WordComponent
306
+ key={wordId}
307
+ word={word}
308
+ shouldFlash={shouldWordFlash({ word, id: wordId })}
309
+ isAnchor={Boolean(anchor)}
310
+ isCorrectedGap={hasCorrection}
311
+ isUncorrectedGap={false}
312
+ isCurrentlyPlaying={shouldHighlightWord({ word, id: wordId })}
313
+ onClick={() => handleWordClick(word, wordId, anchor, undefined)}
314
+ />
315
+ )
316
+ })
317
+
318
+ return (
319
+ <Box key={`line-${lineIndex}`} sx={{
320
+ display: 'flex',
321
+ alignItems: 'flex-start',
322
+ mb: 0,
323
+ lineHeight: 1
324
+ }}>
325
+ <Typography
326
+ component="span"
327
+ sx={{
328
+ color: 'text.secondary',
329
+ width: '2em',
330
+ minWidth: '2em',
331
+ textAlign: 'right',
332
+ marginRight: 1,
333
+ userSelect: 'none',
334
+ fontFamily: 'monospace',
335
+ paddingTop: '1px',
336
+ fontSize: '0.8rem',
337
+ lineHeight: 1
338
+ }}
339
+ >
340
+ {currentLinePosition?.lineNumber ?? lineIndex}
341
+ </Typography>
342
+ <IconButton
343
+ size="small"
344
+ onClick={() => handleCopyLine(line)}
345
+ sx={{
346
+ padding: '1px',
347
+ marginRight: 0.5,
348
+ height: '18px',
349
+ width: '18px',
350
+ minHeight: '18px',
351
+ minWidth: '18px'
352
+ }}
353
+ >
354
+ <ContentCopyIcon sx={{ fontSize: '0.9rem' }} />
355
+ </IconButton>
356
+ <Box sx={{ flex: 1 }}>
357
+ {lineWords}
358
+ </Box>
359
+ </Box>
360
+ )
361
+ })
362
+ }
363
+ return null
364
+ }
365
+
366
+ return (
367
+ <Typography
368
+ component="div"
369
+ sx={{
370
+ fontFamily: 'monospace',
371
+ whiteSpace: preserveSegments ? 'normal' : 'pre-wrap',
372
+ margin: 0,
373
+ lineHeight: 1.5
374
+ }}
375
+ >
376
+ {renderContent()}
377
+ </Typography>
378
+ )
379
+ }
@@ -0,0 +1,56 @@
1
+ import { Box, Button } from '@mui/material'
2
+ import AddIcon from '@mui/icons-material/Add'
3
+
4
+ export interface SourceSelectorProps {
5
+ currentSource: string
6
+ onSourceChange: (source: string) => void
7
+ availableSources: string[]
8
+ onAddLyrics?: () => void
9
+ }
10
+
11
+ export function SourceSelector({ currentSource, onSourceChange, availableSources, onAddLyrics }: SourceSelectorProps) {
12
+ return (
13
+ <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.3, alignItems: 'center' }}>
14
+ {availableSources.map((source) => (
15
+ <Button
16
+ key={source}
17
+ size="small"
18
+ variant={currentSource === source ? 'contained' : 'outlined'}
19
+ onClick={() => onSourceChange(source)}
20
+ sx={{
21
+ mr: 0,
22
+ py: 0.2,
23
+ px: 0.8,
24
+ minWidth: 'auto',
25
+ fontSize: '0.7rem',
26
+ lineHeight: 1.2
27
+ }}
28
+ >
29
+ {/* Capitalize first letter of source */}
30
+ {source.charAt(0).toUpperCase() + source.slice(1)}
31
+ </Button>
32
+ ))}
33
+ {onAddLyrics && (
34
+ <Button
35
+ size="small"
36
+ variant="outlined"
37
+ onClick={onAddLyrics}
38
+ startIcon={<AddIcon sx={{ fontSize: '0.9rem' }} />}
39
+ sx={{
40
+ mr: 0,
41
+ py: 0.2,
42
+ px: 0.8,
43
+ minWidth: 'auto',
44
+ fontSize: '0.7rem',
45
+ lineHeight: 1.2,
46
+ '& .MuiButton-startIcon': {
47
+ marginLeft: '-5px',
48
+ marginRight: '1px',
49
+ marginTop: '-1px'
50
+ }
51
+ }}
52
+ >New</Button>
53
+ )}
54
+ </Box>
55
+ )
56
+ }
@@ -0,0 +1,87 @@
1
+ import React from 'react'
2
+ import { COLORS } from '../constants'
3
+ import { HighlightedWord } from '../styles'
4
+ import { WordProps } from '../types'
5
+ import { Tooltip } from '@mui/material'
6
+
7
+ export const WordComponent = React.memo(function Word({
8
+ word,
9
+ shouldFlash,
10
+ isAnchor,
11
+ isCorrectedGap,
12
+ isUncorrectedGap,
13
+ isCurrentlyPlaying,
14
+ padding = '1px 3px',
15
+ onClick,
16
+ correction
17
+ }: WordProps) {
18
+ if (/^\s+$/.test(word)) {
19
+ return word
20
+ }
21
+
22
+ const backgroundColor = isCurrentlyPlaying
23
+ ? COLORS.playing
24
+ : shouldFlash
25
+ ? COLORS.highlighted
26
+ : isAnchor
27
+ ? COLORS.anchor
28
+ : isCorrectedGap
29
+ ? COLORS.corrected
30
+ : isUncorrectedGap
31
+ ? COLORS.uncorrectedGap
32
+ : 'transparent'
33
+
34
+ const wordElement = (
35
+ <HighlightedWord
36
+ shouldFlash={shouldFlash}
37
+ style={{
38
+ backgroundColor,
39
+ padding,
40
+ cursor: 'pointer',
41
+ borderRadius: '2px',
42
+ color: isCurrentlyPlaying ? '#ffffff' : 'inherit',
43
+ textDecoration: correction ? 'underline dotted' : 'none',
44
+ textDecorationColor: correction ? '#666' : 'inherit',
45
+ textUnderlineOffset: '2px',
46
+ fontSize: '0.85rem',
47
+ lineHeight: 1.2
48
+ }}
49
+ sx={{
50
+ '&:hover': {
51
+ backgroundColor: '#e0e0e0'
52
+ }
53
+ }}
54
+ onClick={onClick}
55
+ >
56
+ {word}
57
+ </HighlightedWord>
58
+ )
59
+
60
+ if (correction) {
61
+ const tooltipContent = (
62
+ <>
63
+ <strong>Original:</strong> "{correction.originalWord}"<br />
64
+ <strong>Corrected by:</strong> {correction.handler}<br />
65
+ <strong>Source:</strong> {correction.source}<br />
66
+ {correction.reason && (
67
+ <>
68
+ <strong>Reason:</strong> {correction.reason}<br />
69
+ </>
70
+ )}
71
+ {correction.confidence !== undefined && correction.confidence > 0 && (
72
+ <>
73
+ <strong>Confidence:</strong> {(correction.confidence * 100).toFixed(0)}%<br />
74
+ </>
75
+ )}
76
+ </>
77
+ )
78
+
79
+ return (
80
+ <Tooltip title={tooltipContent} arrow placement="top">
81
+ {wordElement}
82
+ </Tooltip>
83
+ )
84
+ }
85
+
86
+ return wordElement
87
+ })
@@ -0,0 +1,20 @@
1
+ import { keyframes } from '@mui/system'
2
+
3
+ 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
9
+ } as const
10
+
11
+ export const flashAnimation = keyframes`
12
+ 0%, 100% {
13
+ opacity: 1;
14
+ background-color: inherit;
15
+ }
16
+ 50% {
17
+ opacity: 0.6;
18
+ background-color: ${COLORS.highlighted};
19
+ }
20
+ `