karaoke-gen 0.105.4__py3-none-any.whl → 0.107.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.
Files changed (322) hide show
  1. backend/api/routes/users.py +14 -3
  2. backend/config.py +3 -0
  3. backend/services/encoding_interface.py +4 -0
  4. backend/services/job_notification_service.py +4 -21
  5. backend/tests/test_job_notification_service.py +24 -58
  6. backend/tests/test_video_worker_orchestrator.py +189 -0
  7. backend/workers/video_worker_orchestrator.py +7 -0
  8. karaoke_gen/instrumental_review/server.py +145 -35
  9. karaoke_gen/nextjs_frontend/__init__.py +98 -0
  10. karaoke_gen/nextjs_frontend/out/404/index.html +1 -0
  11. karaoke_gen/nextjs_frontend/out/404.html +1 -0
  12. karaoke_gen/nextjs_frontend/out/__next.__PAGE__.txt +9 -0
  13. karaoke_gen/nextjs_frontend/out/__next._full.txt +22 -0
  14. karaoke_gen/nextjs_frontend/out/__next._head.txt +8 -0
  15. karaoke_gen/nextjs_frontend/out/__next._index.txt +9 -0
  16. karaoke_gen/nextjs_frontend/out/__next._tree.txt +2 -0
  17. karaoke_gen/nextjs_frontend/out/_next/static/chunks/01a7f8fe40f1ff47.js +1 -0
  18. karaoke_gen/nextjs_frontend/out/_next/static/chunks/112f346e31f991df.js +4 -0
  19. karaoke_gen/nextjs_frontend/out/_next/static/chunks/16d1a4dd9d8a873a.js +3 -0
  20. karaoke_gen/nextjs_frontend/out/_next/static/chunks/1ab85c362b8b0e86.js +9 -0
  21. karaoke_gen/nextjs_frontend/out/_next/static/chunks/247eb132b7f7b574.js +1 -0
  22. karaoke_gen/nextjs_frontend/out/_next/static/chunks/2b80d15cc95e4818.js +1 -0
  23. karaoke_gen/nextjs_frontend/out/_next/static/chunks/32c7eba5cd46c1bc.js +7 -0
  24. karaoke_gen/nextjs_frontend/out/_next/static/chunks/483f26794eae53d0.js +1 -0
  25. karaoke_gen/nextjs_frontend/out/_next/static/chunks/550c3b02e85f196a.js +1 -0
  26. karaoke_gen/nextjs_frontend/out/_next/static/chunks/55c5ade44387bef8.js +1 -0
  27. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5628d92b5893add2.css +1 -0
  28. karaoke_gen/nextjs_frontend/out/_next/static/chunks/56ebf7665e4341c8.js +7 -0
  29. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5997132b61dec430.js +1 -0
  30. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5ea55255bce3eb9e.js +5 -0
  31. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5eda89a57490b3cd.js +1 -0
  32. karaoke_gen/nextjs_frontend/out/_next/static/chunks/692f5d9e0d700c76.js +3 -0
  33. karaoke_gen/nextjs_frontend/out/_next/static/chunks/71d7a05b14f9f0f4.js +1 -0
  34. karaoke_gen/nextjs_frontend/out/_next/static/chunks/81ac355749ef3302.js +1 -0
  35. karaoke_gen/nextjs_frontend/out/_next/static/chunks/95f7e5934dbb0e5d.js +1 -0
  36. karaoke_gen/nextjs_frontend/out/_next/static/chunks/9bce8f19eaa46940.js +1 -0
  37. karaoke_gen/nextjs_frontend/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  38. karaoke_gen/nextjs_frontend/out/_next/static/chunks/a9ed54eed3e14c92.js +2 -0
  39. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b35cd41238ecfb17.js +1 -0
  40. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5bc3c3d5ebd49eb.js +1 -0
  41. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5c078c08db5ae32.js +5 -0
  42. karaoke_gen/nextjs_frontend/out/_next/static/chunks/be9c44a178104187.js +1 -0
  43. karaoke_gen/nextjs_frontend/out/_next/static/chunks/c4c840e18cb4861c.js +1 -0
  44. karaoke_gen/nextjs_frontend/out/_next/static/chunks/c645af7d6b65f73e.js +1 -0
  45. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d2c5e2575df784d4.js +1 -0
  46. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d30af02b96d81462.js +1 -0
  47. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d9bdf64f4ec1e9b7.js +7 -0
  48. karaoke_gen/nextjs_frontend/out/_next/static/chunks/dcde6ed684dacd0e.js +5 -0
  49. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e422cbe931246000.js +1 -0
  50. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e483af34fc792d38.js +1 -0
  51. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e57422aad6b897da.js +1 -0
  52. karaoke_gen/nextjs_frontend/out/_next/static/chunks/ef02697fb404726a.js +1 -0
  53. karaoke_gen/nextjs_frontend/out/_next/static/chunks/ff1a16fafef87110.js +1 -0
  54. karaoke_gen/nextjs_frontend/out/_next/static/chunks/turbopack-2d9ca3017a9deedf.js +3 -0
  55. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_buildManifest.js +11 -0
  56. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_clientMiddlewareManifest.json +1 -0
  57. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_ssgManifest.js +1 -0
  58. karaoke_gen/nextjs_frontend/out/_not-found/__next._full.txt +18 -0
  59. karaoke_gen/nextjs_frontend/out/_not-found/__next._head.txt +8 -0
  60. karaoke_gen/nextjs_frontend/out/_not-found/__next._index.txt +9 -0
  61. karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.__PAGE__.txt +5 -0
  62. karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.txt +4 -0
  63. karaoke_gen/nextjs_frontend/out/_not-found/__next._tree.txt +2 -0
  64. karaoke_gen/nextjs_frontend/out/_not-found/index.html +1 -0
  65. karaoke_gen/nextjs_frontend/out/_not-found/index.txt +18 -0
  66. karaoke_gen/nextjs_frontend/out/admin/__next._full.txt +25 -0
  67. karaoke_gen/nextjs_frontend/out/admin/__next._head.txt +8 -0
  68. karaoke_gen/nextjs_frontend/out/admin/__next._index.txt +9 -0
  69. karaoke_gen/nextjs_frontend/out/admin/__next._tree.txt +2 -0
  70. karaoke_gen/nextjs_frontend/out/admin/__next.admin.__PAGE__.txt +9 -0
  71. karaoke_gen/nextjs_frontend/out/admin/__next.admin.txt +7 -0
  72. karaoke_gen/nextjs_frontend/out/admin/beta/__next._full.txt +25 -0
  73. karaoke_gen/nextjs_frontend/out/admin/beta/__next._head.txt +8 -0
  74. karaoke_gen/nextjs_frontend/out/admin/beta/__next._index.txt +9 -0
  75. karaoke_gen/nextjs_frontend/out/admin/beta/__next._tree.txt +2 -0
  76. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.__PAGE__.txt +9 -0
  77. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.txt +4 -0
  78. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.txt +7 -0
  79. karaoke_gen/nextjs_frontend/out/admin/beta/index.html +1 -0
  80. karaoke_gen/nextjs_frontend/out/admin/beta/index.txt +25 -0
  81. karaoke_gen/nextjs_frontend/out/admin/index.html +1 -0
  82. karaoke_gen/nextjs_frontend/out/admin/index.txt +25 -0
  83. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._full.txt +25 -0
  84. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._head.txt +8 -0
  85. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._index.txt +9 -0
  86. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._tree.txt +2 -0
  87. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.__PAGE__.txt +9 -0
  88. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.txt +4 -0
  89. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.txt +7 -0
  90. karaoke_gen/nextjs_frontend/out/admin/jobs/index.html +1 -0
  91. karaoke_gen/nextjs_frontend/out/admin/jobs/index.txt +25 -0
  92. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._full.txt +25 -0
  93. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._head.txt +8 -0
  94. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._index.txt +9 -0
  95. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._tree.txt +2 -0
  96. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.__PAGE__.txt +9 -0
  97. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.txt +4 -0
  98. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.txt +7 -0
  99. karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.html +1 -0
  100. karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.txt +25 -0
  101. karaoke_gen/nextjs_frontend/out/admin/searches/__next._full.txt +25 -0
  102. karaoke_gen/nextjs_frontend/out/admin/searches/__next._head.txt +8 -0
  103. karaoke_gen/nextjs_frontend/out/admin/searches/__next._index.txt +9 -0
  104. karaoke_gen/nextjs_frontend/out/admin/searches/__next._tree.txt +2 -0
  105. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.__PAGE__.txt +9 -0
  106. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.txt +4 -0
  107. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.txt +7 -0
  108. karaoke_gen/nextjs_frontend/out/admin/searches/index.html +1 -0
  109. karaoke_gen/nextjs_frontend/out/admin/searches/index.txt +25 -0
  110. karaoke_gen/nextjs_frontend/out/admin/users/__next._full.txt +25 -0
  111. karaoke_gen/nextjs_frontend/out/admin/users/__next._head.txt +8 -0
  112. karaoke_gen/nextjs_frontend/out/admin/users/__next._index.txt +9 -0
  113. karaoke_gen/nextjs_frontend/out/admin/users/__next._tree.txt +2 -0
  114. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.txt +7 -0
  115. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.__PAGE__.txt +9 -0
  116. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.txt +4 -0
  117. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._full.txt +25 -0
  118. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._head.txt +8 -0
  119. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._index.txt +9 -0
  120. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._tree.txt +2 -0
  121. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.txt +7 -0
  122. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.__PAGE__.txt +9 -0
  123. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.txt +4 -0
  124. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.txt +4 -0
  125. karaoke_gen/nextjs_frontend/out/admin/users/detail/index.html +1 -0
  126. karaoke_gen/nextjs_frontend/out/admin/users/detail/index.txt +25 -0
  127. karaoke_gen/nextjs_frontend/out/admin/users/index.html +1 -0
  128. karaoke_gen/nextjs_frontend/out/admin/users/index.txt +25 -0
  129. karaoke_gen/nextjs_frontend/out/app/__next._full.txt +22 -0
  130. karaoke_gen/nextjs_frontend/out/app/__next._head.txt +8 -0
  131. karaoke_gen/nextjs_frontend/out/app/__next._index.txt +9 -0
  132. karaoke_gen/nextjs_frontend/out/app/__next._tree.txt +2 -0
  133. karaoke_gen/nextjs_frontend/out/app/__next.app.__PAGE__.txt +9 -0
  134. karaoke_gen/nextjs_frontend/out/app/__next.app.txt +4 -0
  135. karaoke_gen/nextjs_frontend/out/app/index.html +1 -0
  136. karaoke_gen/nextjs_frontend/out/app/index.txt +22 -0
  137. karaoke_gen/nextjs_frontend/out/app/jobs/__next._full.txt +19 -0
  138. karaoke_gen/nextjs_frontend/out/app/jobs/__next._head.txt +8 -0
  139. karaoke_gen/nextjs_frontend/out/app/jobs/__next._index.txt +9 -0
  140. karaoke_gen/nextjs_frontend/out/app/jobs/__next._tree.txt +2 -0
  141. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  142. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.txt +4 -0
  143. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.txt +4 -0
  144. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.txt +4 -0
  145. karaoke_gen/nextjs_frontend/out/app/jobs/index.html +1 -0
  146. karaoke_gen/nextjs_frontend/out/app/jobs/index.txt +19 -0
  147. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._full.txt +19 -0
  148. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._head.txt +8 -0
  149. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._index.txt +9 -0
  150. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._tree.txt +2 -0
  151. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  152. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.txt +4 -0
  153. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.txt +4 -0
  154. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.txt +4 -0
  155. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.html +1 -0
  156. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.txt +19 -0
  157. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._full.txt +19 -0
  158. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._head.txt +8 -0
  159. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._index.txt +9 -0
  160. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._tree.txt +2 -0
  161. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  162. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.txt +4 -0
  163. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.txt +4 -0
  164. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.txt +4 -0
  165. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.html +1 -0
  166. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.txt +19 -0
  167. karaoke_gen/nextjs_frontend/out/auth/verify/__next._full.txt +22 -0
  168. karaoke_gen/nextjs_frontend/out/auth/verify/__next._head.txt +8 -0
  169. karaoke_gen/nextjs_frontend/out/auth/verify/__next._index.txt +9 -0
  170. karaoke_gen/nextjs_frontend/out/auth/verify/__next._tree.txt +2 -0
  171. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.txt +4 -0
  172. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.__PAGE__.txt +9 -0
  173. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.txt +4 -0
  174. karaoke_gen/nextjs_frontend/out/auth/verify/index.html +1 -0
  175. karaoke_gen/nextjs_frontend/out/auth/verify/index.txt +22 -0
  176. karaoke_gen/nextjs_frontend/out/index.html +1 -0
  177. karaoke_gen/nextjs_frontend/out/index.txt +22 -0
  178. karaoke_gen/nextjs_frontend/out/manifest.webmanifest +31 -0
  179. karaoke_gen/nextjs_frontend/out/order/success/__next._full.txt +22 -0
  180. karaoke_gen/nextjs_frontend/out/order/success/__next._head.txt +8 -0
  181. karaoke_gen/nextjs_frontend/out/order/success/__next._index.txt +9 -0
  182. karaoke_gen/nextjs_frontend/out/order/success/__next._tree.txt +2 -0
  183. karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.__PAGE__.txt +9 -0
  184. karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.txt +4 -0
  185. karaoke_gen/nextjs_frontend/out/order/success/__next.order.txt +4 -0
  186. karaoke_gen/nextjs_frontend/out/order/success/index.html +1 -0
  187. karaoke_gen/nextjs_frontend/out/order/success/index.txt +22 -0
  188. karaoke_gen/nextjs_frontend/out/payment/success/__next._full.txt +22 -0
  189. karaoke_gen/nextjs_frontend/out/payment/success/__next._head.txt +8 -0
  190. karaoke_gen/nextjs_frontend/out/payment/success/__next._index.txt +9 -0
  191. karaoke_gen/nextjs_frontend/out/payment/success/__next._tree.txt +2 -0
  192. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.__PAGE__.txt +9 -0
  193. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.txt +4 -0
  194. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.txt +4 -0
  195. karaoke_gen/nextjs_frontend/out/payment/success/index.html +1 -0
  196. karaoke_gen/nextjs_frontend/out/payment/success/index.txt +22 -0
  197. karaoke_gen/nextjs_frontend/out/screenshots/email-action_reminder.png +0 -0
  198. karaoke_gen/nextjs_frontend/out/screenshots/email-beta_welcome.png +0 -0
  199. karaoke_gen/nextjs_frontend/out/screenshots/email-job_completion.png +0 -0
  200. karaoke_gen/nextjs_frontend/out/screenshots/example-output.avif +0 -0
  201. karaoke_gen/nextjs_frontend/out/screenshots/homepage-full.png +0 -0
  202. karaoke_gen/nextjs_frontend/out/screenshots/homepage-hero.png +0 -0
  203. karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.avif +0 -0
  204. karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.png +0 -0
  205. karaoke_gen/nextjs_frontend/out/screenshots/job-dashboard.avif +0 -0
  206. karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.avif +0 -0
  207. karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.png +0 -0
  208. karaoke_gen/nextjs_frontend/out/sw.js +183 -0
  209. karaoke_gen/utils/cli_args.py +3 -3
  210. karaoke_gen/utils/gen_cli.py +4 -0
  211. karaoke_gen/utils/remote_cli.py +8 -40
  212. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/METADATA +1 -1
  213. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/RECORD +227 -121
  214. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/WHEEL +1 -1
  215. lyrics_transcriber/correction/agentic/agent.py +83 -60
  216. lyrics_transcriber/correction/anchor_sequence.py +48 -3
  217. lyrics_transcriber/correction/corrector.py +92 -58
  218. lyrics_transcriber/review/server.py +165 -33
  219. lyrics_transcriber/utils/tracing.py +214 -0
  220. karaoke_gen/instrumental_review/static/index.html +0 -1721
  221. lyrics_transcriber/frontend/.gitignore +0 -24
  222. lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +0 -935
  223. lyrics_transcriber/frontend/.yarnrc.yml +0 -3
  224. lyrics_transcriber/frontend/README.md +0 -50
  225. lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +0 -210
  226. lyrics_transcriber/frontend/__init__.py +0 -25
  227. lyrics_transcriber/frontend/e2e/agentic-corrections.spec.ts +0 -207
  228. lyrics_transcriber/frontend/e2e/fixtures/agentic-correction-data.json +0 -226
  229. lyrics_transcriber/frontend/eslint.config.js +0 -28
  230. lyrics_transcriber/frontend/index.html +0 -22
  231. lyrics_transcriber/frontend/package-lock.json +0 -4553
  232. lyrics_transcriber/frontend/package.json +0 -48
  233. lyrics_transcriber/frontend/playwright.config.ts +0 -69
  234. lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
  235. lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
  236. lyrics_transcriber/frontend/src/App.tsx +0 -243
  237. lyrics_transcriber/frontend/src/api.ts +0 -262
  238. lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +0 -111
  239. lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +0 -114
  240. lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +0 -204
  241. lyrics_transcriber/frontend/src/components/AppHeader.tsx +0 -65
  242. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +0 -180
  243. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +0 -175
  244. lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +0 -359
  245. lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +0 -281
  246. lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +0 -162
  247. lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +0 -257
  248. lyrics_transcriber/frontend/src/components/EditActionBar.tsx +0 -94
  249. lyrics_transcriber/frontend/src/components/EditModal.tsx +0 -720
  250. lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +0 -592
  251. lyrics_transcriber/frontend/src/components/EditWordList.tsx +0 -431
  252. lyrics_transcriber/frontend/src/components/FileUpload.tsx +0 -77
  253. lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +0 -467
  254. lyrics_transcriber/frontend/src/components/Header.tsx +0 -520
  255. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +0 -1526
  256. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +0 -216
  257. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +0 -721
  258. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +0 -80
  259. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +0 -999
  260. lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +0 -51
  261. lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +0 -127
  262. lyrics_transcriber/frontend/src/components/ModeSelector.tsx +0 -67
  263. lyrics_transcriber/frontend/src/components/ModelSelector.tsx +0 -23
  264. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +0 -177
  265. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +0 -268
  266. lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +0 -336
  267. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +0 -354
  268. lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +0 -64
  269. lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +0 -383
  270. lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +0 -131
  271. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +0 -266
  272. lyrics_transcriber/frontend/src/components/WordDivider.tsx +0 -191
  273. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +0 -466
  274. lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +0 -56
  275. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +0 -89
  276. lyrics_transcriber/frontend/src/components/shared/constants.ts +0 -30
  277. lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +0 -180
  278. lyrics_transcriber/frontend/src/components/shared/styles.ts +0 -13
  279. lyrics_transcriber/frontend/src/components/shared/types.js +0 -2
  280. lyrics_transcriber/frontend/src/components/shared/types.ts +0 -135
  281. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +0 -177
  282. lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +0 -78
  283. lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +0 -75
  284. lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +0 -360
  285. lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +0 -110
  286. lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +0 -22
  287. lyrics_transcriber/frontend/src/hooks/useManualSync.ts +0 -537
  288. lyrics_transcriber/frontend/src/main.tsx +0 -11
  289. lyrics_transcriber/frontend/src/theme.ts +0 -406
  290. lyrics_transcriber/frontend/src/types/global.d.ts +0 -9
  291. lyrics_transcriber/frontend/src/types.js +0 -2
  292. lyrics_transcriber/frontend/src/types.ts +0 -199
  293. lyrics_transcriber/frontend/src/validation.ts +0 -132
  294. lyrics_transcriber/frontend/src/vite-env.d.ts +0 -1
  295. lyrics_transcriber/frontend/tsconfig.app.json +0 -26
  296. lyrics_transcriber/frontend/tsconfig.json +0 -25
  297. lyrics_transcriber/frontend/tsconfig.node.json +0 -23
  298. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +0 -1
  299. lyrics_transcriber/frontend/update_version.js +0 -11
  300. lyrics_transcriber/frontend/vite.config.d.ts +0 -2
  301. lyrics_transcriber/frontend/vite.config.js +0 -15
  302. lyrics_transcriber/frontend/vite.config.ts +0 -16
  303. lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
  304. lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
  305. lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
  306. lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js +0 -44465
  307. lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js.map +0 -1
  308. lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
  309. lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
  310. lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
  311. lyrics_transcriber/frontend/web_assets/index.html +0 -22
  312. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
  313. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.svg +0 -5
  314. lyrics_transcriber/frontend/yarn.lock +0 -3711
  315. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/apple-touch-icon.png +0 -0
  316. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-16x16.png +0 -0
  317. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-32x32.png +0 -0
  318. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon.ico +0 -0
  319. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/nomad-karaoke-logo.svg +0 -0
  320. /lyrics_transcriber/frontend/public/nomad-karaoke-logo.png → /karaoke_gen/nextjs_frontend/out/nomad-logo.png +0 -0
  321. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/entry_points.txt +0 -0
  322. {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,111 +0,0 @@
1
- import React, { useEffect, useRef } from "react";
2
-
3
- type Props = {
4
- isOpen: boolean;
5
- onClose: () => void;
6
- onSubmit: (payload: { reviewerAction: string; finalText?: string; reasonCategory: string; reasonDetail?: string }) => void;
7
- suggestion?: { text: string; reasoning?: string; confidence?: number };
8
- };
9
-
10
- export const AIFeedbackModal: React.FC<Props> = ({ isOpen, onClose, onSubmit, suggestion }) => {
11
- const [reviewerAction, setAction] = React.useState("ACCEPT");
12
- const [finalText, setFinalText] = React.useState("");
13
- const [reasonCategory, setReason] = React.useState("AI_CORRECT");
14
- const [reasonDetail, setDetail] = React.useState("");
15
- const modalRef = useRef<HTMLDivElement>(null);
16
-
17
- // Handle Escape key to close modal
18
- useEffect(() => {
19
- const handleKeyDown = (e: KeyboardEvent) => {
20
- if (e.key === 'Escape' && isOpen) {
21
- onClose();
22
- }
23
- };
24
- document.addEventListener('keydown', handleKeyDown);
25
- return () => document.removeEventListener('keydown', handleKeyDown);
26
- }, [isOpen, onClose]);
27
-
28
- // Focus modal on open
29
- useEffect(() => {
30
- if (isOpen && modalRef.current) {
31
- modalRef.current.focus();
32
- }
33
- }, [isOpen]);
34
-
35
- if (!isOpen) return null;
36
-
37
- // Dark theme colors matching karaoke-gen
38
- const colors = {
39
- background: '#1a1a1a', // slate-800
40
- text: '#f8fafc', // slate-50
41
- textSecondary: '#888888', // slate-400
42
- border: '#2a2a2a', // slate-700
43
- inputBg: '#0f0f0f', // slate-900
44
- };
45
-
46
- return (
47
- <div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.7)", display: "flex", alignItems: "center", justifyContent: "center", zIndex: 1300 }}>
48
- <div
49
- ref={modalRef}
50
- role="dialog"
51
- aria-modal="true"
52
- aria-labelledby="ai-feedback-title"
53
- tabIndex={-1}
54
- style={{ background: colors.background, padding: 16, width: 480, borderRadius: 8, border: `1px solid ${colors.border}`, color: colors.text, outline: 'none' }}
55
- >
56
- <h3 id="ai-feedback-title" style={{ color: colors.text, margin: 0 }}>AI Suggestion</h3>
57
- <p style={{ marginTop: 8, color: colors.text }}>
58
- {suggestion?.text ?? "No suggestion"}
59
- {suggestion?.confidence != null ? ` (confidence ${Math.round((suggestion.confidence || 0) * 100)}%)` : null}
60
- </p>
61
- {suggestion?.reasoning ? <small style={{ color: colors.textSecondary }}>{suggestion.reasoning}</small> : null}
62
-
63
- <div style={{ marginTop: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
64
- <label htmlFor="ai-action-select" style={{ color: colors.text }}>Action</label>
65
- <select id="ai-action-select" value={reviewerAction} onChange={(e) => setAction(e.target.value)} style={{ background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px' }}>
66
- <option value="ACCEPT">Accept</option>
67
- <option value="REJECT">Reject</option>
68
- <option value="MODIFY">Modify</option>
69
- </select>
70
- </div>
71
-
72
- {reviewerAction === "MODIFY" ? (
73
- <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 4 }}>
74
- <label htmlFor="ai-final-text" style={{ color: colors.text }}>Final Text</label>
75
- <input id="ai-final-text" value={finalText} onChange={(e) => setFinalText(e.target.value)} style={{ width: "100%", background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px', boxSizing: 'border-box' }} />
76
- </div>
77
- ) : null}
78
-
79
- <div style={{ marginTop: 12, display: 'flex', alignItems: 'center', gap: 8 }}>
80
- <label htmlFor="ai-reason-select" style={{ color: colors.text }}>Reason</label>
81
- <select id="ai-reason-select" value={reasonCategory} onChange={(e) => setReason(e.target.value)} style={{ background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px' }}>
82
- <option value="AI_CORRECT">AI_CORRECT</option>
83
- <option value="AI_INCORRECT">AI_INCORRECT</option>
84
- <option value="AI_SUBOPTIMAL">AI_SUBOPTIMAL</option>
85
- <option value="CONTEXT_NEEDED">CONTEXT_NEEDED</option>
86
- <option value="SUBJECTIVE_PREFERENCE">SUBJECTIVE_PREFERENCE</option>
87
- </select>
88
- </div>
89
-
90
- <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 4 }}>
91
- <label htmlFor="ai-details-textarea" style={{ color: colors.text }}>Details</label>
92
- <textarea id="ai-details-textarea" value={reasonDetail} onChange={(e) => setDetail(e.target.value)} style={{ width: "100%", background: colors.inputBg, color: colors.text, border: `1px solid ${colors.border}`, borderRadius: 4, padding: '4px 8px', boxSizing: 'border-box' }} />
93
- </div>
94
-
95
- <div style={{ display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }}>
96
- <button onClick={onClose} style={{ background: colors.border, color: colors.text, border: 'none', borderRadius: 4, padding: '6px 12px', cursor: 'pointer' }}>Cancel</button>
97
- <button
98
- onClick={() =>
99
- onSubmit({ reviewerAction, finalText: finalText || undefined, reasonCategory, reasonDetail: reasonDetail || undefined })
100
- }
101
- style={{ background: '#ff7acc', color: '#fff', border: 'none', borderRadius: 4, padding: '6px 12px', cursor: 'pointer' }}
102
- >
103
- Submit
104
- </button>
105
- </div>
106
- </div>
107
- </div>
108
- );
109
- };
110
-
111
- export default AIFeedbackModal;
@@ -1,114 +0,0 @@
1
- import { useState } from 'react'
2
- import {
3
- Dialog,
4
- DialogTitle,
5
- DialogContent,
6
- DialogActions,
7
- Button,
8
- TextField,
9
- Box,
10
- CircularProgress,
11
- Typography
12
- } from '@mui/material'
13
-
14
- interface AddLyricsModalProps {
15
- open: boolean
16
- onClose: () => void
17
- onSubmit: (source: string, lyrics: string) => Promise<void>
18
- isSubmitting: boolean
19
- }
20
-
21
- export default function AddLyricsModal({
22
- open,
23
- onClose,
24
- onSubmit,
25
- isSubmitting
26
- }: AddLyricsModalProps) {
27
- const [source, setSource] = useState('')
28
- const [lyrics, setLyrics] = useState('')
29
- const [error, setError] = useState<string | null>(null)
30
-
31
- const handleSubmit = async () => {
32
- if (!source.trim()) {
33
- setError('Please enter a source name')
34
- return
35
- }
36
- if (!lyrics.trim()) {
37
- setError('Please enter lyrics text')
38
- return
39
- }
40
-
41
- try {
42
- await onSubmit(source.trim(), lyrics.trim())
43
- // Reset form on success
44
- setSource('')
45
- setLyrics('')
46
- setError(null)
47
- onClose()
48
- } catch (err) {
49
- setError(err instanceof Error ? err.message : 'Failed to add lyrics')
50
- }
51
- }
52
-
53
- const handleClose = () => {
54
- // Don't allow closing if currently submitting
55
- if (isSubmitting) return
56
-
57
- setSource('')
58
- setLyrics('')
59
- setError(null)
60
- onClose()
61
- }
62
-
63
- return (
64
- <Dialog
65
- open={open}
66
- onClose={handleClose}
67
- maxWidth="md"
68
- fullWidth
69
- disableEscapeKeyDown={isSubmitting}
70
- >
71
- <DialogTitle>Add Reference Lyrics</DialogTitle>
72
- <DialogContent>
73
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mt: 1 }}>
74
- {error && (
75
- <Typography color="error" variant="body2">
76
- {error}
77
- </Typography>
78
- )}
79
- <TextField
80
- label="Source Name"
81
- value={source}
82
- onChange={(e) => setSource(e.target.value)}
83
- disabled={isSubmitting}
84
- fullWidth
85
- placeholder="e.g., Official Lyrics, Album Booklet"
86
- />
87
- <TextField
88
- label="Lyrics"
89
- value={lyrics}
90
- onChange={(e) => setLyrics(e.target.value)}
91
- disabled={isSubmitting}
92
- fullWidth
93
- multiline
94
- rows={10}
95
- placeholder="Paste lyrics text here (one line per segment)"
96
- />
97
- </Box>
98
- </DialogContent>
99
- <DialogActions>
100
- <Button onClick={handleClose} disabled={isSubmitting}>
101
- Cancel
102
- </Button>
103
- <Button
104
- onClick={handleSubmit}
105
- variant="contained"
106
- disabled={isSubmitting}
107
- startIcon={isSubmitting ? <CircularProgress size={20} /> : undefined}
108
- >
109
- {isSubmitting ? 'Adding...' : 'Add Lyrics'}
110
- </Button>
111
- </DialogActions>
112
- </Dialog>
113
- )
114
- }
@@ -1,204 +0,0 @@
1
- import { Paper, Box, Typography, Chip, Tooltip } from '@mui/material'
2
- import { WordCorrection } from '../types'
3
- import { useMemo } from 'react'
4
-
5
- interface GapCategoryMetric {
6
- category: string
7
- count: number
8
- avgConfidence: number
9
- corrections: WordCorrection[]
10
- }
11
-
12
- interface AgenticCorrectionMetricsProps {
13
- corrections: WordCorrection[]
14
- onCategoryClick?: (category: string) => void
15
- onConfidenceFilterClick?: (filter: 'low' | 'high') => void
16
- }
17
-
18
- export default function AgenticCorrectionMetrics({
19
- corrections,
20
- onCategoryClick,
21
- onConfidenceFilterClick
22
- }: AgenticCorrectionMetricsProps) {
23
-
24
- const metrics = useMemo(() => {
25
- // Filter only agentic corrections
26
- const agenticCorrections = corrections.filter(c => c.handler === 'AgenticCorrector')
27
-
28
- // Parse category from reason string (format: "reason [CATEGORY] (confidence: XX%)")
29
- const categoryMap = new Map<string, GapCategoryMetric>()
30
-
31
- agenticCorrections.forEach(correction => {
32
- const categoryMatch = correction.reason?.match(/\[([A-Z_]+)\]/)
33
- const category = categoryMatch ? categoryMatch[1] : 'UNKNOWN'
34
-
35
- if (!categoryMap.has(category)) {
36
- categoryMap.set(category, {
37
- category,
38
- count: 0,
39
- avgConfidence: 0,
40
- corrections: []
41
- })
42
- }
43
-
44
- const metric = categoryMap.get(category)!
45
- metric.count++
46
- metric.corrections.push(correction)
47
- })
48
-
49
- // Calculate average confidence for each category
50
- categoryMap.forEach((metric) => {
51
- const totalConfidence = metric.corrections.reduce((sum, c) => sum + c.confidence, 0)
52
- metric.avgConfidence = totalConfidence / metric.count
53
- })
54
-
55
- // Convert to array and sort by count descending
56
- const sortedMetrics = Array.from(categoryMap.values()).sort((a, b) => b.count - a.count)
57
-
58
- // Calculate overall stats
59
- const totalCorrections = agenticCorrections.length
60
- const avgConfidence = totalCorrections > 0
61
- ? agenticCorrections.reduce((sum, c) => sum + c.confidence, 0) / totalCorrections
62
- : 0
63
-
64
- const lowConfidenceCount = agenticCorrections.filter(c => c.confidence < 0.6).length
65
- const highConfidenceCount = agenticCorrections.filter(c => c.confidence >= 0.8).length
66
-
67
- return {
68
- categories: sortedMetrics,
69
- totalCorrections,
70
- avgConfidence,
71
- lowConfidenceCount,
72
- highConfidenceCount
73
- }
74
- }, [corrections])
75
-
76
- // Format category name for display
77
- const formatCategory = (category: string): string => {
78
- return category
79
- .split('_')
80
- .map(word => word.charAt(0) + word.slice(1).toLowerCase())
81
- .join(' ')
82
- }
83
-
84
- // Get emoji/icon for category
85
- const getCategoryIcon = (category: string): string => {
86
- const icons: Record<string, string> = {
87
- 'SOUND_ALIKE': '🎵',
88
- 'PUNCTUATION_ONLY': '✏️',
89
- 'BACKGROUND_VOCALS': '🎤',
90
- 'EXTRA_WORDS': '➕',
91
- 'REPEATED_SECTION': '🔁',
92
- 'COMPLEX_MULTI_ERROR': '🔧',
93
- 'AMBIGUOUS': '❓',
94
- 'NO_ERROR': '✅'
95
- }
96
- return icons[category] || '📝'
97
- }
98
-
99
- return (
100
- <Paper
101
- sx={{
102
- p: 0.8,
103
- height: '100%',
104
- display: 'flex',
105
- flexDirection: 'column'
106
- }}
107
- >
108
- <Typography variant="subtitle2" color="text.secondary" sx={{ mb: 0.5, fontSize: '0.7rem' }}>
109
- Agentic AI Corrections
110
- </Typography>
111
-
112
- {/* Overall stats */}
113
- <Box sx={{ mb: 1 }}>
114
- <Typography variant="body2" sx={{ fontSize: '0.75rem', mb: 0.3 }}>
115
- Total: <strong>{metrics.totalCorrections}</strong>
116
- </Typography>
117
- <Typography variant="body2" sx={{ fontSize: '0.75rem', mb: 0.5 }}>
118
- Avg Confidence: <strong>{(metrics.avgConfidence * 100).toFixed(0)}%</strong>
119
- </Typography>
120
-
121
- {/* Quick filters */}
122
- <Box sx={{ display: 'flex', gap: 0.5, flexWrap: 'wrap' }}>
123
- <Chip
124
- label={`Low (<60%): ${metrics.lowConfidenceCount}`}
125
- size="small"
126
- variant="outlined"
127
- color="warning"
128
- onClick={() => onConfidenceFilterClick?.('low')}
129
- sx={{ fontSize: '0.65rem', height: '20px', cursor: 'pointer' }}
130
- />
131
- <Chip
132
- label={`High (≥80%): ${metrics.highConfidenceCount}`}
133
- size="small"
134
- variant="outlined"
135
- color="success"
136
- onClick={() => onConfidenceFilterClick?.('high')}
137
- sx={{ fontSize: '0.65rem', height: '20px', cursor: 'pointer' }}
138
- />
139
- </Box>
140
- </Box>
141
-
142
- {/* Category breakdown */}
143
- <Typography variant="subtitle2" color="text.secondary" sx={{ mb: 0.5, fontSize: '0.7rem' }}>
144
- By Category
145
- </Typography>
146
- <Box sx={{ flex: 1, overflow: 'auto' }}>
147
- {metrics.categories.map((metric) => (
148
- <Box
149
- key={metric.category}
150
- sx={{
151
- mb: 0.5,
152
- p: 0.5,
153
- borderRadius: 1,
154
- cursor: 'pointer',
155
- '&:hover': {
156
- bgcolor: 'action.hover'
157
- }
158
- }}
159
- onClick={() => onCategoryClick?.(metric.category)}
160
- >
161
- <Tooltip
162
- title={`${metric.count} correction${metric.count !== 1 ? 's' : ''} • Avg confidence: ${(metric.avgConfidence * 100).toFixed(0)}%`}
163
- placement="right"
164
- >
165
- <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
166
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
167
- <span style={{ fontSize: '0.85rem' }}>{getCategoryIcon(metric.category)}</span>
168
- <Typography variant="body2" sx={{ fontSize: '0.7rem' }}>
169
- {formatCategory(metric.category)}
170
- </Typography>
171
- </Box>
172
- <Box sx={{ display: 'flex', gap: 0.5, alignItems: 'center' }}>
173
- <Typography variant="body2" sx={{ fontSize: '0.65rem', color: 'text.secondary' }}>
174
- {(metric.avgConfidence * 100).toFixed(0)}%
175
- </Typography>
176
- <Typography
177
- variant="body2"
178
- sx={{
179
- fontSize: '0.7rem',
180
- fontWeight: 600,
181
- bgcolor: 'action.selected',
182
- px: 0.5,
183
- borderRadius: 0.5,
184
- minWidth: '24px',
185
- textAlign: 'center'
186
- }}
187
- >
188
- {metric.count}
189
- </Typography>
190
- </Box>
191
- </Box>
192
- </Tooltip>
193
- </Box>
194
- ))}
195
- {metrics.categories.length === 0 && (
196
- <Typography variant="body2" color="text.secondary" sx={{ fontSize: '0.7rem', fontStyle: 'italic' }}>
197
- No agentic corrections
198
- </Typography>
199
- )}
200
- </Box>
201
- </Paper>
202
- )
203
- }
204
-
@@ -1,65 +0,0 @@
1
- import { Box, IconButton, Tooltip, Typography, useTheme } from '@mui/material'
2
- import { Sun, Moon } from 'lucide-react'
3
-
4
- interface AppHeaderProps {
5
- isDarkMode?: boolean
6
- onToggleTheme?: () => void
7
- }
8
-
9
- export default function AppHeader({ isDarkMode = true, onToggleTheme }: AppHeaderProps) {
10
- const theme = useTheme()
11
-
12
- return (
13
- <Box
14
- component="header"
15
- sx={{
16
- borderBottom: `1px solid ${theme.palette.divider}`,
17
- backgroundColor: theme.palette.background.paper,
18
- backdropFilter: 'blur(8px)',
19
- position: 'sticky',
20
- top: 0,
21
- zIndex: 1100,
22
- px: 2,
23
- py: 1.5,
24
- display: 'flex',
25
- alignItems: 'center',
26
- justifyContent: 'space-between',
27
- }}
28
- >
29
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
30
- <img src="/nomad-karaoke-logo.svg" alt="Nomad Karaoke" style={{ height: 40 }} />
31
- <Typography
32
- variant="h6"
33
- sx={{
34
- fontWeight: 'bold',
35
- color: theme.palette.text.primary,
36
- fontSize: '1.1rem',
37
- lineHeight: 1,
38
- m: 0,
39
- }}
40
- >
41
- Lyrics Transcription Review
42
- </Typography>
43
- </Box>
44
-
45
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
46
- {onToggleTheme && (
47
- <Tooltip title={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}>
48
- <IconButton
49
- onClick={onToggleTheme}
50
- sx={{
51
- color: theme.palette.text.secondary,
52
- '&:hover': {
53
- color: theme.palette.text.primary,
54
- backgroundColor: theme.palette.action.hover,
55
- },
56
- }}
57
- >
58
- {isDarkMode ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
59
- </IconButton>
60
- </Tooltip>
61
- )}
62
- </Box>
63
- </Box>
64
- )
65
- }
@@ -1,180 +0,0 @@
1
- import { Box, IconButton, Slider, Typography } from '@mui/material'
2
- import PlayArrowIcon from '@mui/icons-material/PlayArrow'
3
- import PauseIcon from '@mui/icons-material/Pause'
4
- import { useEffect, useRef, useState, useCallback } from 'react'
5
- import { ApiClient } from '../api'
6
-
7
- interface AudioPlayerProps {
8
- apiClient: ApiClient | null,
9
- onTimeUpdate?: (time: number) => void,
10
- audioHash: string
11
- }
12
-
13
- export default function AudioPlayer({ apiClient, onTimeUpdate, audioHash }: AudioPlayerProps) {
14
- const [isPlaying, setIsPlaying] = useState(false)
15
- const [currentTime, setCurrentTime] = useState(0)
16
- const [duration, setDuration] = useState(0)
17
- const audioRef = useRef<HTMLAudioElement | null>(null)
18
-
19
- useEffect(() => {
20
- if (!apiClient) return
21
-
22
- const audio = new Audio(apiClient.getAudioUrl(audioHash))
23
- audioRef.current = audio
24
-
25
- // Add requestAnimationFrame for smoother updates
26
- let animationFrameId: number
27
-
28
- const updateTime = () => {
29
- const time = audio.currentTime
30
- setCurrentTime(time)
31
- onTimeUpdate?.(time)
32
- animationFrameId = requestAnimationFrame(updateTime)
33
- }
34
-
35
- audio.addEventListener('play', () => {
36
- setIsPlaying(true)
37
- window.isAudioPlaying = true
38
- updateTime()
39
- })
40
-
41
- audio.addEventListener('pause', () => {
42
- setIsPlaying(false)
43
- window.isAudioPlaying = false
44
- cancelAnimationFrame(animationFrameId)
45
- })
46
-
47
- audio.addEventListener('ended', () => {
48
- cancelAnimationFrame(animationFrameId)
49
- setIsPlaying(false)
50
- window.isAudioPlaying = false
51
- setCurrentTime(0)
52
- })
53
-
54
- audio.addEventListener('loadedmetadata', () => {
55
- setDuration(audio.duration)
56
- })
57
-
58
- return () => {
59
- cancelAnimationFrame(animationFrameId)
60
- audio.pause()
61
- audio.src = ''
62
- audioRef.current = null
63
- window.isAudioPlaying = false
64
- }
65
- }, [apiClient, onTimeUpdate, audioHash])
66
-
67
- const handlePlayPause = () => {
68
- if (!audioRef.current) return
69
-
70
- if (isPlaying) {
71
- audioRef.current.pause()
72
- } else {
73
- audioRef.current.play()
74
- }
75
- setIsPlaying(!isPlaying)
76
- }
77
-
78
- const handleSeek = (_: Event, newValue: number | number[]) => {
79
- if (!audioRef.current) return
80
- const time = newValue as number
81
- audioRef.current.currentTime = time
82
- setCurrentTime(time)
83
- }
84
-
85
- const formatTime = (seconds: number) => {
86
- const mins = Math.floor(seconds / 60)
87
- const secs = Math.floor(seconds % 60)
88
- return `${mins}:${secs.toString().padStart(2, '0')}`
89
- }
90
-
91
- // Add this method to expose seeking functionality
92
- const seekAndPlay = (time: number) => {
93
- if (!audioRef.current) return
94
-
95
- audioRef.current.currentTime = time
96
- setCurrentTime(time)
97
- audioRef.current.play()
98
- setIsPlaying(true)
99
- }
100
-
101
- const togglePlayback = useCallback(() => {
102
- if (!audioRef.current) return
103
-
104
- if (isPlaying) {
105
- audioRef.current.pause()
106
- } else {
107
- audioRef.current.play()
108
- }
109
- setIsPlaying(!isPlaying)
110
- }, [isPlaying])
111
-
112
- // Expose methods and duration globally
113
- useEffect(() => {
114
- if (!apiClient) return
115
-
116
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
117
- const win = window as any
118
- win.seekAndPlayAudio = seekAndPlay
119
- win.toggleAudioPlayback = togglePlayback
120
- win.getAudioDuration = () => duration
121
-
122
- return () => {
123
- delete win.seekAndPlayAudio
124
- delete win.toggleAudioPlayback
125
- delete win.getAudioDuration
126
- }
127
- }, [apiClient, togglePlayback, duration])
128
-
129
- if (!apiClient) return null
130
-
131
- return (
132
- <Box sx={{
133
- display: 'flex',
134
- alignItems: 'center',
135
- gap: 0.5,
136
- backgroundColor: 'background.paper',
137
- borderRadius: 1,
138
- height: '32px',
139
- }}>
140
- <Typography variant="body2" color="text.secondary" sx={{ mr: 0.5, fontSize: '0.75rem' }}>
141
- Playback:
142
- </Typography>
143
-
144
- <IconButton
145
- onClick={handlePlayPause}
146
- size="small"
147
- sx={{ p: 0.5 }}
148
- >
149
- {isPlaying ? <PauseIcon fontSize="small" /> : <PlayArrowIcon fontSize="small" />}
150
- </IconButton>
151
-
152
- <Typography variant="body2" sx={{ minWidth: 32, fontSize: '0.75rem' }}>
153
- {formatTime(currentTime)}
154
- </Typography>
155
-
156
- <Slider
157
- value={currentTime}
158
- min={0}
159
- max={duration}
160
- onChange={handleSeek}
161
- size="small"
162
- sx={{
163
- width: 150,
164
- mx: 0.5,
165
- '& .MuiSlider-thumb': {
166
- width: 10,
167
- height: 10,
168
- },
169
- '& .MuiSlider-rail, & .MuiSlider-track': {
170
- height: 3
171
- }
172
- }}
173
- />
174
-
175
- <Typography variant="body2" sx={{ minWidth: 32, fontSize: '0.75rem' }}>
176
- {formatTime(duration)}
177
- </Typography>
178
- </Box>
179
- )
180
- }