karaoke-gen 0.103.1__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 (339) hide show
  1. backend/Dockerfile.base +1 -0
  2. backend/api/routes/admin.py +226 -3
  3. backend/api/routes/push.py +238 -0
  4. backend/api/routes/users.py +14 -3
  5. backend/config.py +12 -1
  6. backend/main.py +2 -1
  7. backend/models/job.py +4 -0
  8. backend/models/user.py +20 -2
  9. backend/services/encoding_interface.py +4 -0
  10. backend/services/gce_encoding/main.py +22 -8
  11. backend/services/job_manager.py +68 -11
  12. backend/services/job_notification_service.py +4 -21
  13. backend/services/push_notification_service.py +409 -0
  14. backend/services/stripe_service.py +2 -2
  15. backend/tests/conftest.py +2 -1
  16. backend/tests/test_admin_delete_outputs.py +352 -0
  17. backend/tests/test_gce_encoding_worker.py +229 -0
  18. backend/tests/test_impersonation.py +18 -3
  19. backend/tests/test_job_notification_service.py +24 -58
  20. backend/tests/test_push_notification_service.py +460 -0
  21. backend/tests/test_push_routes.py +357 -0
  22. backend/tests/test_stripe_service.py +205 -0
  23. backend/tests/test_video_worker_orchestrator.py +189 -0
  24. backend/workers/video_worker_orchestrator.py +23 -0
  25. karaoke_gen/instrumental_review/server.py +145 -35
  26. karaoke_gen/nextjs_frontend/__init__.py +98 -0
  27. karaoke_gen/nextjs_frontend/out/404/index.html +1 -0
  28. karaoke_gen/nextjs_frontend/out/404.html +1 -0
  29. karaoke_gen/nextjs_frontend/out/__next.__PAGE__.txt +9 -0
  30. karaoke_gen/nextjs_frontend/out/__next._full.txt +22 -0
  31. karaoke_gen/nextjs_frontend/out/__next._head.txt +8 -0
  32. karaoke_gen/nextjs_frontend/out/__next._index.txt +9 -0
  33. karaoke_gen/nextjs_frontend/out/__next._tree.txt +2 -0
  34. karaoke_gen/nextjs_frontend/out/_next/static/chunks/01a7f8fe40f1ff47.js +1 -0
  35. karaoke_gen/nextjs_frontend/out/_next/static/chunks/112f346e31f991df.js +4 -0
  36. karaoke_gen/nextjs_frontend/out/_next/static/chunks/16d1a4dd9d8a873a.js +3 -0
  37. karaoke_gen/nextjs_frontend/out/_next/static/chunks/1ab85c362b8b0e86.js +9 -0
  38. karaoke_gen/nextjs_frontend/out/_next/static/chunks/247eb132b7f7b574.js +1 -0
  39. karaoke_gen/nextjs_frontend/out/_next/static/chunks/2b80d15cc95e4818.js +1 -0
  40. karaoke_gen/nextjs_frontend/out/_next/static/chunks/32c7eba5cd46c1bc.js +7 -0
  41. karaoke_gen/nextjs_frontend/out/_next/static/chunks/483f26794eae53d0.js +1 -0
  42. karaoke_gen/nextjs_frontend/out/_next/static/chunks/550c3b02e85f196a.js +1 -0
  43. karaoke_gen/nextjs_frontend/out/_next/static/chunks/55c5ade44387bef8.js +1 -0
  44. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5628d92b5893add2.css +1 -0
  45. karaoke_gen/nextjs_frontend/out/_next/static/chunks/56ebf7665e4341c8.js +7 -0
  46. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5997132b61dec430.js +1 -0
  47. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5ea55255bce3eb9e.js +5 -0
  48. karaoke_gen/nextjs_frontend/out/_next/static/chunks/5eda89a57490b3cd.js +1 -0
  49. karaoke_gen/nextjs_frontend/out/_next/static/chunks/692f5d9e0d700c76.js +3 -0
  50. karaoke_gen/nextjs_frontend/out/_next/static/chunks/71d7a05b14f9f0f4.js +1 -0
  51. karaoke_gen/nextjs_frontend/out/_next/static/chunks/81ac355749ef3302.js +1 -0
  52. karaoke_gen/nextjs_frontend/out/_next/static/chunks/95f7e5934dbb0e5d.js +1 -0
  53. karaoke_gen/nextjs_frontend/out/_next/static/chunks/9bce8f19eaa46940.js +1 -0
  54. karaoke_gen/nextjs_frontend/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  55. karaoke_gen/nextjs_frontend/out/_next/static/chunks/a9ed54eed3e14c92.js +2 -0
  56. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b35cd41238ecfb17.js +1 -0
  57. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5bc3c3d5ebd49eb.js +1 -0
  58. karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5c078c08db5ae32.js +5 -0
  59. karaoke_gen/nextjs_frontend/out/_next/static/chunks/be9c44a178104187.js +1 -0
  60. karaoke_gen/nextjs_frontend/out/_next/static/chunks/c4c840e18cb4861c.js +1 -0
  61. karaoke_gen/nextjs_frontend/out/_next/static/chunks/c645af7d6b65f73e.js +1 -0
  62. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d2c5e2575df784d4.js +1 -0
  63. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d30af02b96d81462.js +1 -0
  64. karaoke_gen/nextjs_frontend/out/_next/static/chunks/d9bdf64f4ec1e9b7.js +7 -0
  65. karaoke_gen/nextjs_frontend/out/_next/static/chunks/dcde6ed684dacd0e.js +5 -0
  66. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e422cbe931246000.js +1 -0
  67. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e483af34fc792d38.js +1 -0
  68. karaoke_gen/nextjs_frontend/out/_next/static/chunks/e57422aad6b897da.js +1 -0
  69. karaoke_gen/nextjs_frontend/out/_next/static/chunks/ef02697fb404726a.js +1 -0
  70. karaoke_gen/nextjs_frontend/out/_next/static/chunks/ff1a16fafef87110.js +1 -0
  71. karaoke_gen/nextjs_frontend/out/_next/static/chunks/turbopack-2d9ca3017a9deedf.js +3 -0
  72. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_buildManifest.js +11 -0
  73. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_clientMiddlewareManifest.json +1 -0
  74. karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_ssgManifest.js +1 -0
  75. karaoke_gen/nextjs_frontend/out/_not-found/__next._full.txt +18 -0
  76. karaoke_gen/nextjs_frontend/out/_not-found/__next._head.txt +8 -0
  77. karaoke_gen/nextjs_frontend/out/_not-found/__next._index.txt +9 -0
  78. karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.__PAGE__.txt +5 -0
  79. karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.txt +4 -0
  80. karaoke_gen/nextjs_frontend/out/_not-found/__next._tree.txt +2 -0
  81. karaoke_gen/nextjs_frontend/out/_not-found/index.html +1 -0
  82. karaoke_gen/nextjs_frontend/out/_not-found/index.txt +18 -0
  83. karaoke_gen/nextjs_frontend/out/admin/__next._full.txt +25 -0
  84. karaoke_gen/nextjs_frontend/out/admin/__next._head.txt +8 -0
  85. karaoke_gen/nextjs_frontend/out/admin/__next._index.txt +9 -0
  86. karaoke_gen/nextjs_frontend/out/admin/__next._tree.txt +2 -0
  87. karaoke_gen/nextjs_frontend/out/admin/__next.admin.__PAGE__.txt +9 -0
  88. karaoke_gen/nextjs_frontend/out/admin/__next.admin.txt +7 -0
  89. karaoke_gen/nextjs_frontend/out/admin/beta/__next._full.txt +25 -0
  90. karaoke_gen/nextjs_frontend/out/admin/beta/__next._head.txt +8 -0
  91. karaoke_gen/nextjs_frontend/out/admin/beta/__next._index.txt +9 -0
  92. karaoke_gen/nextjs_frontend/out/admin/beta/__next._tree.txt +2 -0
  93. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.__PAGE__.txt +9 -0
  94. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.txt +4 -0
  95. karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.txt +7 -0
  96. karaoke_gen/nextjs_frontend/out/admin/beta/index.html +1 -0
  97. karaoke_gen/nextjs_frontend/out/admin/beta/index.txt +25 -0
  98. karaoke_gen/nextjs_frontend/out/admin/index.html +1 -0
  99. karaoke_gen/nextjs_frontend/out/admin/index.txt +25 -0
  100. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._full.txt +25 -0
  101. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._head.txt +8 -0
  102. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._index.txt +9 -0
  103. karaoke_gen/nextjs_frontend/out/admin/jobs/__next._tree.txt +2 -0
  104. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.__PAGE__.txt +9 -0
  105. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.txt +4 -0
  106. karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.txt +7 -0
  107. karaoke_gen/nextjs_frontend/out/admin/jobs/index.html +1 -0
  108. karaoke_gen/nextjs_frontend/out/admin/jobs/index.txt +25 -0
  109. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._full.txt +25 -0
  110. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._head.txt +8 -0
  111. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._index.txt +9 -0
  112. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._tree.txt +2 -0
  113. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.__PAGE__.txt +9 -0
  114. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.txt +4 -0
  115. karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.txt +7 -0
  116. karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.html +1 -0
  117. karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.txt +25 -0
  118. karaoke_gen/nextjs_frontend/out/admin/searches/__next._full.txt +25 -0
  119. karaoke_gen/nextjs_frontend/out/admin/searches/__next._head.txt +8 -0
  120. karaoke_gen/nextjs_frontend/out/admin/searches/__next._index.txt +9 -0
  121. karaoke_gen/nextjs_frontend/out/admin/searches/__next._tree.txt +2 -0
  122. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.__PAGE__.txt +9 -0
  123. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.txt +4 -0
  124. karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.txt +7 -0
  125. karaoke_gen/nextjs_frontend/out/admin/searches/index.html +1 -0
  126. karaoke_gen/nextjs_frontend/out/admin/searches/index.txt +25 -0
  127. karaoke_gen/nextjs_frontend/out/admin/users/__next._full.txt +25 -0
  128. karaoke_gen/nextjs_frontend/out/admin/users/__next._head.txt +8 -0
  129. karaoke_gen/nextjs_frontend/out/admin/users/__next._index.txt +9 -0
  130. karaoke_gen/nextjs_frontend/out/admin/users/__next._tree.txt +2 -0
  131. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.txt +7 -0
  132. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.__PAGE__.txt +9 -0
  133. karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.txt +4 -0
  134. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._full.txt +25 -0
  135. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._head.txt +8 -0
  136. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._index.txt +9 -0
  137. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._tree.txt +2 -0
  138. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.txt +7 -0
  139. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.__PAGE__.txt +9 -0
  140. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.txt +4 -0
  141. karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.txt +4 -0
  142. karaoke_gen/nextjs_frontend/out/admin/users/detail/index.html +1 -0
  143. karaoke_gen/nextjs_frontend/out/admin/users/detail/index.txt +25 -0
  144. karaoke_gen/nextjs_frontend/out/admin/users/index.html +1 -0
  145. karaoke_gen/nextjs_frontend/out/admin/users/index.txt +25 -0
  146. karaoke_gen/nextjs_frontend/out/app/__next._full.txt +22 -0
  147. karaoke_gen/nextjs_frontend/out/app/__next._head.txt +8 -0
  148. karaoke_gen/nextjs_frontend/out/app/__next._index.txt +9 -0
  149. karaoke_gen/nextjs_frontend/out/app/__next._tree.txt +2 -0
  150. karaoke_gen/nextjs_frontend/out/app/__next.app.__PAGE__.txt +9 -0
  151. karaoke_gen/nextjs_frontend/out/app/__next.app.txt +4 -0
  152. karaoke_gen/nextjs_frontend/out/app/index.html +1 -0
  153. karaoke_gen/nextjs_frontend/out/app/index.txt +22 -0
  154. karaoke_gen/nextjs_frontend/out/app/jobs/__next._full.txt +19 -0
  155. karaoke_gen/nextjs_frontend/out/app/jobs/__next._head.txt +8 -0
  156. karaoke_gen/nextjs_frontend/out/app/jobs/__next._index.txt +9 -0
  157. karaoke_gen/nextjs_frontend/out/app/jobs/__next._tree.txt +2 -0
  158. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  159. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.txt +4 -0
  160. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.txt +4 -0
  161. karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.txt +4 -0
  162. karaoke_gen/nextjs_frontend/out/app/jobs/index.html +1 -0
  163. karaoke_gen/nextjs_frontend/out/app/jobs/index.txt +19 -0
  164. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._full.txt +19 -0
  165. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._head.txt +8 -0
  166. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._index.txt +9 -0
  167. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._tree.txt +2 -0
  168. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  169. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.txt +4 -0
  170. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.txt +4 -0
  171. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.txt +4 -0
  172. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.html +1 -0
  173. karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.txt +19 -0
  174. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._full.txt +19 -0
  175. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._head.txt +8 -0
  176. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._index.txt +9 -0
  177. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._tree.txt +2 -0
  178. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
  179. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.txt +4 -0
  180. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.txt +4 -0
  181. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.txt +4 -0
  182. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.html +1 -0
  183. karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.txt +19 -0
  184. karaoke_gen/nextjs_frontend/out/auth/verify/__next._full.txt +22 -0
  185. karaoke_gen/nextjs_frontend/out/auth/verify/__next._head.txt +8 -0
  186. karaoke_gen/nextjs_frontend/out/auth/verify/__next._index.txt +9 -0
  187. karaoke_gen/nextjs_frontend/out/auth/verify/__next._tree.txt +2 -0
  188. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.txt +4 -0
  189. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.__PAGE__.txt +9 -0
  190. karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.txt +4 -0
  191. karaoke_gen/nextjs_frontend/out/auth/verify/index.html +1 -0
  192. karaoke_gen/nextjs_frontend/out/auth/verify/index.txt +22 -0
  193. karaoke_gen/nextjs_frontend/out/index.html +1 -0
  194. karaoke_gen/nextjs_frontend/out/index.txt +22 -0
  195. karaoke_gen/nextjs_frontend/out/manifest.webmanifest +31 -0
  196. karaoke_gen/nextjs_frontend/out/order/success/__next._full.txt +22 -0
  197. karaoke_gen/nextjs_frontend/out/order/success/__next._head.txt +8 -0
  198. karaoke_gen/nextjs_frontend/out/order/success/__next._index.txt +9 -0
  199. karaoke_gen/nextjs_frontend/out/order/success/__next._tree.txt +2 -0
  200. karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.__PAGE__.txt +9 -0
  201. karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.txt +4 -0
  202. karaoke_gen/nextjs_frontend/out/order/success/__next.order.txt +4 -0
  203. karaoke_gen/nextjs_frontend/out/order/success/index.html +1 -0
  204. karaoke_gen/nextjs_frontend/out/order/success/index.txt +22 -0
  205. karaoke_gen/nextjs_frontend/out/payment/success/__next._full.txt +22 -0
  206. karaoke_gen/nextjs_frontend/out/payment/success/__next._head.txt +8 -0
  207. karaoke_gen/nextjs_frontend/out/payment/success/__next._index.txt +9 -0
  208. karaoke_gen/nextjs_frontend/out/payment/success/__next._tree.txt +2 -0
  209. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.__PAGE__.txt +9 -0
  210. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.txt +4 -0
  211. karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.txt +4 -0
  212. karaoke_gen/nextjs_frontend/out/payment/success/index.html +1 -0
  213. karaoke_gen/nextjs_frontend/out/payment/success/index.txt +22 -0
  214. karaoke_gen/nextjs_frontend/out/screenshots/email-action_reminder.png +0 -0
  215. karaoke_gen/nextjs_frontend/out/screenshots/email-beta_welcome.png +0 -0
  216. karaoke_gen/nextjs_frontend/out/screenshots/email-job_completion.png +0 -0
  217. karaoke_gen/nextjs_frontend/out/screenshots/example-output.avif +0 -0
  218. karaoke_gen/nextjs_frontend/out/screenshots/homepage-full.png +0 -0
  219. karaoke_gen/nextjs_frontend/out/screenshots/homepage-hero.png +0 -0
  220. karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.avif +0 -0
  221. karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.png +0 -0
  222. karaoke_gen/nextjs_frontend/out/screenshots/job-dashboard.avif +0 -0
  223. karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.avif +0 -0
  224. karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.png +0 -0
  225. karaoke_gen/nextjs_frontend/out/sw.js +183 -0
  226. karaoke_gen/utils/cli_args.py +3 -3
  227. karaoke_gen/utils/gen_cli.py +4 -0
  228. karaoke_gen/utils/remote_cli.py +8 -40
  229. {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/METADATA +2 -1
  230. {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/RECORD +244 -131
  231. {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/WHEEL +1 -1
  232. lyrics_transcriber/correction/agentic/agent.py +83 -60
  233. lyrics_transcriber/correction/anchor_sequence.py +48 -3
  234. lyrics_transcriber/correction/corrector.py +92 -58
  235. lyrics_transcriber/review/server.py +165 -33
  236. lyrics_transcriber/utils/tracing.py +214 -0
  237. karaoke_gen/instrumental_review/static/index.html +0 -1695
  238. lyrics_transcriber/frontend/.gitignore +0 -24
  239. lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +0 -935
  240. lyrics_transcriber/frontend/.yarnrc.yml +0 -3
  241. lyrics_transcriber/frontend/README.md +0 -50
  242. lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +0 -210
  243. lyrics_transcriber/frontend/__init__.py +0 -25
  244. lyrics_transcriber/frontend/e2e/agentic-corrections.spec.ts +0 -207
  245. lyrics_transcriber/frontend/e2e/fixtures/agentic-correction-data.json +0 -226
  246. lyrics_transcriber/frontend/eslint.config.js +0 -28
  247. lyrics_transcriber/frontend/index.html +0 -22
  248. lyrics_transcriber/frontend/package-lock.json +0 -4553
  249. lyrics_transcriber/frontend/package.json +0 -48
  250. lyrics_transcriber/frontend/playwright.config.ts +0 -69
  251. lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
  252. lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
  253. lyrics_transcriber/frontend/src/App.tsx +0 -243
  254. lyrics_transcriber/frontend/src/api.ts +0 -262
  255. lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +0 -111
  256. lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +0 -114
  257. lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +0 -204
  258. lyrics_transcriber/frontend/src/components/AppHeader.tsx +0 -65
  259. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +0 -180
  260. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +0 -175
  261. lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +0 -359
  262. lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +0 -281
  263. lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +0 -162
  264. lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +0 -257
  265. lyrics_transcriber/frontend/src/components/EditActionBar.tsx +0 -94
  266. lyrics_transcriber/frontend/src/components/EditModal.tsx +0 -720
  267. lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +0 -592
  268. lyrics_transcriber/frontend/src/components/EditWordList.tsx +0 -431
  269. lyrics_transcriber/frontend/src/components/FileUpload.tsx +0 -77
  270. lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +0 -467
  271. lyrics_transcriber/frontend/src/components/Header.tsx +0 -520
  272. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +0 -1526
  273. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +0 -216
  274. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +0 -721
  275. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +0 -80
  276. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +0 -999
  277. lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +0 -51
  278. lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +0 -127
  279. lyrics_transcriber/frontend/src/components/ModeSelector.tsx +0 -67
  280. lyrics_transcriber/frontend/src/components/ModelSelector.tsx +0 -23
  281. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +0 -177
  282. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +0 -268
  283. lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +0 -336
  284. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +0 -354
  285. lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +0 -64
  286. lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +0 -383
  287. lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +0 -131
  288. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +0 -266
  289. lyrics_transcriber/frontend/src/components/WordDivider.tsx +0 -191
  290. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +0 -466
  291. lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +0 -56
  292. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +0 -89
  293. lyrics_transcriber/frontend/src/components/shared/constants.ts +0 -30
  294. lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +0 -180
  295. lyrics_transcriber/frontend/src/components/shared/styles.ts +0 -13
  296. lyrics_transcriber/frontend/src/components/shared/types.js +0 -2
  297. lyrics_transcriber/frontend/src/components/shared/types.ts +0 -135
  298. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +0 -177
  299. lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +0 -78
  300. lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +0 -75
  301. lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +0 -360
  302. lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +0 -110
  303. lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +0 -22
  304. lyrics_transcriber/frontend/src/hooks/useManualSync.ts +0 -537
  305. lyrics_transcriber/frontend/src/main.tsx +0 -11
  306. lyrics_transcriber/frontend/src/theme.ts +0 -406
  307. lyrics_transcriber/frontend/src/types/global.d.ts +0 -9
  308. lyrics_transcriber/frontend/src/types.js +0 -2
  309. lyrics_transcriber/frontend/src/types.ts +0 -199
  310. lyrics_transcriber/frontend/src/validation.ts +0 -132
  311. lyrics_transcriber/frontend/src/vite-env.d.ts +0 -1
  312. lyrics_transcriber/frontend/tsconfig.app.json +0 -26
  313. lyrics_transcriber/frontend/tsconfig.json +0 -25
  314. lyrics_transcriber/frontend/tsconfig.node.json +0 -23
  315. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +0 -1
  316. lyrics_transcriber/frontend/update_version.js +0 -11
  317. lyrics_transcriber/frontend/vite.config.d.ts +0 -2
  318. lyrics_transcriber/frontend/vite.config.js +0 -15
  319. lyrics_transcriber/frontend/vite.config.ts +0 -16
  320. lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
  321. lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
  322. lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
  323. lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js +0 -44465
  324. lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js.map +0 -1
  325. lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
  326. lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
  327. lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
  328. lyrics_transcriber/frontend/web_assets/index.html +0 -22
  329. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
  330. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.svg +0 -5
  331. lyrics_transcriber/frontend/yarn.lock +0 -3711
  332. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/apple-touch-icon.png +0 -0
  333. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-16x16.png +0 -0
  334. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-32x32.png +0 -0
  335. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon.ico +0 -0
  336. {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/nomad-karaoke-logo.svg +0 -0
  337. /lyrics_transcriber/frontend/public/nomad-karaoke-logo.png → /karaoke_gen/nextjs_frontend/out/nomad-logo.png +0 -0
  338. {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/entry_points.txt +0 -0
  339. {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,431 +0,0 @@
1
- import {
2
- Box,
3
- TextField,
4
- IconButton,
5
- Button,
6
- Pagination,
7
- Typography,
8
- useMediaQuery,
9
- useTheme
10
- } from '@mui/material'
11
- import DeleteIcon from '@mui/icons-material/Delete'
12
- import SplitIcon from '@mui/icons-material/CallSplit'
13
- import AutoFixHighIcon from '@mui/icons-material/AutoFixHigh'
14
- import { Word } from '../types'
15
- import { useState, memo, useMemo } from 'react'
16
- import WordDivider from './WordDivider'
17
-
18
- interface EditWordListProps {
19
- words: Word[]
20
- onWordUpdate: (index: number, updates: Partial<Word>) => void
21
- onSplitWord: (index: number) => void
22
- onMergeWords: (index: number) => void
23
- onAddWord: (index?: number) => void
24
- onRemoveWord: (index: number) => void
25
- onSplitSegment?: (wordIndex: number) => void
26
- onAddSegment?: (beforeIndex: number) => void
27
- onMergeSegment?: (mergeWithNext: boolean) => void
28
- isGlobal?: boolean
29
- }
30
-
31
- // Create a memoized word row component to prevent re-renders
32
- const WordRow = memo(function WordRow({
33
- word,
34
- index,
35
- onWordUpdate,
36
- onSplitWord,
37
- onRemoveWord,
38
- wordsLength,
39
- onTabNavigation,
40
- isMobile
41
- }: {
42
- word: Word
43
- index: number
44
- onWordUpdate: (index: number, updates: Partial<Word>) => void
45
- onSplitWord: (index: number) => void
46
- onRemoveWord: (index: number) => void
47
- wordsLength: number
48
- onTabNavigation: (currentIndex: number) => void
49
- isMobile: boolean
50
- }) {
51
- const handleKeyDown = (e: React.KeyboardEvent) => {
52
- if (e.key === 'Tab' && !e.shiftKey) {
53
- e.preventDefault();
54
- onTabNavigation(index);
55
- }
56
- };
57
-
58
- return (
59
- <Box sx={{
60
- display: 'flex',
61
- flexDirection: isMobile ? 'column' : 'row',
62
- gap: isMobile ? 1 : 2,
63
- alignItems: isMobile ? 'stretch' : 'center',
64
- padding: '4px 0',
65
- }}>
66
- {/* Word text field - full width on mobile */}
67
- <Box sx={{
68
- display: 'flex',
69
- gap: 1,
70
- alignItems: 'center',
71
- flex: isMobile ? 'none' : 1
72
- }}>
73
- <TextField
74
- label={`Word ${index}`}
75
- value={word.text}
76
- onChange={(e) => onWordUpdate(index, { text: e.target.value })}
77
- onKeyDown={handleKeyDown}
78
- fullWidth
79
- size="small"
80
- id={`word-text-${index}`}
81
- />
82
- {/* Action buttons inline with word on mobile */}
83
- {isMobile && (
84
- <>
85
- <IconButton
86
- onClick={() => onSplitWord(index)}
87
- title="Split Word"
88
- sx={{ color: 'primary.main' }}
89
- size="small"
90
- >
91
- <SplitIcon fontSize="small" />
92
- </IconButton>
93
- <IconButton
94
- onClick={() => onRemoveWord(index)}
95
- disabled={wordsLength <= 1}
96
- title="Remove Word"
97
- sx={{ color: 'error.main' }}
98
- size="small"
99
- >
100
- <DeleteIcon fontSize="small" />
101
- </IconButton>
102
- </>
103
- )}
104
- </Box>
105
-
106
- {/* Time fields - row on desktop, separate row on mobile */}
107
- <Box sx={{
108
- display: 'flex',
109
- gap: 1,
110
- alignItems: 'center',
111
- justifyContent: isMobile ? 'flex-start' : 'flex-end'
112
- }}>
113
- <TextField
114
- label="Start"
115
- value={word.start_time?.toFixed(2) ?? ''}
116
- onChange={(e) => onWordUpdate(index, { start_time: parseFloat(e.target.value) })}
117
- type="number"
118
- inputProps={{ step: 0.01 }}
119
- sx={{ width: isMobile ? '80px' : '100px' }}
120
- size="small"
121
- />
122
- <TextField
123
- label="End"
124
- value={word.end_time?.toFixed(2) ?? ''}
125
- onChange={(e) => onWordUpdate(index, { end_time: parseFloat(e.target.value) })}
126
- type="number"
127
- inputProps={{ step: 0.01 }}
128
- sx={{ width: isMobile ? '80px' : '100px' }}
129
- size="small"
130
- />
131
- {/* Action buttons on desktop only */}
132
- {!isMobile && (
133
- <>
134
- <IconButton
135
- onClick={() => onSplitWord(index)}
136
- title="Split Word"
137
- sx={{ color: 'primary.main' }}
138
- size="small"
139
- >
140
- <SplitIcon fontSize="small" />
141
- </IconButton>
142
- <IconButton
143
- onClick={() => onRemoveWord(index)}
144
- disabled={wordsLength <= 1}
145
- title="Remove Word"
146
- sx={{ color: 'error.main' }}
147
- size="small"
148
- >
149
- <DeleteIcon fontSize="small" />
150
- </IconButton>
151
- </>
152
- )}
153
- </Box>
154
- </Box>
155
- );
156
- });
157
-
158
- // Memoized word item component that includes the word row and divider
159
- const WordItem = memo(function WordItem({
160
- word,
161
- index,
162
- onWordUpdate,
163
- onSplitWord,
164
- onRemoveWord,
165
- onAddWord,
166
- onMergeWords,
167
- onSplitSegment,
168
- onAddSegment,
169
- onMergeSegment,
170
- wordsLength,
171
- isGlobal,
172
- onTabNavigation,
173
- isMobile
174
- }: {
175
- word: Word
176
- index: number
177
- onWordUpdate: (index: number, updates: Partial<Word>) => void
178
- onSplitWord: (index: number) => void
179
- onRemoveWord: (index: number) => void
180
- onAddWord: (index: number) => void
181
- onMergeWords: (index: number) => void
182
- onSplitSegment?: (index: number) => void
183
- onAddSegment?: (index: number) => void
184
- onMergeSegment?: (mergeWithNext: boolean) => void
185
- wordsLength: number
186
- isGlobal: boolean
187
- onTabNavigation: (currentIndex: number) => void
188
- isMobile: boolean
189
- }) {
190
- return (
191
- <Box key={word.id}>
192
- <WordRow
193
- word={word}
194
- index={index}
195
- onWordUpdate={onWordUpdate}
196
- onSplitWord={onSplitWord}
197
- onRemoveWord={onRemoveWord}
198
- wordsLength={wordsLength}
199
- onTabNavigation={onTabNavigation}
200
- isMobile={isMobile}
201
- />
202
-
203
- {/* Word divider with merge/split functionality */}
204
- {!isGlobal && (
205
- <WordDivider
206
- onAddWord={() => onAddWord(index)}
207
- onMergeWords={() => onMergeWords(index)}
208
- onSplitSegment={() => onSplitSegment?.(index)}
209
- onAddSegmentAfter={
210
- index === wordsLength - 1
211
- ? () => onAddSegment?.(index + 1)
212
- : undefined
213
- }
214
- onMergeSegment={
215
- index === wordsLength - 1
216
- ? () => onMergeSegment?.(true)
217
- : undefined
218
- }
219
- canMerge={index < wordsLength - 1}
220
- isLast={index === wordsLength - 1}
221
- />
222
- )}
223
- {isGlobal && (
224
- <WordDivider
225
- onAddWord={() => onAddWord(index)}
226
- onMergeWords={index < wordsLength - 1 ? () => onMergeWords(index) : undefined}
227
- canMerge={index < wordsLength - 1}
228
- />
229
- )}
230
- </Box>
231
- );
232
- });
233
-
234
- export default function EditWordList({
235
- words,
236
- onWordUpdate,
237
- onSplitWord,
238
- onMergeWords,
239
- onAddWord,
240
- onRemoveWord,
241
- onSplitSegment,
242
- onAddSegment,
243
- onMergeSegment,
244
- isGlobal = false
245
- }: EditWordListProps) {
246
- const theme = useTheme()
247
- const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
248
-
249
- const [replacementText, setReplacementText] = useState('')
250
- const [page, setPage] = useState(1)
251
- const pageSize = isGlobal ? 50 : words.length // Use pagination only in global mode
252
-
253
- const handleReplaceAllWords = () => {
254
- const newWords = replacementText.trim().split(/\s+/)
255
- newWords.forEach((text, index) => {
256
- if (index < words.length) {
257
- onWordUpdate(index, { text })
258
- }
259
- })
260
- setReplacementText('')
261
- }
262
-
263
- // Calculate pagination values
264
- const pageCount = Math.ceil(words.length / pageSize)
265
- const startIndex = (page - 1) * pageSize
266
- const endIndex = Math.min(startIndex + pageSize, words.length)
267
-
268
- // Get the words for the current page
269
- const visibleWords = useMemo(() => {
270
- return isGlobal
271
- ? words.slice(startIndex, endIndex)
272
- : words;
273
- }, [words, isGlobal, startIndex, endIndex]);
274
-
275
- // Handle page change
276
- const handlePageChange = (_event: React.ChangeEvent<unknown>, value: number) => {
277
- setPage(value);
278
- };
279
-
280
- // Handle tab navigation between word text fields
281
- const handleTabNavigation = (currentIndex: number) => {
282
- // console.log('handleTabNavigation called with index:', currentIndex);
283
- const nextIndex = (currentIndex + 1) % words.length;
284
- // console.log('Next index calculated:', nextIndex, 'Total words:', words.length);
285
-
286
- // If the next word is on a different page, change the page
287
- if (isGlobal && (nextIndex < startIndex || nextIndex >= endIndex)) {
288
- // console.log('Next word is on different page. Current page:', page, 'startIndex:', startIndex, 'endIndex:', endIndex);
289
- const nextPage = Math.floor(nextIndex / pageSize) + 1;
290
- // console.log('Changing to page:', nextPage);
291
- setPage(nextPage);
292
-
293
- // Use setTimeout to allow the page change to render before focusing
294
- setTimeout(() => {
295
- // console.log('Timeout callback executing, trying to focus element with ID:', `word-text-${nextIndex}`);
296
- focusWordTextField(nextIndex);
297
- }, 50);
298
- } else {
299
- // console.log('Next word is on same page, trying to focus element with ID:', `word-text-${nextIndex}`);
300
- focusWordTextField(nextIndex);
301
- }
302
- };
303
-
304
- // Helper function to focus a word text field by index
305
- const focusWordTextField = (index: number) => {
306
- // Material-UI TextField uses a more complex structure
307
- // The actual input is inside the TextField component
308
- const element = document.getElementById(`word-text-${index}`);
309
- // console.log('Element found:', !!element);
310
-
311
- if (element) {
312
- // Try different selectors to find the input element
313
- // First try the standard input selector
314
- let input = element.querySelector('input');
315
-
316
- // If that doesn't work, try the MUI-specific selector
317
- if (!input) {
318
- input = element.querySelector('.MuiInputBase-input');
319
- }
320
-
321
- // console.log('Input element found:', !!input);
322
- if (input) {
323
- input.focus();
324
- input.select();
325
- // console.log('Focus and select called on input');
326
- } else {
327
- // As a fallback, try to focus the TextField itself
328
- // console.log('Trying to focus the TextField itself');
329
- element.focus();
330
- }
331
- }
332
- };
333
-
334
- return (
335
- <Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, flexGrow: 1, minHeight: 0 }}>
336
- {/* Initial divider with Add Segment Before button */}
337
- {!isGlobal && (
338
- <WordDivider
339
- onAddWord={() => onAddWord(-1)}
340
- onAddSegmentBefore={() => onAddSegment?.(0)}
341
- onMergeSegment={() => onMergeSegment?.(false)}
342
- isFirst={true}
343
- />
344
- )}
345
- {isGlobal && (
346
- <WordDivider
347
- onAddWord={() => onAddWord(-1)}
348
- />
349
- )}
350
-
351
- {/* Word list with scrolling */}
352
- <Box sx={{
353
- display: 'flex',
354
- flexDirection: 'column',
355
- gap: 0.5,
356
- flexGrow: 1,
357
- overflowY: 'auto',
358
- mb: 0,
359
- pt: 1,
360
- '&::-webkit-scrollbar': {
361
- width: '8px',
362
- },
363
- '&::-webkit-scrollbar-thumb': {
364
- backgroundColor: (theme) => theme.palette.mode === 'dark'
365
- ? 'rgba(248, 250, 252, 0.2)' // light scrollbar for dark mode
366
- : 'rgba(30, 41, 59, 0.3)', // dark scrollbar for light mode
367
- borderRadius: '4px',
368
- },
369
- scrollbarWidth: 'thin',
370
- msOverflowStyle: 'autohiding-scrollbar',
371
- }}>
372
- {visibleWords.map((word, visibleIndex) => {
373
- const actualIndex = isGlobal ? startIndex + visibleIndex : visibleIndex;
374
- return (
375
- <WordItem
376
- key={word.id}
377
- word={word}
378
- index={actualIndex}
379
- onWordUpdate={onWordUpdate}
380
- onSplitWord={onSplitWord}
381
- onRemoveWord={onRemoveWord}
382
- onAddWord={onAddWord}
383
- onMergeWords={onMergeWords}
384
- onSplitSegment={onSplitSegment}
385
- onAddSegment={onAddSegment}
386
- onMergeSegment={onMergeSegment}
387
- wordsLength={words.length}
388
- isGlobal={isGlobal}
389
- onTabNavigation={handleTabNavigation}
390
- isMobile={isMobile}
391
- />
392
- );
393
- })}
394
- </Box>
395
-
396
- {/* Pagination controls (only in global mode) */}
397
- {isGlobal && pageCount > 1 && (
398
- <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', mb: 1 }}>
399
- <Pagination
400
- count={pageCount}
401
- page={page}
402
- onChange={handlePageChange}
403
- color="primary"
404
- size="small"
405
- />
406
- <Typography variant="body2" sx={{ ml: 2 }}>
407
- Showing words {startIndex + 1}-{endIndex} of {words.length}
408
- </Typography>
409
- </Box>
410
- )}
411
-
412
- <Box sx={{ display: 'flex', gap: 2, mb: 0.6 }}>
413
- <TextField
414
- value={replacementText}
415
- onChange={(e) => setReplacementText(e.target.value)}
416
- placeholder="Replace all words"
417
- size="small"
418
- sx={{ flexGrow: 1, maxWidth: 'calc(100% - 140px)' }}
419
- />
420
- <Button
421
- onClick={handleReplaceAllWords}
422
- startIcon={<AutoFixHighIcon />}
423
- size="small"
424
- sx={{ whiteSpace: 'nowrap' }}
425
- >
426
- Replace All
427
- </Button>
428
- </Box>
429
- </Box>
430
- )
431
- }
@@ -1,77 +0,0 @@
1
- import { ChangeEvent, DragEvent, useState } from 'react'
2
- import { Paper, Typography } from '@mui/material'
3
- import CloudUploadIcon from '@mui/icons-material/CloudUpload'
4
- import { CorrectionData } from '../types'
5
-
6
- interface FileUploadProps {
7
- onUpload: (data: CorrectionData) => void
8
- }
9
-
10
- export default function FileUpload({ onUpload }: FileUploadProps) {
11
- const [isDragging, setIsDragging] = useState(false)
12
-
13
- const handleDragOver = (e: DragEvent) => {
14
- e.preventDefault()
15
- setIsDragging(true)
16
- }
17
-
18
- const handleDragLeave = () => {
19
- setIsDragging(false)
20
- }
21
-
22
- const handleDrop = async (e: DragEvent) => {
23
- e.preventDefault()
24
- setIsDragging(false)
25
- const file = e.dataTransfer.files[0]
26
- await processFile(file)
27
- }
28
-
29
- const handleFileInput = async (e: ChangeEvent<HTMLInputElement>) => {
30
- if (e.target.files?.length) {
31
- await processFile(e.target.files[0])
32
- }
33
- }
34
-
35
- const processFile = async (file: File) => {
36
- try {
37
- const text = await file.text()
38
- const data = JSON.parse(text)
39
- onUpload(data)
40
- } catch (error) {
41
- console.error('Error processing file:', error)
42
- // TODO: Add error handling UI
43
- }
44
- }
45
-
46
- return (
47
- <Paper
48
- sx={{
49
- p: 4,
50
- display: 'flex',
51
- flexDirection: 'column',
52
- alignItems: 'center',
53
- backgroundColor: isDragging ? 'action.hover' : 'background.paper',
54
- cursor: 'pointer',
55
- }}
56
- onDragOver={handleDragOver}
57
- onDragLeave={handleDragLeave}
58
- onDrop={handleDrop}
59
- onClick={() => document.getElementById('file-input')?.click()}
60
- >
61
- <input
62
- type="file"
63
- id="file-input"
64
- style={{ display: 'none' }}
65
- accept="application/json"
66
- onChange={handleFileInput}
67
- />
68
- <CloudUploadIcon sx={{ fontSize: 48, mb: 2 }} />
69
- <Typography variant="h6" gutterBottom>
70
- Upload Lyrics Correction Review JSON
71
- </Typography>
72
- <Typography variant="body2" color="text.secondary">
73
- Drag and drop a file here, or click to select
74
- </Typography>
75
- </Paper>
76
- )
77
- }