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.
- backend/Dockerfile.base +1 -0
- backend/api/routes/admin.py +226 -3
- backend/api/routes/push.py +238 -0
- backend/api/routes/users.py +14 -3
- backend/config.py +12 -1
- backend/main.py +2 -1
- backend/models/job.py +4 -0
- backend/models/user.py +20 -2
- backend/services/encoding_interface.py +4 -0
- backend/services/gce_encoding/main.py +22 -8
- backend/services/job_manager.py +68 -11
- backend/services/job_notification_service.py +4 -21
- backend/services/push_notification_service.py +409 -0
- backend/services/stripe_service.py +2 -2
- backend/tests/conftest.py +2 -1
- backend/tests/test_admin_delete_outputs.py +352 -0
- backend/tests/test_gce_encoding_worker.py +229 -0
- backend/tests/test_impersonation.py +18 -3
- backend/tests/test_job_notification_service.py +24 -58
- backend/tests/test_push_notification_service.py +460 -0
- backend/tests/test_push_routes.py +357 -0
- backend/tests/test_stripe_service.py +205 -0
- backend/tests/test_video_worker_orchestrator.py +189 -0
- backend/workers/video_worker_orchestrator.py +23 -0
- karaoke_gen/instrumental_review/server.py +145 -35
- karaoke_gen/nextjs_frontend/__init__.py +98 -0
- karaoke_gen/nextjs_frontend/out/404/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/404.html +1 -0
- karaoke_gen/nextjs_frontend/out/__next.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/__next._full.txt +22 -0
- karaoke_gen/nextjs_frontend/out/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/01a7f8fe40f1ff47.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/112f346e31f991df.js +4 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/16d1a4dd9d8a873a.js +3 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/1ab85c362b8b0e86.js +9 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/247eb132b7f7b574.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/2b80d15cc95e4818.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/32c7eba5cd46c1bc.js +7 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/483f26794eae53d0.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/550c3b02e85f196a.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/55c5ade44387bef8.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/5628d92b5893add2.css +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/56ebf7665e4341c8.js +7 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/5997132b61dec430.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/5ea55255bce3eb9e.js +5 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/5eda89a57490b3cd.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/692f5d9e0d700c76.js +3 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/71d7a05b14f9f0f4.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/81ac355749ef3302.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/95f7e5934dbb0e5d.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/9bce8f19eaa46940.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/a9ed54eed3e14c92.js +2 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/b35cd41238ecfb17.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5bc3c3d5ebd49eb.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/b5c078c08db5ae32.js +5 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/be9c44a178104187.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/c4c840e18cb4861c.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/c645af7d6b65f73e.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/d2c5e2575df784d4.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/d30af02b96d81462.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/d9bdf64f4ec1e9b7.js +7 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/dcde6ed684dacd0e.js +5 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/e422cbe931246000.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/e483af34fc792d38.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/e57422aad6b897da.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/ef02697fb404726a.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/ff1a16fafef87110.js +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/chunks/turbopack-2d9ca3017a9deedf.js +3 -0
- karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_buildManifest.js +11 -0
- karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_clientMiddlewareManifest.json +1 -0
- karaoke_gen/nextjs_frontend/out/_next/static/zpw_-rjFIDV5tlPPtnvRI/_ssgManifest.js +1 -0
- karaoke_gen/nextjs_frontend/out/_not-found/__next._full.txt +18 -0
- karaoke_gen/nextjs_frontend/out/_not-found/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/_not-found/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.__PAGE__.txt +5 -0
- karaoke_gen/nextjs_frontend/out/_not-found/__next._not-found.txt +4 -0
- karaoke_gen/nextjs_frontend/out/_not-found/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/_not-found/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/_not-found/index.txt +18 -0
- karaoke_gen/nextjs_frontend/out/admin/__next._full.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/admin/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/admin/__next.admin.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/__next.admin.txt +7 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/__next._full.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.beta.txt +4 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/__next.admin.txt +7 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/admin/beta/index.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/admin/index.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/__next._full.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.jobs.txt +4 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/__next.admin.txt +7 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/admin/jobs/index.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._full.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.rate-limits.txt +4 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/__next.admin.txt +7 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/admin/rate-limits/index.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/__next._full.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.searches.txt +4 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/__next.admin.txt +7 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/admin/searches/index.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/users/__next._full.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/users/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/admin/users/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/users/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.txt +7 -0
- karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/users/__next.admin.users.txt +4 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._full.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.txt +7 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.detail.txt +4 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/__next.admin.users.txt +4 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/admin/users/detail/index.txt +25 -0
- karaoke_gen/nextjs_frontend/out/admin/users/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/admin/users/index.txt +25 -0
- karaoke_gen/nextjs_frontend/out/app/__next._full.txt +22 -0
- karaoke_gen/nextjs_frontend/out/app/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/app/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/app/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/app/__next.app.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/app/__next.app.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/app/index.txt +22 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next._full.txt +19 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.$oc$slug.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.jobs.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/__next.app.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/index.txt +19 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._full.txt +19 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.$oc$slug.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.jobs.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/__next.app.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/instrumental/index.txt +19 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._full.txt +19 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.__PAGE__.txt +6 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.$oc$slug.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.jobs.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/__next.app.txt +4 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/app/jobs/local/review/index.txt +19 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/__next._full.txt +22 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.txt +4 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/__next.auth.verify.txt +4 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/auth/verify/index.txt +22 -0
- karaoke_gen/nextjs_frontend/out/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/index.txt +22 -0
- karaoke_gen/nextjs_frontend/out/manifest.webmanifest +31 -0
- karaoke_gen/nextjs_frontend/out/order/success/__next._full.txt +22 -0
- karaoke_gen/nextjs_frontend/out/order/success/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/order/success/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/order/success/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/order/success/__next.order.success.txt +4 -0
- karaoke_gen/nextjs_frontend/out/order/success/__next.order.txt +4 -0
- karaoke_gen/nextjs_frontend/out/order/success/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/order/success/index.txt +22 -0
- karaoke_gen/nextjs_frontend/out/payment/success/__next._full.txt +22 -0
- karaoke_gen/nextjs_frontend/out/payment/success/__next._head.txt +8 -0
- karaoke_gen/nextjs_frontend/out/payment/success/__next._index.txt +9 -0
- karaoke_gen/nextjs_frontend/out/payment/success/__next._tree.txt +2 -0
- karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.__PAGE__.txt +9 -0
- karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.success.txt +4 -0
- karaoke_gen/nextjs_frontend/out/payment/success/__next.payment.txt +4 -0
- karaoke_gen/nextjs_frontend/out/payment/success/index.html +1 -0
- karaoke_gen/nextjs_frontend/out/payment/success/index.txt +22 -0
- karaoke_gen/nextjs_frontend/out/screenshots/email-action_reminder.png +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/email-beta_welcome.png +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/email-job_completion.png +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/example-output.avif +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/homepage-full.png +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/homepage-hero.png +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.avif +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/instrumental-review.png +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/job-dashboard.avif +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.avif +0 -0
- karaoke_gen/nextjs_frontend/out/screenshots/lyrics-review.png +0 -0
- karaoke_gen/nextjs_frontend/out/sw.js +183 -0
- karaoke_gen/utils/cli_args.py +3 -3
- karaoke_gen/utils/gen_cli.py +4 -0
- karaoke_gen/utils/remote_cli.py +8 -40
- {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/METADATA +2 -1
- {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/RECORD +244 -131
- {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/WHEEL +1 -1
- lyrics_transcriber/correction/agentic/agent.py +83 -60
- lyrics_transcriber/correction/anchor_sequence.py +48 -3
- lyrics_transcriber/correction/corrector.py +92 -58
- lyrics_transcriber/review/server.py +165 -33
- lyrics_transcriber/utils/tracing.py +214 -0
- karaoke_gen/instrumental_review/static/index.html +0 -1695
- lyrics_transcriber/frontend/.gitignore +0 -24
- lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +0 -935
- lyrics_transcriber/frontend/.yarnrc.yml +0 -3
- lyrics_transcriber/frontend/README.md +0 -50
- lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +0 -210
- lyrics_transcriber/frontend/__init__.py +0 -25
- lyrics_transcriber/frontend/e2e/agentic-corrections.spec.ts +0 -207
- lyrics_transcriber/frontend/e2e/fixtures/agentic-correction-data.json +0 -226
- lyrics_transcriber/frontend/eslint.config.js +0 -28
- lyrics_transcriber/frontend/index.html +0 -22
- lyrics_transcriber/frontend/package-lock.json +0 -4553
- lyrics_transcriber/frontend/package.json +0 -48
- lyrics_transcriber/frontend/playwright.config.ts +0 -69
- lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
- lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
- lyrics_transcriber/frontend/src/App.tsx +0 -243
- lyrics_transcriber/frontend/src/api.ts +0 -262
- lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +0 -111
- lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +0 -114
- lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +0 -204
- lyrics_transcriber/frontend/src/components/AppHeader.tsx +0 -65
- lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +0 -180
- lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +0 -175
- lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +0 -359
- lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +0 -281
- lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +0 -162
- lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +0 -257
- lyrics_transcriber/frontend/src/components/EditActionBar.tsx +0 -94
- lyrics_transcriber/frontend/src/components/EditModal.tsx +0 -720
- lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +0 -592
- lyrics_transcriber/frontend/src/components/EditWordList.tsx +0 -431
- lyrics_transcriber/frontend/src/components/FileUpload.tsx +0 -77
- lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +0 -467
- lyrics_transcriber/frontend/src/components/Header.tsx +0 -520
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +0 -1526
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +0 -216
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +0 -721
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +0 -80
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +0 -999
- lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +0 -51
- lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +0 -127
- lyrics_transcriber/frontend/src/components/ModeSelector.tsx +0 -67
- lyrics_transcriber/frontend/src/components/ModelSelector.tsx +0 -23
- lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +0 -177
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +0 -268
- lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +0 -336
- lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +0 -354
- lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +0 -64
- lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +0 -383
- lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +0 -131
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +0 -266
- lyrics_transcriber/frontend/src/components/WordDivider.tsx +0 -191
- lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +0 -466
- lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +0 -56
- lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +0 -89
- lyrics_transcriber/frontend/src/components/shared/constants.ts +0 -30
- lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +0 -180
- lyrics_transcriber/frontend/src/components/shared/styles.ts +0 -13
- lyrics_transcriber/frontend/src/components/shared/types.js +0 -2
- lyrics_transcriber/frontend/src/components/shared/types.ts +0 -135
- lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +0 -177
- lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +0 -78
- lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +0 -75
- lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +0 -360
- lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +0 -110
- lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +0 -22
- lyrics_transcriber/frontend/src/hooks/useManualSync.ts +0 -537
- lyrics_transcriber/frontend/src/main.tsx +0 -11
- lyrics_transcriber/frontend/src/theme.ts +0 -406
- lyrics_transcriber/frontend/src/types/global.d.ts +0 -9
- lyrics_transcriber/frontend/src/types.js +0 -2
- lyrics_transcriber/frontend/src/types.ts +0 -199
- lyrics_transcriber/frontend/src/validation.ts +0 -132
- lyrics_transcriber/frontend/src/vite-env.d.ts +0 -1
- lyrics_transcriber/frontend/tsconfig.app.json +0 -26
- lyrics_transcriber/frontend/tsconfig.json +0 -25
- lyrics_transcriber/frontend/tsconfig.node.json +0 -23
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +0 -1
- lyrics_transcriber/frontend/update_version.js +0 -11
- lyrics_transcriber/frontend/vite.config.d.ts +0 -2
- lyrics_transcriber/frontend/vite.config.js +0 -15
- lyrics_transcriber/frontend/vite.config.ts +0 -16
- lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
- lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
- lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
- lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js +0 -44465
- lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js.map +0 -1
- lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
- lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
- lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
- lyrics_transcriber/frontend/web_assets/index.html +0 -22
- lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
- lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.svg +0 -5
- lyrics_transcriber/frontend/yarn.lock +0 -3711
- {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/apple-touch-icon.png +0 -0
- {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-16x16.png +0 -0
- {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon-32x32.png +0 -0
- {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/favicon.ico +0 -0
- {lyrics_transcriber/frontend/public → karaoke_gen/nextjs_frontend/out}/nomad-karaoke-logo.svg +0 -0
- /lyrics_transcriber/frontend/public/nomad-karaoke-logo.png → /karaoke_gen/nextjs_frontend/out/nomad-logo.png +0 -0
- {karaoke_gen-0.103.1.dist-info → karaoke_gen-0.107.0.dist-info}/entry_points.txt +0 -0
- {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
|
-
)
|