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.
- backend/api/routes/users.py +14 -3
- backend/config.py +3 -0
- backend/services/encoding_interface.py +4 -0
- backend/services/job_notification_service.py +4 -21
- backend/tests/test_job_notification_service.py +24 -58
- backend/tests/test_video_worker_orchestrator.py +189 -0
- backend/workers/video_worker_orchestrator.py +7 -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.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/METADATA +1 -1
- {karaoke_gen-0.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/RECORD +227 -121
- {karaoke_gen-0.105.4.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 -1721
- 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.105.4.dist-info → karaoke_gen-0.107.0.dist-info}/entry_points.txt +0 -0
- {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
|
-
}
|