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,537 +0,0 @@
1
- import { useState, useCallback, useEffect, useRef } from 'react'
2
- import { LyricsSegment, Word } from '../types'
3
-
4
- interface UseManualSyncProps {
5
- editedSegment: LyricsSegment | null
6
- currentTime: number
7
- onPlaySegment?: (startTime: number) => void
8
- updateSegment: (words: Word[]) => void
9
- }
10
-
11
- // Constants for tap detection
12
- const TAP_THRESHOLD_MS = 200 // If spacebar is pressed for less than this time, it's considered a tap
13
- const DEFAULT_WORD_DURATION = 0.5 // Default duration in seconds when tapping (500ms)
14
- const OVERLAP_BUFFER = 0.01 // Buffer to prevent word overlap (10ms)
15
-
16
- export default function useManualSync({
17
- editedSegment,
18
- currentTime,
19
- onPlaySegment,
20
- updateSegment
21
- }: UseManualSyncProps) {
22
- const [isManualSyncing, setIsManualSyncing] = useState(false)
23
- const [isPaused, setIsPaused] = useState(false)
24
- const [syncWordIndex, setSyncWordIndex] = useState<number>(-1)
25
- const currentTimeRef = useRef(currentTime)
26
- const [isSpacebarPressed, setIsSpacebarPressed] = useState(false)
27
- const wordStartTimeRef = useRef<number | null>(null)
28
- const wordsRef = useRef<Word[]>([])
29
- const spacebarPressTimeRef = useRef<number | null>(null)
30
-
31
- // Use ref to track if we need to update segment to avoid calling it too frequently
32
- const needsSegmentUpdateRef = useRef(false)
33
-
34
- // Keep currentTimeRef up to date
35
- useEffect(() => {
36
- currentTimeRef.current = currentTime
37
- }, [currentTime])
38
-
39
- // Keep wordsRef up to date
40
- useEffect(() => {
41
- if (editedSegment) {
42
- wordsRef.current = [...editedSegment.words]
43
- }
44
- }, [editedSegment])
45
-
46
- // Debounced segment update to batch multiple word changes
47
- useEffect(() => {
48
- if (needsSegmentUpdateRef.current) {
49
- needsSegmentUpdateRef.current = false
50
- updateSegment(wordsRef.current)
51
- }
52
- }, [updateSegment, syncWordIndex]) // Only update when syncWordIndex changes
53
-
54
- const cleanupManualSync = useCallback(() => {
55
- setIsManualSyncing(false)
56
- setIsPaused(false)
57
- setSyncWordIndex(-1)
58
- setIsSpacebarPressed(false)
59
- wordStartTimeRef.current = null
60
- spacebarPressTimeRef.current = null
61
- needsSegmentUpdateRef.current = false
62
-
63
- // Stop audio playback when cleaning up manual sync
64
- if (window.toggleAudioPlayback && window.isAudioPlaying) {
65
- window.toggleAudioPlayback()
66
- }
67
- }, [])
68
-
69
- const pauseManualSync = useCallback(() => {
70
- if (isManualSyncing && !isPaused) {
71
- console.log('useManualSync - Pausing manual sync')
72
- setIsPaused(true)
73
- // Pause audio playback
74
- if (window.toggleAudioPlayback && window.isAudioPlaying) {
75
- window.toggleAudioPlayback()
76
- }
77
- }
78
- }, [isManualSyncing, isPaused])
79
-
80
- const resumeManualSync = useCallback(() => {
81
- if (isManualSyncing && isPaused) {
82
- console.log('useManualSync - Resuming manual sync')
83
- setIsPaused(false)
84
-
85
- // Find the first unsynced word and resume from there
86
- if (editedSegment) {
87
- const firstUnsyncedIndex = editedSegment.words.findIndex(word =>
88
- word.start_time === null || word.end_time === null
89
- )
90
-
91
- if (firstUnsyncedIndex !== -1 && firstUnsyncedIndex !== syncWordIndex) {
92
- console.log('useManualSync - Resuming from first unsynced word', {
93
- previousIndex: syncWordIndex,
94
- newIndex: firstUnsyncedIndex,
95
- wordText: editedSegment.words[firstUnsyncedIndex]?.text
96
- })
97
- setSyncWordIndex(firstUnsyncedIndex)
98
- } else {
99
- console.log('useManualSync - Resuming from current position', { syncWordIndex })
100
- }
101
- }
102
-
103
- // Resume audio playback if we have an onPlaySegment function
104
- if (onPlaySegment && currentTimeRef.current !== undefined) {
105
- onPlaySegment(currentTimeRef.current)
106
- }
107
- }
108
- }, [isManualSyncing, isPaused, onPlaySegment, editedSegment, syncWordIndex])
109
-
110
- const handleKeyDown = useCallback((e: KeyboardEvent) => {
111
- if (e.code !== 'Space') return
112
-
113
- console.log('useManualSync - Spacebar pressed down', {
114
- isManualSyncing,
115
- hasEditedSegment: !!editedSegment,
116
- syncWordIndex,
117
- currentTime: currentTimeRef.current
118
- })
119
-
120
- e.preventDefault()
121
- e.stopPropagation()
122
-
123
- if (isManualSyncing && editedSegment && !isSpacebarPressed && !isPaused) {
124
- setIsSpacebarPressed(true)
125
-
126
- // Record the start time of the current word
127
- wordStartTimeRef.current = currentTimeRef.current
128
-
129
- // Record when the spacebar was pressed (for tap detection)
130
- spacebarPressTimeRef.current = Date.now()
131
-
132
- // Update the word's start time immediately
133
- if (syncWordIndex < editedSegment.words.length) {
134
- const newWords = [...wordsRef.current]
135
- const currentWord = newWords[syncWordIndex]
136
- const currentStartTime = currentTimeRef.current
137
-
138
- // Set the start time for the current word
139
- currentWord.start_time = currentStartTime
140
-
141
- // Handle the end time of the previous word (if it exists)
142
- if (syncWordIndex > 0) {
143
- const previousWord = newWords[syncWordIndex - 1]
144
- if (previousWord.start_time !== null) {
145
- const timeSincePreviousStart = currentStartTime - previousWord.start_time
146
-
147
- // Only adjust previous word's end time if:
148
- // 1. It doesn't have an end time set yet (was never released), OR
149
- // 2. The current start would overlap with existing end time
150
- const needsAdjustment = previousWord.end_time === null ||
151
- (previousWord.end_time !== null && previousWord.end_time > currentStartTime)
152
-
153
- if (needsAdjustment) {
154
- if (timeSincePreviousStart > 1.0) {
155
- // Gap of over 1 second - set previous word's end time to 500ms after its start
156
- previousWord.end_time = previousWord.start_time + 0.5
157
- console.log('useManualSync - Gap detected, setting previous word end time to +500ms', {
158
- previousWordIndex: syncWordIndex - 1,
159
- previousWordText: previousWord.text,
160
- previousStartTime: previousWord.start_time,
161
- previousEndTime: previousWord.end_time,
162
- gap: timeSincePreviousStart.toFixed(2) + 's',
163
- reason: 'gap > 1s'
164
- })
165
- } else {
166
- // Normal flow - set previous word's end time to current word's start time minus 5ms
167
- previousWord.end_time = currentStartTime - 0.005
168
- console.log('useManualSync - Setting previous word end time to current start - 5ms', {
169
- previousWordIndex: syncWordIndex - 1,
170
- previousWordText: previousWord.text,
171
- previousEndTime: previousWord.end_time,
172
- currentStartTime: currentStartTime,
173
- gap: timeSincePreviousStart.toFixed(2) + 's',
174
- reason: 'normal flow'
175
- })
176
- }
177
- } else {
178
- console.log('useManualSync - Preserving previous word timing (manually set)', {
179
- previousWordIndex: syncWordIndex - 1,
180
- previousWordText: previousWord.text,
181
- previousStartTime: previousWord.start_time,
182
- previousEndTime: previousWord.end_time,
183
- preservedDuration: previousWord.end_time !== null ?
184
- (previousWord.end_time - previousWord.start_time).toFixed(2) + 's' : 'N/A',
185
- reason: 'already timed correctly'
186
- })
187
- }
188
- }
189
- }
190
-
191
- console.log('useManualSync - Recording word start time', {
192
- wordIndex: syncWordIndex,
193
- wordText: currentWord?.text,
194
- time: currentStartTime
195
- })
196
-
197
- // Update our ref
198
- wordsRef.current = newWords
199
-
200
- // Mark that we need to update the segment
201
- needsSegmentUpdateRef.current = true
202
- }
203
- } else if (!isManualSyncing && editedSegment && onPlaySegment) {
204
- console.log('useManualSync - Handling segment playback', {
205
- editedSegmentId: editedSegment.id,
206
- isGlobalReplacement: editedSegment.id === 'global-replacement'
207
- })
208
-
209
- // For global replacement segments, don't handle general playback
210
- // since we want the user to use Manual Sync instead
211
- if (editedSegment.id === 'global-replacement') {
212
- console.log('useManualSync - Ignoring playback for global replacement - please use Manual Sync')
213
- return
214
- }
215
-
216
- // Toggle segment playback when not in manual sync mode
217
- const startTime = editedSegment.start_time ?? 0
218
- const endTime = editedSegment.end_time ?? 0
219
-
220
- if (currentTimeRef.current >= startTime && currentTimeRef.current <= endTime) {
221
- if (window.toggleAudioPlayback) {
222
- window.toggleAudioPlayback()
223
- }
224
- } else {
225
- onPlaySegment(startTime)
226
- }
227
- }
228
- }, [isManualSyncing, editedSegment, syncWordIndex, onPlaySegment, isSpacebarPressed, isPaused])
229
-
230
- const handleKeyUp = useCallback((e: KeyboardEvent) => {
231
- if (e.code !== 'Space') return
232
-
233
- console.log('useManualSync - Spacebar released', {
234
- isManualSyncing,
235
- hasEditedSegment: !!editedSegment,
236
- syncWordIndex,
237
- currentTime: currentTimeRef.current,
238
- wordStartTime: wordStartTimeRef.current
239
- })
240
-
241
- e.preventDefault()
242
- e.stopPropagation()
243
-
244
- if (isManualSyncing && editedSegment && isSpacebarPressed && !isPaused) {
245
- const currentWord = syncWordIndex < editedSegment.words.length ? editedSegment.words[syncWordIndex] : null
246
- const pressDuration = spacebarPressTimeRef.current ? Date.now() - spacebarPressTimeRef.current : 0
247
- const isTap = pressDuration < TAP_THRESHOLD_MS
248
-
249
- console.log('useManualSync - Recording word end time', {
250
- wordIndex: syncWordIndex,
251
- wordText: currentWord?.text,
252
- startTime: wordStartTimeRef.current,
253
- endTime: currentTimeRef.current,
254
- pressDuration: `${pressDuration}ms`,
255
- isTap,
256
- tapThreshold: TAP_THRESHOLD_MS,
257
- duration: currentWord ? (currentTimeRef.current - (wordStartTimeRef.current || 0)).toFixed(2) + 's' : 'N/A'
258
- })
259
-
260
- setIsSpacebarPressed(false)
261
-
262
- if (syncWordIndex < editedSegment.words.length) {
263
- const newWords = [...wordsRef.current]
264
- const currentWord = newWords[syncWordIndex]
265
-
266
- // Set the end time for the current word based on whether it was a tap or hold
267
- if (isTap) {
268
- // For a tap, set a default duration
269
- const defaultEndTime = (wordStartTimeRef.current || currentTimeRef.current) + DEFAULT_WORD_DURATION
270
- currentWord.end_time = defaultEndTime
271
- console.log('useManualSync - Tap detected, setting default duration', {
272
- wordText: currentWord.text,
273
- startTime: wordStartTimeRef.current,
274
- defaultEndTime,
275
- duration: DEFAULT_WORD_DURATION
276
- })
277
- } else {
278
- // For a hold, use the current time as the end time
279
- currentWord.end_time = currentTimeRef.current
280
- console.log('useManualSync - Hold detected, using actual timing', {
281
- wordText: currentWord.text,
282
- startTime: wordStartTimeRef.current,
283
- endTime: currentTimeRef.current,
284
- actualDuration: (currentTimeRef.current - (wordStartTimeRef.current || 0)).toFixed(2) + 's'
285
- })
286
- }
287
-
288
- // Update our ref
289
- wordsRef.current = newWords
290
-
291
- // Move to the next word
292
- if (syncWordIndex === editedSegment.words.length - 1) {
293
- // If this was the last word, finish manual sync
294
- console.log('useManualSync - Completed manual sync for all words')
295
- setIsManualSyncing(false)
296
- setSyncWordIndex(-1)
297
- wordStartTimeRef.current = null
298
- spacebarPressTimeRef.current = null
299
- } else {
300
- // Otherwise, move to the next word
301
- const nextWord = editedSegment.words[syncWordIndex + 1]
302
- console.log('useManualSync - Moving to next word', {
303
- nextWordIndex: syncWordIndex + 1,
304
- nextWordText: nextWord?.text
305
- })
306
- setSyncWordIndex(syncWordIndex + 1)
307
- }
308
-
309
- // Mark that we need to update the segment
310
- needsSegmentUpdateRef.current = true
311
- }
312
- }
313
- }, [isManualSyncing, editedSegment, syncWordIndex, isSpacebarPressed, isPaused])
314
-
315
- // Add a handler for when the next word starts to adjust previous word's end time if needed
316
- useEffect(() => {
317
- if (isManualSyncing && editedSegment && syncWordIndex > 0) {
318
- const newWords = [...wordsRef.current]
319
- const prevWord = newWords[syncWordIndex - 1]
320
- const currentWord = newWords[syncWordIndex]
321
-
322
- // If the previous word's end time overlaps with the current word's start time,
323
- // adjust the previous word's end time
324
- if (prevWord && currentWord &&
325
- prevWord.end_time !== null && currentWord.start_time !== null &&
326
- prevWord.end_time > currentWord.start_time) {
327
-
328
- console.log('useManualSync - Adjusting previous word end time to prevent overlap', {
329
- prevWordIndex: syncWordIndex - 1,
330
- prevWordText: prevWord.text,
331
- prevWordEndTime: prevWord.end_time,
332
- currentWordStartTime: currentWord.start_time,
333
- newEndTime: currentWord.start_time - OVERLAP_BUFFER
334
- })
335
-
336
- prevWord.end_time = currentWord.start_time - OVERLAP_BUFFER
337
-
338
- // Update our ref
339
- wordsRef.current = newWords
340
-
341
- // Mark that we need to update the segment
342
- needsSegmentUpdateRef.current = true
343
- }
344
- }
345
- }, [syncWordIndex, isManualSyncing, editedSegment])
346
-
347
- // Combine the key handlers into a single function for external use
348
- const handleSpacebar = useCallback((e: KeyboardEvent) => {
349
- if (e.type === 'keydown') {
350
- handleKeyDown(e)
351
- } else if (e.type === 'keyup') {
352
- handleKeyUp(e)
353
- }
354
- }, [handleKeyDown, handleKeyUp])
355
-
356
- // Touch-friendly handlers for mobile (simulates spacebar press/release)
357
- const handleTapStart = useCallback(() => {
358
- if (!isManualSyncing || !editedSegment || isSpacebarPressed || isPaused) return
359
-
360
- console.log('useManualSync - Touch/tap started', {
361
- syncWordIndex,
362
- currentTime: currentTimeRef.current
363
- })
364
-
365
- setIsSpacebarPressed(true)
366
-
367
- // Record the start time of the current word
368
- wordStartTimeRef.current = currentTimeRef.current
369
-
370
- // Record when the tap started (for tap detection)
371
- spacebarPressTimeRef.current = Date.now()
372
-
373
- // Update the word's start time immediately
374
- if (syncWordIndex < editedSegment.words.length) {
375
- const newWords = [...wordsRef.current]
376
- const currentWord = newWords[syncWordIndex]
377
- const currentStartTime = currentTimeRef.current
378
-
379
- // Set the start time for the current word
380
- currentWord.start_time = currentStartTime
381
-
382
- // Handle the end time of the previous word (if it exists)
383
- if (syncWordIndex > 0) {
384
- const previousWord = newWords[syncWordIndex - 1]
385
- if (previousWord.start_time !== null) {
386
- const timeSincePreviousStart = currentStartTime - previousWord.start_time
387
-
388
- const needsAdjustment = previousWord.end_time === null ||
389
- (previousWord.end_time !== null && previousWord.end_time > currentStartTime)
390
-
391
- if (needsAdjustment) {
392
- if (timeSincePreviousStart > 1.0) {
393
- previousWord.end_time = previousWord.start_time + 0.5
394
- } else {
395
- previousWord.end_time = currentStartTime - 0.005
396
- }
397
- }
398
- }
399
- }
400
-
401
- // Update our ref
402
- wordsRef.current = newWords
403
-
404
- // Mark that we need to update the segment
405
- needsSegmentUpdateRef.current = true
406
- }
407
- }, [isManualSyncing, editedSegment, syncWordIndex, isSpacebarPressed, isPaused])
408
-
409
- const handleTapEnd = useCallback(() => {
410
- if (!isManualSyncing || !editedSegment || !isSpacebarPressed || isPaused) return
411
-
412
- const pressDuration = spacebarPressTimeRef.current ? Date.now() - spacebarPressTimeRef.current : 0
413
- const isTap = pressDuration < TAP_THRESHOLD_MS
414
-
415
- console.log('useManualSync - Touch/tap ended', {
416
- syncWordIndex,
417
- pressDuration: `${pressDuration}ms`,
418
- isTap
419
- })
420
-
421
- setIsSpacebarPressed(false)
422
-
423
- if (syncWordIndex < editedSegment.words.length) {
424
- const newWords = [...wordsRef.current]
425
- const currentWord = newWords[syncWordIndex]
426
-
427
- // Set the end time for the current word based on whether it was a tap or hold
428
- if (isTap) {
429
- const defaultEndTime = (wordStartTimeRef.current || currentTimeRef.current) + DEFAULT_WORD_DURATION
430
- currentWord.end_time = defaultEndTime
431
- } else {
432
- currentWord.end_time = currentTimeRef.current
433
- }
434
-
435
- // Update our ref
436
- wordsRef.current = newWords
437
-
438
- // Move to the next word
439
- if (syncWordIndex === editedSegment.words.length - 1) {
440
- // If this was the last word, finish manual sync
441
- console.log('useManualSync - Completed manual sync for all words')
442
- setIsManualSyncing(false)
443
- setSyncWordIndex(-1)
444
- wordStartTimeRef.current = null
445
- spacebarPressTimeRef.current = null
446
- } else {
447
- // Otherwise, move to the next word
448
- setSyncWordIndex(syncWordIndex + 1)
449
- }
450
-
451
- // Mark that we need to update the segment
452
- needsSegmentUpdateRef.current = true
453
- }
454
- }, [isManualSyncing, editedSegment, syncWordIndex, isSpacebarPressed, isPaused])
455
-
456
- const startManualSync = useCallback(() => {
457
- if (isManualSyncing) {
458
- cleanupManualSync()
459
- return
460
- }
461
-
462
- if (!editedSegment || !onPlaySegment) return
463
-
464
- // Make sure we have the latest words
465
- wordsRef.current = [...editedSegment.words]
466
-
467
- // Find the first unsynced word to start from
468
- const firstUnsyncedIndex = editedSegment.words.findIndex(word =>
469
- word.start_time === null || word.end_time === null
470
- )
471
-
472
- const startIndex = firstUnsyncedIndex !== -1 ? firstUnsyncedIndex : 0
473
-
474
- console.log('useManualSync - Starting manual sync', {
475
- totalWords: editedSegment.words.length,
476
- startingFromIndex: startIndex,
477
- startingWord: editedSegment.words[startIndex]?.text
478
- })
479
-
480
- setIsManualSyncing(true)
481
- setSyncWordIndex(startIndex)
482
- setIsSpacebarPressed(false)
483
- wordStartTimeRef.current = null
484
- spacebarPressTimeRef.current = null
485
- needsSegmentUpdateRef.current = false
486
- // Start playing 3 seconds before segment start
487
- onPlaySegment((editedSegment.start_time ?? 0) - 3)
488
- }, [isManualSyncing, editedSegment, onPlaySegment, cleanupManualSync])
489
-
490
- // Auto-stop sync if we go past the end time (but not for global replacement segments)
491
- useEffect(() => {
492
- if (!editedSegment || !isManualSyncing) return
493
-
494
- // Don't auto-stop for global replacement segments - let user manually finish
495
- if (editedSegment.id === 'global-replacement') {
496
- console.log('useManualSync - Skipping auto-stop for global replacement segment')
497
- return
498
- }
499
-
500
- // Set up an interval to check if we should auto-stop
501
- const checkAutoStop = () => {
502
- const endTime = editedSegment.end_time ?? 0
503
-
504
- if (window.isAudioPlaying && currentTimeRef.current > endTime) {
505
- console.log('useManualSync - Auto-stopping: current time exceeded end time', {
506
- currentTime: currentTimeRef.current,
507
- endTime,
508
- segmentId: editedSegment.id
509
- })
510
- window.toggleAudioPlayback?.()
511
- cleanupManualSync()
512
- }
513
- }
514
-
515
- // Check immediately and then every 100ms
516
- checkAutoStop()
517
- const intervalId = setInterval(checkAutoStop, 100)
518
-
519
- return () => {
520
- clearInterval(intervalId)
521
- }
522
- }, [isManualSyncing, editedSegment, cleanupManualSync])
523
-
524
- return {
525
- isManualSyncing,
526
- isPaused,
527
- syncWordIndex,
528
- startManualSync,
529
- pauseManualSync,
530
- resumeManualSync,
531
- cleanupManualSync,
532
- handleSpacebar,
533
- isSpacebarPressed,
534
- handleTapStart,
535
- handleTapEnd
536
- }
537
- }
@@ -1,11 +0,0 @@
1
- import ReactDOM from 'react-dom/client'
2
- import App from './App'
3
- // Import version from package.json
4
- import packageJson from '../package.json'
5
-
6
- // Log the frontend version when the app loads
7
- console.log(`🎵 Lyrics Transcriber Frontend v${packageJson.version}`)
8
-
9
- ReactDOM.createRoot(document.getElementById('root')!).render(
10
- <App />
11
- )