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,592 +0,0 @@
1
- import {
2
- Box,
3
- Button,
4
- Typography,
5
- IconButton,
6
- Tooltip,
7
- Stack,
8
- useMediaQuery,
9
- useTheme
10
- } from '@mui/material'
11
- import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'
12
- import CancelIcon from '@mui/icons-material/Cancel'
13
- import ZoomInIcon from '@mui/icons-material/ZoomIn'
14
- import ZoomOutIcon from '@mui/icons-material/ZoomOut'
15
- import ArrowBackIcon from '@mui/icons-material/ArrowBack'
16
- import ArrowForwardIcon from '@mui/icons-material/ArrowForward'
17
- import AutorenewIcon from '@mui/icons-material/Autorenew'
18
- import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline'
19
- import PlayArrowIcon from '@mui/icons-material/PlayArrow'
20
- import StopIcon from '@mui/icons-material/Stop'
21
- import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong'
22
- import TouchAppIcon from '@mui/icons-material/TouchApp'
23
- import TimelineEditor from './TimelineEditor'
24
- import { Word } from '../types'
25
- import { useState, useEffect, useCallback, useRef, useMemo, memo } from 'react'
26
-
27
- // Separate TapButton component to properly track local press state
28
- // This prevents issues where onMouseLeave fires before parent state updates
29
- const TapButton = memo(function TapButton({
30
- isSpacebarPressed,
31
- onTapStart,
32
- onTapEnd
33
- }: {
34
- isSpacebarPressed: boolean
35
- onTapStart: () => void
36
- onTapEnd: () => void
37
- }) {
38
- const isPressedRef = useRef(false)
39
-
40
- const handleTapStart = useCallback(() => {
41
- isPressedRef.current = true
42
- onTapStart()
43
- }, [onTapStart])
44
-
45
- const handleTapEnd = useCallback(() => {
46
- if (isPressedRef.current) {
47
- isPressedRef.current = false
48
- onTapEnd()
49
- }
50
- }, [onTapEnd])
51
-
52
- return (
53
- <Button
54
- variant="contained"
55
- color={isSpacebarPressed ? "secondary" : "primary"}
56
- onTouchStart={(e) => {
57
- e.preventDefault()
58
- handleTapStart()
59
- }}
60
- onTouchEnd={(e) => {
61
- e.preventDefault()
62
- handleTapEnd()
63
- }}
64
- onMouseDown={handleTapStart}
65
- onMouseUp={handleTapEnd}
66
- onMouseLeave={handleTapEnd}
67
- startIcon={<TouchAppIcon />}
68
- sx={{
69
- py: 2,
70
- fontSize: '1.1rem',
71
- fontWeight: 'bold',
72
- width: '100%',
73
- minHeight: '56px',
74
- userSelect: 'none',
75
- WebkitUserSelect: 'none',
76
- touchAction: 'manipulation'
77
- }}
78
- >
79
- {isSpacebarPressed ? "HOLD..." : "TAP"}
80
- </Button>
81
- )
82
- })
83
-
84
- interface EditTimelineSectionProps {
85
- words: Word[]
86
- startTime: number
87
- endTime: number
88
- originalStartTime: number | null
89
- originalEndTime: number | null
90
- currentStartTime: number | null
91
- currentEndTime: number | null
92
- currentTime?: number
93
- isManualSyncing: boolean
94
- syncWordIndex: number
95
- isSpacebarPressed: boolean
96
- onWordUpdate: (index: number, updates: Partial<Word>) => void
97
- onUnsyncWord?: (index: number) => void
98
- onPlaySegment?: (time: number) => void
99
- onStopAudio?: () => void
100
- startManualSync: () => void
101
- pauseManualSync?: () => void
102
- resumeManualSync?: () => void
103
- isPaused?: boolean
104
- isGlobal?: boolean
105
- defaultZoomLevel?: number
106
- isReplaceAllMode?: boolean
107
- onTapStart?: () => void
108
- onTapEnd?: () => void
109
- }
110
-
111
- // Memoized control buttons to prevent unnecessary re-renders
112
- const TimelineControls = memo(({
113
- isGlobal,
114
- visibleStartTime,
115
- visibleEndTime,
116
- startTime,
117
- endTime,
118
- zoomLevel,
119
- autoScrollEnabled,
120
- currentTime,
121
- isManualSyncing,
122
- isReplaceAllMode,
123
- isPaused,
124
- onScrollLeft,
125
- onZoomOut,
126
- onZoomIn,
127
- onScrollRight,
128
- onToggleAutoScroll,
129
- onJumpToCurrentTime,
130
- onStartManualSync,
131
- onPauseResume,
132
- onStopAudio
133
- }: {
134
- isGlobal: boolean
135
- visibleStartTime: number
136
- visibleEndTime: number
137
- startTime: number
138
- endTime: number
139
- zoomLevel: number
140
- autoScrollEnabled: boolean
141
- currentTime?: number
142
- isManualSyncing: boolean
143
- isReplaceAllMode: boolean
144
- isPaused: boolean
145
- onScrollLeft: () => void
146
- onZoomOut: () => void
147
- onZoomIn: () => void
148
- onScrollRight: () => void
149
- onToggleAutoScroll: () => void
150
- onJumpToCurrentTime: () => void
151
- onStartManualSync: () => void
152
- onPauseResume: () => void
153
- onStopAudio?: () => void
154
- }) => {
155
- return (
156
- <Stack
157
- direction="row"
158
- spacing={0.5}
159
- alignItems="center"
160
- sx={{ flexWrap: 'wrap', justifyContent: 'center', gap: 0.5 }}
161
- >
162
- {isGlobal && (
163
- <>
164
- <Tooltip title="Scroll Left">
165
- <IconButton
166
- onClick={onScrollLeft}
167
- disabled={visibleStartTime <= startTime}
168
- size="small"
169
- >
170
- <ArrowBackIcon fontSize="small" />
171
- </IconButton>
172
- </Tooltip>
173
- <Tooltip title="Zoom Out (Show More Time)">
174
- <IconButton
175
- onClick={onZoomOut}
176
- disabled={zoomLevel >= (endTime - startTime) || (isReplaceAllMode && isManualSyncing && !isPaused)}
177
- size="small"
178
- >
179
- <ZoomOutIcon fontSize="small" />
180
- </IconButton>
181
- </Tooltip>
182
- <Tooltip title="Zoom In (Show Less Time)">
183
- <IconButton
184
- onClick={onZoomIn}
185
- disabled={zoomLevel <= 2 || (isReplaceAllMode && isManualSyncing && !isPaused)}
186
- size="small"
187
- >
188
- <ZoomInIcon fontSize="small" />
189
- </IconButton>
190
- </Tooltip>
191
- <Tooltip title="Scroll Right">
192
- <IconButton
193
- onClick={onScrollRight}
194
- disabled={visibleEndTime >= endTime}
195
- size="small"
196
- >
197
- <ArrowForwardIcon fontSize="small" />
198
- </IconButton>
199
- </Tooltip>
200
- <Tooltip
201
- title={autoScrollEnabled ?
202
- "Disable Auto-Page Turn During Playback" :
203
- "Enable Auto-Page Turn During Playback"}
204
- >
205
- <IconButton
206
- onClick={onToggleAutoScroll}
207
- color={autoScrollEnabled ? "primary" : "default"}
208
- size="small"
209
- >
210
- {autoScrollEnabled ? <AutorenewIcon fontSize="small" /> : <PauseCircleOutlineIcon fontSize="small" />}
211
- </IconButton>
212
- </Tooltip>
213
- <Tooltip title="Jump to Current Playback Position">
214
- <IconButton
215
- onClick={onJumpToCurrentTime}
216
- disabled={!currentTime}
217
- size="small"
218
- >
219
- <CenterFocusStrongIcon fontSize="small" />
220
- </IconButton>
221
- </Tooltip>
222
- </>
223
- )}
224
- {isReplaceAllMode && onStopAudio && (
225
- <Button
226
- variant="outlined"
227
- onClick={onStopAudio}
228
- startIcon={<StopIcon fontSize="small" />}
229
- color="error"
230
- size="small"
231
- >
232
- Stop
233
- </Button>
234
- )}
235
- <Button
236
- variant={isManualSyncing ? "outlined" : "contained"}
237
- onClick={onStartManualSync}
238
- startIcon={isManualSyncing ? <CancelIcon fontSize="small" /> : <PlayCircleOutlineIcon fontSize="small" />}
239
- color={isManualSyncing ? "error" : "primary"}
240
- size="small"
241
- >
242
- {isManualSyncing ? "Cancel" : "Tap To Sync"}
243
- </Button>
244
- {isManualSyncing && isReplaceAllMode && (
245
- <Button
246
- variant="outlined"
247
- onClick={onPauseResume}
248
- startIcon={isPaused ? <PlayArrowIcon fontSize="small" /> : <PauseCircleOutlineIcon fontSize="small" />}
249
- color={isPaused ? "success" : "warning"}
250
- size="small"
251
- >
252
- {isPaused ? "Resume" : "Pause"}
253
- </Button>
254
- )}
255
- </Stack>
256
- )
257
- })
258
-
259
- export default function EditTimelineSection({
260
- words,
261
- startTime,
262
- endTime,
263
- originalStartTime,
264
- originalEndTime,
265
- currentStartTime,
266
- currentEndTime,
267
- currentTime,
268
- isManualSyncing,
269
- syncWordIndex,
270
- isSpacebarPressed,
271
- onWordUpdate,
272
- onUnsyncWord,
273
- onPlaySegment,
274
- onStopAudio,
275
- startManualSync,
276
- pauseManualSync,
277
- resumeManualSync,
278
- isPaused = false,
279
- isGlobal = false,
280
- defaultZoomLevel = 10,
281
- isReplaceAllMode = false,
282
- onTapStart,
283
- onTapEnd
284
- }: EditTimelineSectionProps) {
285
- const theme = useTheme()
286
- const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
287
- // Add state for zoom level - use larger default for Replace All mode
288
- const [zoomLevel, setZoomLevel] = useState(defaultZoomLevel)
289
- const [visibleStartTime, setVisibleStartTime] = useState(startTime)
290
- const [visibleEndTime, setVisibleEndTime] = useState(Math.min(startTime + zoomLevel, endTime))
291
- const [autoScrollEnabled, setAutoScrollEnabled] = useState(true) // Default to enabled
292
- const timelineRef = useRef<HTMLDivElement>(null)
293
-
294
- // Memoize the effective time range to prevent recalculation
295
- const effectiveTimeRange = useMemo(() => ({
296
- start: isGlobal ? visibleStartTime : startTime,
297
- end: isGlobal ? visibleEndTime : endTime
298
- }), [isGlobal, visibleStartTime, visibleEndTime, startTime, endTime])
299
-
300
- // Auto-enable auto-scroll when manual sync starts or resumes
301
- useEffect(() => {
302
- if (isManualSyncing && !isPaused) {
303
- console.log('EditTimelineSection - Auto-enabling auto-scroll for manual sync')
304
- setAutoScrollEnabled(true)
305
- }
306
- }, [isManualSyncing, isPaused])
307
-
308
- // Initial setup of visible time range
309
- useEffect(() => {
310
- if (isGlobal) {
311
- // For global mode, start at the beginning
312
- setVisibleStartTime(startTime)
313
- setVisibleEndTime(Math.min(startTime + zoomLevel, endTime))
314
- } else {
315
- // For segment mode, always show the full segment
316
- setVisibleStartTime(startTime)
317
- setVisibleEndTime(endTime)
318
- }
319
- }, [startTime, endTime, zoomLevel, isGlobal])
320
-
321
- // Throttled auto-scroll to reduce frequent updates during playback
322
- const lastScrollUpdateRef = useRef<number>(0)
323
- const SCROLL_THROTTLE_MS = 100 // Only update scroll position every 100ms
324
-
325
- // Handle playback scrolling with "page turning" approach - throttled for performance
326
- useEffect(() => {
327
- // Skip if not in global mode, no current time, or auto-scroll is disabled
328
- if (!isGlobal || !currentTime || !autoScrollEnabled) return
329
-
330
- // Throttle scroll updates for performance
331
- const now = Date.now()
332
- if (now - lastScrollUpdateRef.current < SCROLL_THROTTLE_MS) return
333
- lastScrollUpdateRef.current = now
334
-
335
- // Only scroll when current time is outside or near the edge of the visible window
336
- if (currentTime < visibleStartTime) {
337
- // If current time is before visible window, jump to show it at the start
338
- const newStart = Math.max(startTime, currentTime)
339
- const newEnd = Math.min(endTime, newStart + zoomLevel)
340
- setVisibleStartTime(newStart)
341
- setVisibleEndTime(newEnd)
342
- } else if (currentTime > visibleEndTime - (zoomLevel * 0.05)) {
343
- // If current time is near the end of visible window (within 5% of zoom level from the end),
344
- // jump to the next "page" with current time at 5% from the left
345
- const pageOffset = zoomLevel * 0.05 // Position current time 5% from the left edge
346
- const newStart = Math.max(startTime, currentTime - pageOffset)
347
- const newEnd = Math.min(endTime, newStart + zoomLevel)
348
-
349
- // Only update if we're actually moving forward
350
- if (newStart > visibleStartTime) {
351
- setVisibleStartTime(newStart)
352
- setVisibleEndTime(newEnd)
353
- }
354
- }
355
- }, [currentTime, visibleStartTime, visibleEndTime, startTime, endTime, zoomLevel, isGlobal, autoScrollEnabled])
356
-
357
- // Update visible time range when zoom level changes - but don't auto-center on current time
358
- useEffect(() => {
359
- if (isGlobal) {
360
- // Don't auto-center on current time, just adjust the visible window based on zoom level
361
- // while keeping the left edge fixed (unless it would go out of bounds)
362
- const newEnd = Math.min(endTime, visibleStartTime + zoomLevel)
363
-
364
- // If the new end would exceed the total range, adjust the start time
365
- if (newEnd === endTime) {
366
- const newStart = Math.max(startTime, endTime - zoomLevel)
367
- setVisibleStartTime(newStart)
368
- }
369
-
370
- setVisibleEndTime(newEnd)
371
- } else {
372
- // For segment mode, always show the full segment
373
- setVisibleStartTime(startTime)
374
- setVisibleEndTime(endTime)
375
- }
376
- }, [zoomLevel, startTime, endTime, isGlobal, visibleStartTime])
377
-
378
- // Memoized event handlers to prevent unnecessary re-renders
379
- const handleZoomIn = useCallback(() => {
380
- if (isReplaceAllMode && isManualSyncing && !isPaused) return // Prevent zoom changes during active sync (but allow when paused)
381
- if (zoomLevel > 2) { // Minimum zoom level of 2 seconds
382
- setZoomLevel(zoomLevel - 2)
383
- }
384
- }, [isReplaceAllMode, isManualSyncing, isPaused, zoomLevel])
385
-
386
- const handleZoomOut = useCallback(() => {
387
- if (isReplaceAllMode && isManualSyncing && !isPaused) return // Prevent zoom changes during active sync (but allow when paused)
388
- if (zoomLevel < (endTime - startTime)) { // Maximum zoom is the full range
389
- setZoomLevel(zoomLevel + 2)
390
- }
391
- }, [isReplaceAllMode, isManualSyncing, isPaused, zoomLevel, endTime, startTime])
392
-
393
- const toggleAutoScroll = useCallback(() => {
394
- setAutoScrollEnabled(!autoScrollEnabled)
395
- }, [autoScrollEnabled])
396
-
397
- const jumpToCurrentTime = useCallback(() => {
398
- if (!isGlobal || !currentTime) return
399
-
400
- // Center the view around the current time
401
- const halfZoom = zoomLevel / 2
402
- let newStart = Math.max(startTime, currentTime - halfZoom)
403
- const newEnd = Math.min(endTime, newStart + zoomLevel)
404
-
405
- // Adjust start time if end time hits the boundary
406
- if (newEnd === endTime) {
407
- newStart = Math.max(startTime, endTime - zoomLevel)
408
- }
409
-
410
- setVisibleStartTime(newStart)
411
- setVisibleEndTime(newEnd)
412
- }, [currentTime, zoomLevel, startTime, endTime, isGlobal])
413
-
414
- // Handle horizontal scrolling - throttled for performance
415
- const handleScroll = useCallback((event: React.WheelEvent<HTMLDivElement>) => {
416
- if (isGlobal && event.deltaX !== 0) {
417
- event.preventDefault()
418
-
419
- // Disable auto-scroll when user manually scrolls
420
- setAutoScrollEnabled(false)
421
-
422
- // Calculate scroll amount in seconds (scale based on zoom level)
423
- const scrollAmount = (event.deltaX / 100) * (zoomLevel / 10)
424
-
425
- // Update visible time range
426
- let newStart = visibleStartTime + scrollAmount
427
- let newEnd = visibleEndTime + scrollAmount
428
-
429
- // Ensure we don't scroll beyond the boundaries
430
- if (newStart < startTime) {
431
- newStart = startTime
432
- newEnd = newStart + zoomLevel
433
- }
434
-
435
- if (newEnd > endTime) {
436
- newEnd = endTime
437
- newStart = Math.max(startTime, newEnd - zoomLevel)
438
- }
439
-
440
- setVisibleStartTime(newStart)
441
- setVisibleEndTime(newEnd)
442
- }
443
- }, [isGlobal, visibleStartTime, visibleEndTime, startTime, endTime, zoomLevel])
444
-
445
- const handleScrollLeft = useCallback(() => {
446
- if (!isGlobal) return
447
-
448
- // Disable auto-scroll when user manually scrolls
449
- setAutoScrollEnabled(false)
450
-
451
- // Scroll left by 25% of the current visible range
452
- const scrollAmount = zoomLevel * 0.25
453
- const newStart = Math.max(startTime, visibleStartTime - scrollAmount)
454
- const newEnd = newStart + zoomLevel
455
-
456
- setVisibleStartTime(newStart)
457
- setVisibleEndTime(newEnd)
458
- }, [isGlobal, zoomLevel, startTime, visibleStartTime])
459
-
460
- const handleScrollRight = useCallback(() => {
461
- if (!isGlobal) return
462
-
463
- // Disable auto-scroll when user manually scrolls
464
- setAutoScrollEnabled(false)
465
-
466
- // Scroll right by 25% of the current visible range
467
- const scrollAmount = zoomLevel * 0.25
468
- const newEnd = Math.min(endTime, visibleEndTime + scrollAmount)
469
- let newStart = newEnd - zoomLevel
470
-
471
- // Ensure we don't scroll beyond the start boundary
472
- if (newStart < startTime) {
473
- newStart = startTime
474
- const adjustedNewEnd = Math.min(endTime, newStart + zoomLevel)
475
- setVisibleEndTime(adjustedNewEnd)
476
- } else {
477
- setVisibleEndTime(newEnd)
478
- }
479
-
480
- setVisibleStartTime(newStart)
481
- }, [isGlobal, zoomLevel, endTime, visibleEndTime, startTime])
482
-
483
- const handlePauseResume = useCallback(() => {
484
- if (isPaused && resumeManualSync) {
485
- resumeManualSync()
486
- } else if (!isPaused && pauseManualSync) {
487
- pauseManualSync()
488
- }
489
- }, [isPaused, resumeManualSync, pauseManualSync])
490
-
491
- // Memoize current word info to prevent recalculation
492
- const currentWordInfo = useMemo(() => {
493
- if (!isManualSyncing || syncWordIndex < 0 || syncWordIndex >= words.length) {
494
- return null
495
- }
496
-
497
- return {
498
- index: syncWordIndex + 1,
499
- total: words.length,
500
- text: words[syncWordIndex]?.text || ''
501
- }
502
- }, [isManualSyncing, syncWordIndex, words])
503
-
504
- return (
505
- <>
506
- <Box
507
- sx={{ height: isMobile ? '80px' : '120px', mb: 0 }}
508
- ref={timelineRef}
509
- onWheel={handleScroll}
510
- >
511
- <TimelineEditor
512
- words={words}
513
- startTime={effectiveTimeRange.start}
514
- endTime={effectiveTimeRange.end}
515
- onWordUpdate={onWordUpdate}
516
- currentTime={currentTime}
517
- onPlaySegment={onPlaySegment}
518
- onUnsyncWord={onUnsyncWord}
519
- />
520
- </Box>
521
-
522
- <Box sx={{
523
- display: 'flex',
524
- flexDirection: isMobile ? 'column' : 'row',
525
- alignItems: isMobile ? 'stretch' : 'center',
526
- justifyContent: 'space-between',
527
- gap: isMobile ? 1 : 0,
528
- mt: isMobile ? 1.5 : 0,
529
- mb: isMobile ? 2 : 0
530
- }}>
531
- {/* Time range info - hidden on mobile to save space */}
532
- {!isMobile && (
533
- <Typography variant="body2" color="text.secondary">
534
- Original Time Range: {originalStartTime?.toFixed(2) ?? 'N/A'} - {originalEndTime?.toFixed(2) ?? 'N/A'}
535
- <br />
536
- Current Time Range: {currentStartTime?.toFixed(2) ?? 'N/A'} - {currentEndTime?.toFixed(2) ?? 'N/A'}
537
- </Typography>
538
- )}
539
-
540
- <Box sx={{
541
- display: 'flex',
542
- flexDirection: isMobile ? 'column' : 'row',
543
- alignItems: isMobile ? 'stretch' : 'center',
544
- gap: isMobile ? 1 : 2
545
- }}>
546
- <TimelineControls
547
- isGlobal={isGlobal}
548
- visibleStartTime={visibleStartTime}
549
- visibleEndTime={visibleEndTime}
550
- startTime={startTime}
551
- endTime={endTime}
552
- zoomLevel={zoomLevel}
553
- autoScrollEnabled={autoScrollEnabled}
554
- currentTime={currentTime}
555
- isManualSyncing={isManualSyncing}
556
- isReplaceAllMode={isReplaceAllMode}
557
- isPaused={isPaused}
558
- onScrollLeft={handleScrollLeft}
559
- onZoomOut={handleZoomOut}
560
- onZoomIn={handleZoomIn}
561
- onScrollRight={handleScrollRight}
562
- onToggleAutoScroll={toggleAutoScroll}
563
- onJumpToCurrentTime={jumpToCurrentTime}
564
- onStartManualSync={startManualSync}
565
- onPauseResume={handlePauseResume}
566
- onStopAudio={onStopAudio}
567
- />
568
- {currentWordInfo && (
569
- <Box sx={{ textAlign: isMobile ? 'center' : 'left' }}>
570
- <Typography variant="body2">
571
- Word {currentWordInfo.index} of {currentWordInfo.total}: <strong>{currentWordInfo.text}</strong>
572
- </Typography>
573
- <Typography variant="caption" color="text.secondary">
574
- {isSpacebarPressed ?
575
- "Holding... Release when word ends" :
576
- (isMobile ? "Tap the button when word starts" : "Press spacebar when word starts (tap for short words, hold for long words)")}
577
- </Typography>
578
- </Box>
579
- )}
580
- {/* Mobile TAP button for manual sync */}
581
- {isMobile && isManualSyncing && onTapStart && onTapEnd && (
582
- <TapButton
583
- isSpacebarPressed={isSpacebarPressed}
584
- onTapStart={onTapStart}
585
- onTapEnd={onTapEnd}
586
- />
587
- )}
588
- </Box>
589
- </Box>
590
- </>
591
- )
592
- }