karaoke-gen 0.90.1__py3-none-any.whl → 0.99.3__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/.coveragerc +20 -0
- backend/.gitignore +37 -0
- backend/Dockerfile +43 -0
- backend/Dockerfile.base +74 -0
- backend/README.md +242 -0
- backend/__init__.py +0 -0
- backend/api/__init__.py +0 -0
- backend/api/dependencies.py +457 -0
- backend/api/routes/__init__.py +0 -0
- backend/api/routes/admin.py +835 -0
- backend/api/routes/audio_search.py +913 -0
- backend/api/routes/auth.py +348 -0
- backend/api/routes/file_upload.py +2112 -0
- backend/api/routes/health.py +409 -0
- backend/api/routes/internal.py +435 -0
- backend/api/routes/jobs.py +1629 -0
- backend/api/routes/review.py +652 -0
- backend/api/routes/themes.py +162 -0
- backend/api/routes/users.py +1513 -0
- backend/config.py +172 -0
- backend/main.py +157 -0
- backend/middleware/__init__.py +5 -0
- backend/middleware/audit_logging.py +124 -0
- backend/models/__init__.py +0 -0
- backend/models/job.py +519 -0
- backend/models/requests.py +123 -0
- backend/models/theme.py +153 -0
- backend/models/user.py +254 -0
- backend/models/worker_log.py +164 -0
- backend/pyproject.toml +29 -0
- backend/quick-check.sh +93 -0
- backend/requirements.txt +29 -0
- backend/run_tests.sh +60 -0
- backend/services/__init__.py +0 -0
- backend/services/audio_analysis_service.py +243 -0
- backend/services/audio_editing_service.py +278 -0
- backend/services/audio_search_service.py +702 -0
- backend/services/auth_service.py +630 -0
- backend/services/credential_manager.py +792 -0
- backend/services/discord_service.py +172 -0
- backend/services/dropbox_service.py +301 -0
- backend/services/email_service.py +1093 -0
- backend/services/encoding_interface.py +454 -0
- backend/services/encoding_service.py +502 -0
- backend/services/firestore_service.py +512 -0
- backend/services/flacfetch_client.py +573 -0
- backend/services/gce_encoding/README.md +72 -0
- backend/services/gce_encoding/__init__.py +22 -0
- backend/services/gce_encoding/main.py +589 -0
- backend/services/gce_encoding/requirements.txt +16 -0
- backend/services/gdrive_service.py +356 -0
- backend/services/job_logging.py +258 -0
- backend/services/job_manager.py +853 -0
- backend/services/job_notification_service.py +271 -0
- backend/services/langfuse_preloader.py +98 -0
- backend/services/local_encoding_service.py +590 -0
- backend/services/local_preview_encoding_service.py +407 -0
- backend/services/lyrics_cache_service.py +216 -0
- backend/services/metrics.py +413 -0
- backend/services/nltk_preloader.py +122 -0
- backend/services/packaging_service.py +287 -0
- backend/services/rclone_service.py +106 -0
- backend/services/spacy_preloader.py +65 -0
- backend/services/storage_service.py +209 -0
- backend/services/stripe_service.py +371 -0
- backend/services/structured_logging.py +254 -0
- backend/services/template_service.py +330 -0
- backend/services/theme_service.py +469 -0
- backend/services/tracing.py +543 -0
- backend/services/user_service.py +721 -0
- backend/services/worker_service.py +558 -0
- backend/services/youtube_service.py +112 -0
- backend/services/youtube_upload_service.py +445 -0
- backend/tests/__init__.py +4 -0
- backend/tests/conftest.py +224 -0
- backend/tests/emulator/__init__.py +7 -0
- backend/tests/emulator/conftest.py +109 -0
- backend/tests/emulator/test_e2e_cli_backend.py +1053 -0
- backend/tests/emulator/test_emulator_integration.py +356 -0
- backend/tests/emulator/test_style_loading_direct.py +436 -0
- backend/tests/emulator/test_worker_logs_direct.py +229 -0
- backend/tests/emulator/test_worker_logs_subcollection.py +443 -0
- backend/tests/requirements-test.txt +10 -0
- backend/tests/requirements.txt +6 -0
- backend/tests/test_admin_email_endpoints.py +411 -0
- backend/tests/test_api_integration.py +460 -0
- backend/tests/test_api_routes.py +93 -0
- backend/tests/test_audio_analysis_service.py +294 -0
- backend/tests/test_audio_editing_service.py +386 -0
- backend/tests/test_audio_search.py +1398 -0
- backend/tests/test_audio_services.py +378 -0
- backend/tests/test_auth_firestore.py +231 -0
- backend/tests/test_config_extended.py +68 -0
- backend/tests/test_credential_manager.py +377 -0
- backend/tests/test_dependencies.py +54 -0
- backend/tests/test_discord_service.py +244 -0
- backend/tests/test_distribution_services.py +820 -0
- backend/tests/test_dropbox_service.py +472 -0
- backend/tests/test_email_service.py +492 -0
- backend/tests/test_emulator_integration.py +322 -0
- backend/tests/test_encoding_interface.py +412 -0
- backend/tests/test_file_upload.py +1739 -0
- backend/tests/test_flacfetch_client.py +632 -0
- backend/tests/test_gdrive_service.py +524 -0
- backend/tests/test_instrumental_api.py +431 -0
- backend/tests/test_internal_api.py +343 -0
- backend/tests/test_job_creation_regression.py +583 -0
- backend/tests/test_job_manager.py +356 -0
- backend/tests/test_job_manager_notifications.py +329 -0
- backend/tests/test_job_notification_service.py +443 -0
- backend/tests/test_jobs_api.py +283 -0
- backend/tests/test_local_encoding_service.py +423 -0
- backend/tests/test_local_preview_encoding_service.py +567 -0
- backend/tests/test_main.py +87 -0
- backend/tests/test_models.py +918 -0
- backend/tests/test_packaging_service.py +382 -0
- backend/tests/test_requests.py +201 -0
- backend/tests/test_routes_jobs.py +282 -0
- backend/tests/test_routes_review.py +337 -0
- backend/tests/test_services.py +556 -0
- backend/tests/test_services_extended.py +112 -0
- backend/tests/test_spacy_preloader.py +119 -0
- backend/tests/test_storage_service.py +448 -0
- backend/tests/test_style_upload.py +261 -0
- backend/tests/test_template_service.py +295 -0
- backend/tests/test_theme_service.py +516 -0
- backend/tests/test_unicode_sanitization.py +522 -0
- backend/tests/test_upload_api.py +256 -0
- backend/tests/test_validate.py +156 -0
- backend/tests/test_video_worker_orchestrator.py +847 -0
- backend/tests/test_worker_log_subcollection.py +509 -0
- backend/tests/test_worker_logging.py +365 -0
- backend/tests/test_workers.py +1116 -0
- backend/tests/test_workers_extended.py +178 -0
- backend/tests/test_youtube_service.py +247 -0
- backend/tests/test_youtube_upload_service.py +568 -0
- backend/utils/test_data.py +27 -0
- backend/validate.py +173 -0
- backend/version.py +27 -0
- backend/workers/README.md +597 -0
- backend/workers/__init__.py +11 -0
- backend/workers/audio_worker.py +618 -0
- backend/workers/lyrics_worker.py +683 -0
- backend/workers/render_video_worker.py +483 -0
- backend/workers/screens_worker.py +535 -0
- backend/workers/style_helper.py +198 -0
- backend/workers/video_worker.py +1277 -0
- backend/workers/video_worker_orchestrator.py +701 -0
- backend/workers/worker_logging.py +278 -0
- karaoke_gen/instrumental_review/static/index.html +7 -4
- karaoke_gen/karaoke_finalise/karaoke_finalise.py +6 -1
- karaoke_gen/utils/__init__.py +163 -8
- karaoke_gen/video_background_processor.py +9 -4
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.99.3.dist-info}/METADATA +1 -1
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.99.3.dist-info}/RECORD +196 -46
- lyrics_transcriber/correction/agentic/agent.py +17 -6
- lyrics_transcriber/correction/agentic/providers/config.py +9 -5
- lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +96 -93
- lyrics_transcriber/correction/agentic/providers/model_factory.py +27 -6
- lyrics_transcriber/correction/anchor_sequence.py +151 -37
- lyrics_transcriber/correction/corrector.py +192 -130
- lyrics_transcriber/correction/handlers/syllables_match.py +44 -2
- lyrics_transcriber/correction/operations.py +24 -9
- lyrics_transcriber/correction/phrase_analyzer.py +18 -0
- lyrics_transcriber/frontend/package-lock.json +2 -2
- lyrics_transcriber/frontend/package.json +1 -1
- lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +1 -1
- lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +11 -7
- lyrics_transcriber/frontend/src/components/EditActionBar.tsx +31 -5
- lyrics_transcriber/frontend/src/components/EditModal.tsx +28 -10
- lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +123 -27
- lyrics_transcriber/frontend/src/components/EditWordList.tsx +112 -60
- lyrics_transcriber/frontend/src/components/Header.tsx +90 -76
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +53 -31
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +44 -13
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +66 -50
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +124 -30
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +1 -1
- lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +12 -5
- lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +3 -3
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +1 -1
- lyrics_transcriber/frontend/src/components/WordDivider.tsx +11 -7
- lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +4 -2
- lyrics_transcriber/frontend/src/hooks/useManualSync.ts +103 -1
- lyrics_transcriber/frontend/src/theme.ts +42 -15
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
- lyrics_transcriber/frontend/vite.config.js +5 -0
- lyrics_transcriber/frontend/web_assets/assets/{index-BECn1o8Q.js → index-BSMgOq4Z.js} +6959 -5782
- lyrics_transcriber/frontend/web_assets/assets/index-BSMgOq4Z.js.map +1 -0
- lyrics_transcriber/frontend/web_assets/index.html +6 -2
- lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.svg +5 -0
- lyrics_transcriber/output/generator.py +17 -3
- lyrics_transcriber/output/video.py +60 -95
- lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js.map +0 -1
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.99.3.dist-info}/WHEEL +0 -0
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.99.3.dist-info}/entry_points.txt +0 -0
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.99.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Box, Button } from '@mui/material'
|
|
1
|
+
import { Box, Button, useMediaQuery, useTheme } from '@mui/material'
|
|
2
2
|
import DeleteIcon from '@mui/icons-material/Delete'
|
|
3
3
|
import RestoreIcon from '@mui/icons-material/RestoreFromTrash'
|
|
4
4
|
import HistoryIcon from '@mui/icons-material/History'
|
|
@@ -25,13 +25,29 @@ export default function EditActionBar({
|
|
|
25
25
|
originalTranscribedSegment,
|
|
26
26
|
isGlobal = false
|
|
27
27
|
}: EditActionBarProps) {
|
|
28
|
+
const theme = useTheme()
|
|
29
|
+
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
|
|
30
|
+
|
|
28
31
|
return (
|
|
29
|
-
<Box sx={{
|
|
30
|
-
|
|
32
|
+
<Box sx={{
|
|
33
|
+
display: 'flex',
|
|
34
|
+
flexDirection: isMobile ? 'column' : 'row',
|
|
35
|
+
alignItems: isMobile ? 'stretch' : 'center',
|
|
36
|
+
gap: 1,
|
|
37
|
+
width: '100%'
|
|
38
|
+
}}>
|
|
39
|
+
<Box sx={{
|
|
40
|
+
display: 'flex',
|
|
41
|
+
alignItems: 'center',
|
|
42
|
+
gap: 1,
|
|
43
|
+
flexWrap: 'wrap',
|
|
44
|
+
justifyContent: isMobile ? 'center' : 'flex-start'
|
|
45
|
+
}}>
|
|
31
46
|
<Button
|
|
32
47
|
startIcon={<RestoreIcon />}
|
|
33
48
|
onClick={onReset}
|
|
34
49
|
color="warning"
|
|
50
|
+
size={isMobile ? 'small' : 'medium'}
|
|
35
51
|
>
|
|
36
52
|
Reset
|
|
37
53
|
</Button>
|
|
@@ -39,6 +55,7 @@ export default function EditActionBar({
|
|
|
39
55
|
<Button
|
|
40
56
|
onClick={onRevertToOriginal}
|
|
41
57
|
startIcon={<HistoryIcon />}
|
|
58
|
+
size={isMobile ? 'small' : 'medium'}
|
|
42
59
|
>
|
|
43
60
|
Un-Correct
|
|
44
61
|
</Button>
|
|
@@ -48,17 +65,26 @@ export default function EditActionBar({
|
|
|
48
65
|
startIcon={<DeleteIcon />}
|
|
49
66
|
onClick={onDelete}
|
|
50
67
|
color="error"
|
|
68
|
+
size={isMobile ? 'small' : 'medium'}
|
|
51
69
|
>
|
|
52
70
|
Delete Segment
|
|
53
71
|
</Button>
|
|
54
72
|
)}
|
|
55
73
|
</Box>
|
|
56
|
-
<Box sx={{
|
|
57
|
-
|
|
74
|
+
<Box sx={{
|
|
75
|
+
ml: isMobile ? 0 : 'auto',
|
|
76
|
+
display: 'flex',
|
|
77
|
+
gap: 1,
|
|
78
|
+
justifyContent: isMobile ? 'center' : 'flex-end'
|
|
79
|
+
}}>
|
|
80
|
+
<Button onClick={onClose} size={isMobile ? 'small' : 'medium'}>
|
|
81
|
+
Cancel
|
|
82
|
+
</Button>
|
|
58
83
|
<Button
|
|
59
84
|
onClick={onSave}
|
|
60
85
|
variant="contained"
|
|
61
86
|
disabled={!editedSegment || editedSegment.words.length === 0}
|
|
87
|
+
size={isMobile ? 'small' : 'medium'}
|
|
62
88
|
>
|
|
63
89
|
Save
|
|
64
90
|
</Button>
|
|
@@ -6,7 +6,9 @@ import {
|
|
|
6
6
|
IconButton,
|
|
7
7
|
Box,
|
|
8
8
|
CircularProgress,
|
|
9
|
-
Typography
|
|
9
|
+
Typography,
|
|
10
|
+
useMediaQuery,
|
|
11
|
+
useTheme
|
|
10
12
|
} from '@mui/material'
|
|
11
13
|
import CloseIcon from '@mui/icons-material/Close'
|
|
12
14
|
import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'
|
|
@@ -33,6 +35,8 @@ interface TimelineSectionProps {
|
|
|
33
35
|
onPlaySegment?: (startTime: number) => void
|
|
34
36
|
startManualSync: () => void
|
|
35
37
|
isGlobal: boolean
|
|
38
|
+
onTapStart?: () => void
|
|
39
|
+
onTapEnd?: () => void
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
const MemoizedTimelineSection = memo(function TimelineSection({
|
|
@@ -47,7 +51,9 @@ const MemoizedTimelineSection = memo(function TimelineSection({
|
|
|
47
51
|
onWordUpdate,
|
|
48
52
|
onPlaySegment,
|
|
49
53
|
startManualSync,
|
|
50
|
-
isGlobal
|
|
54
|
+
isGlobal,
|
|
55
|
+
onTapStart,
|
|
56
|
+
onTapEnd
|
|
51
57
|
}: TimelineSectionProps) {
|
|
52
58
|
return (
|
|
53
59
|
<EditTimelineSection
|
|
@@ -66,6 +72,8 @@ const MemoizedTimelineSection = memo(function TimelineSection({
|
|
|
66
72
|
onPlaySegment={onPlaySegment}
|
|
67
73
|
startManualSync={startManualSync}
|
|
68
74
|
isGlobal={isGlobal}
|
|
75
|
+
onTapStart={onTapStart}
|
|
76
|
+
onTapEnd={onTapEnd}
|
|
69
77
|
/>
|
|
70
78
|
)
|
|
71
79
|
})
|
|
@@ -195,6 +203,9 @@ export default function EditModal({
|
|
|
195
203
|
// hasOriginalTranscribedSegment: !!originalTranscribedSegment
|
|
196
204
|
// });
|
|
197
205
|
|
|
206
|
+
const theme = useTheme()
|
|
207
|
+
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
|
|
208
|
+
|
|
198
209
|
const [editedSegment, setEditedSegment] = useState<LyricsSegment | null>(segment)
|
|
199
210
|
const [isPlaying, setIsPlaying] = useState(false)
|
|
200
211
|
|
|
@@ -224,7 +235,9 @@ export default function EditModal({
|
|
|
224
235
|
startManualSync,
|
|
225
236
|
cleanupManualSync,
|
|
226
237
|
handleSpacebar,
|
|
227
|
-
isSpacebarPressed
|
|
238
|
+
isSpacebarPressed,
|
|
239
|
+
handleTapStart,
|
|
240
|
+
handleTapEnd
|
|
228
241
|
} = useManualSync({
|
|
229
242
|
editedSegment,
|
|
230
243
|
currentTime,
|
|
@@ -588,6 +601,7 @@ export default function EditModal({
|
|
|
588
601
|
onClose={handleClose}
|
|
589
602
|
maxWidth="md"
|
|
590
603
|
fullWidth
|
|
604
|
+
fullScreen={isMobile}
|
|
591
605
|
onKeyDown={(e) => {
|
|
592
606
|
if (e.key === 'Enter' && !e.shiftKey && !isLoading) {
|
|
593
607
|
e.preventDefault()
|
|
@@ -596,8 +610,8 @@ export default function EditModal({
|
|
|
596
610
|
}}
|
|
597
611
|
PaperProps={{
|
|
598
612
|
sx: {
|
|
599
|
-
height: '90vh',
|
|
600
|
-
margin: '5vh 0'
|
|
613
|
+
height: isMobile ? '100%' : '90vh',
|
|
614
|
+
margin: isMobile ? 0 : '5vh 0'
|
|
601
615
|
}
|
|
602
616
|
}}
|
|
603
617
|
>
|
|
@@ -614,17 +628,19 @@ export default function EditModal({
|
|
|
614
628
|
}}
|
|
615
629
|
>
|
|
616
630
|
{isLoading && (
|
|
617
|
-
<Box sx={{
|
|
618
|
-
display: 'flex',
|
|
631
|
+
<Box sx={{
|
|
632
|
+
display: 'flex',
|
|
619
633
|
flexDirection: 'column',
|
|
620
|
-
alignItems: 'center',
|
|
621
|
-
justifyContent: 'center',
|
|
634
|
+
alignItems: 'center',
|
|
635
|
+
justifyContent: 'center',
|
|
622
636
|
height: '100%',
|
|
623
637
|
width: '100%',
|
|
624
638
|
position: 'absolute',
|
|
625
639
|
top: 0,
|
|
626
640
|
left: 0,
|
|
627
|
-
backgroundColor:
|
|
641
|
+
backgroundColor: (theme) => theme.palette.mode === 'dark'
|
|
642
|
+
? 'rgba(30, 41, 59, 0.95)' // slate-800 with opacity for dark mode
|
|
643
|
+
: 'rgba(248, 250, 252, 0.95)', // light background for light mode
|
|
628
644
|
zIndex: 10
|
|
629
645
|
}}>
|
|
630
646
|
<CircularProgress size={60} thickness={4} />
|
|
@@ -652,6 +668,8 @@ export default function EditModal({
|
|
|
652
668
|
onPlaySegment={onPlaySegment}
|
|
653
669
|
startManualSync={startManualSync}
|
|
654
670
|
isGlobal={isGlobal}
|
|
671
|
+
onTapStart={handleTapStart}
|
|
672
|
+
onTapEnd={handleTapEnd}
|
|
655
673
|
/>
|
|
656
674
|
|
|
657
675
|
<MemoizedWordList
|
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
Typography,
|
|
5
5
|
IconButton,
|
|
6
6
|
Tooltip,
|
|
7
|
-
Stack
|
|
7
|
+
Stack,
|
|
8
|
+
useMediaQuery,
|
|
9
|
+
useTheme
|
|
8
10
|
} from '@mui/material'
|
|
9
11
|
import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'
|
|
10
12
|
import CancelIcon from '@mui/icons-material/Cancel'
|
|
@@ -17,10 +19,68 @@ import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline'
|
|
|
17
19
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow'
|
|
18
20
|
import StopIcon from '@mui/icons-material/Stop'
|
|
19
21
|
import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong'
|
|
22
|
+
import TouchAppIcon from '@mui/icons-material/TouchApp'
|
|
20
23
|
import TimelineEditor from './TimelineEditor'
|
|
21
24
|
import { Word } from '../types'
|
|
22
25
|
import { useState, useEffect, useCallback, useRef, useMemo, memo } from 'react'
|
|
23
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
|
+
|
|
24
84
|
interface EditTimelineSectionProps {
|
|
25
85
|
words: Word[]
|
|
26
86
|
startTime: number
|
|
@@ -44,6 +104,8 @@ interface EditTimelineSectionProps {
|
|
|
44
104
|
isGlobal?: boolean
|
|
45
105
|
defaultZoomLevel?: number
|
|
46
106
|
isReplaceAllMode?: boolean
|
|
107
|
+
onTapStart?: () => void
|
|
108
|
+
onTapEnd?: () => void
|
|
47
109
|
}
|
|
48
110
|
|
|
49
111
|
// Memoized control buttons to prevent unnecessary re-renders
|
|
@@ -91,16 +153,21 @@ const TimelineControls = memo(({
|
|
|
91
153
|
onStopAudio?: () => void
|
|
92
154
|
}) => {
|
|
93
155
|
return (
|
|
94
|
-
<Stack
|
|
156
|
+
<Stack
|
|
157
|
+
direction="row"
|
|
158
|
+
spacing={0.5}
|
|
159
|
+
alignItems="center"
|
|
160
|
+
sx={{ flexWrap: 'wrap', justifyContent: 'center', gap: 0.5 }}
|
|
161
|
+
>
|
|
95
162
|
{isGlobal && (
|
|
96
|
-
<>
|
|
163
|
+
<>
|
|
97
164
|
<Tooltip title="Scroll Left">
|
|
98
165
|
<IconButton
|
|
99
166
|
onClick={onScrollLeft}
|
|
100
167
|
disabled={visibleStartTime <= startTime}
|
|
101
168
|
size="small"
|
|
102
169
|
>
|
|
103
|
-
<ArrowBackIcon />
|
|
170
|
+
<ArrowBackIcon fontSize="small" />
|
|
104
171
|
</IconButton>
|
|
105
172
|
</Tooltip>
|
|
106
173
|
<Tooltip title="Zoom Out (Show More Time)">
|
|
@@ -109,7 +176,7 @@ const TimelineControls = memo(({
|
|
|
109
176
|
disabled={zoomLevel >= (endTime - startTime) || (isReplaceAllMode && isManualSyncing && !isPaused)}
|
|
110
177
|
size="small"
|
|
111
178
|
>
|
|
112
|
-
<ZoomOutIcon />
|
|
179
|
+
<ZoomOutIcon fontSize="small" />
|
|
113
180
|
</IconButton>
|
|
114
181
|
</Tooltip>
|
|
115
182
|
<Tooltip title="Zoom In (Show Less Time)">
|
|
@@ -118,7 +185,7 @@ const TimelineControls = memo(({
|
|
|
118
185
|
disabled={zoomLevel <= 2 || (isReplaceAllMode && isManualSyncing && !isPaused)}
|
|
119
186
|
size="small"
|
|
120
187
|
>
|
|
121
|
-
<ZoomInIcon />
|
|
188
|
+
<ZoomInIcon fontSize="small" />
|
|
122
189
|
</IconButton>
|
|
123
190
|
</Tooltip>
|
|
124
191
|
<Tooltip title="Scroll Right">
|
|
@@ -127,7 +194,7 @@ const TimelineControls = memo(({
|
|
|
127
194
|
disabled={visibleEndTime >= endTime}
|
|
128
195
|
size="small"
|
|
129
196
|
>
|
|
130
|
-
<ArrowForwardIcon />
|
|
197
|
+
<ArrowForwardIcon fontSize="small" />
|
|
131
198
|
</IconButton>
|
|
132
199
|
</Tooltip>
|
|
133
200
|
<Tooltip
|
|
@@ -140,7 +207,7 @@ const TimelineControls = memo(({
|
|
|
140
207
|
color={autoScrollEnabled ? "primary" : "default"}
|
|
141
208
|
size="small"
|
|
142
209
|
>
|
|
143
|
-
{autoScrollEnabled ? <AutorenewIcon /> : <PauseCircleOutlineIcon />}
|
|
210
|
+
{autoScrollEnabled ? <AutorenewIcon fontSize="small" /> : <PauseCircleOutlineIcon fontSize="small" />}
|
|
144
211
|
</IconButton>
|
|
145
212
|
</Tooltip>
|
|
146
213
|
<Tooltip title="Jump to Current Playback Position">
|
|
@@ -149,7 +216,7 @@ const TimelineControls = memo(({
|
|
|
149
216
|
disabled={!currentTime}
|
|
150
217
|
size="small"
|
|
151
218
|
>
|
|
152
|
-
<CenterFocusStrongIcon />
|
|
219
|
+
<CenterFocusStrongIcon fontSize="small" />
|
|
153
220
|
</IconButton>
|
|
154
221
|
</Tooltip>
|
|
155
222
|
</>
|
|
@@ -158,26 +225,27 @@ const TimelineControls = memo(({
|
|
|
158
225
|
<Button
|
|
159
226
|
variant="outlined"
|
|
160
227
|
onClick={onStopAudio}
|
|
161
|
-
startIcon={<StopIcon />}
|
|
228
|
+
startIcon={<StopIcon fontSize="small" />}
|
|
162
229
|
color="error"
|
|
163
230
|
size="small"
|
|
164
231
|
>
|
|
165
|
-
Stop
|
|
232
|
+
Stop
|
|
166
233
|
</Button>
|
|
167
234
|
)}
|
|
168
235
|
<Button
|
|
169
236
|
variant={isManualSyncing ? "outlined" : "contained"}
|
|
170
237
|
onClick={onStartManualSync}
|
|
171
|
-
startIcon={isManualSyncing ? <CancelIcon /> : <PlayCircleOutlineIcon />}
|
|
238
|
+
startIcon={isManualSyncing ? <CancelIcon fontSize="small" /> : <PlayCircleOutlineIcon fontSize="small" />}
|
|
172
239
|
color={isManualSyncing ? "error" : "primary"}
|
|
240
|
+
size="small"
|
|
173
241
|
>
|
|
174
|
-
{isManualSyncing ? "Cancel
|
|
242
|
+
{isManualSyncing ? "Cancel" : "Tap To Sync"}
|
|
175
243
|
</Button>
|
|
176
244
|
{isManualSyncing && isReplaceAllMode && (
|
|
177
245
|
<Button
|
|
178
246
|
variant="outlined"
|
|
179
247
|
onClick={onPauseResume}
|
|
180
|
-
startIcon={isPaused ? <PlayArrowIcon /> : <PauseCircleOutlineIcon />}
|
|
248
|
+
startIcon={isPaused ? <PlayArrowIcon fontSize="small" /> : <PauseCircleOutlineIcon fontSize="small" />}
|
|
181
249
|
color={isPaused ? "success" : "warning"}
|
|
182
250
|
size="small"
|
|
183
251
|
>
|
|
@@ -210,8 +278,12 @@ export default function EditTimelineSection({
|
|
|
210
278
|
isPaused = false,
|
|
211
279
|
isGlobal = false,
|
|
212
280
|
defaultZoomLevel = 10,
|
|
213
|
-
isReplaceAllMode = false
|
|
281
|
+
isReplaceAllMode = false,
|
|
282
|
+
onTapStart,
|
|
283
|
+
onTapEnd
|
|
214
284
|
}: EditTimelineSectionProps) {
|
|
285
|
+
const theme = useTheme()
|
|
286
|
+
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
|
|
215
287
|
// Add state for zoom level - use larger default for Replace All mode
|
|
216
288
|
const [zoomLevel, setZoomLevel] = useState(defaultZoomLevel)
|
|
217
289
|
const [visibleStartTime, setVisibleStartTime] = useState(startTime)
|
|
@@ -432,7 +504,7 @@ export default function EditTimelineSection({
|
|
|
432
504
|
return (
|
|
433
505
|
<>
|
|
434
506
|
<Box
|
|
435
|
-
sx={{ height: '120px', mb:
|
|
507
|
+
sx={{ height: isMobile ? '80px' : '120px', mb: 0 }}
|
|
436
508
|
ref={timelineRef}
|
|
437
509
|
onWheel={handleScroll}
|
|
438
510
|
>
|
|
@@ -447,14 +519,30 @@ export default function EditTimelineSection({
|
|
|
447
519
|
/>
|
|
448
520
|
</Box>
|
|
449
521
|
|
|
450
|
-
<Box sx={{
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
|
+
}}>
|
|
458
546
|
<TimelineControls
|
|
459
547
|
isGlobal={isGlobal}
|
|
460
548
|
visibleStartTime={visibleStartTime}
|
|
@@ -478,17 +566,25 @@ export default function EditTimelineSection({
|
|
|
478
566
|
onStopAudio={onStopAudio}
|
|
479
567
|
/>
|
|
480
568
|
{currentWordInfo && (
|
|
481
|
-
<Box>
|
|
569
|
+
<Box sx={{ textAlign: isMobile ? 'center' : 'left' }}>
|
|
482
570
|
<Typography variant="body2">
|
|
483
571
|
Word {currentWordInfo.index} of {currentWordInfo.total}: <strong>{currentWordInfo.text}</strong>
|
|
484
572
|
</Typography>
|
|
485
573
|
<Typography variant="caption" color="text.secondary">
|
|
486
574
|
{isSpacebarPressed ?
|
|
487
|
-
"Holding
|
|
488
|
-
"Press spacebar when word starts (tap for short words, hold for long words)"}
|
|
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)")}
|
|
489
577
|
</Typography>
|
|
490
578
|
</Box>
|
|
491
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
|
+
)}
|
|
492
588
|
</Box>
|
|
493
589
|
</Box>
|
|
494
590
|
</>
|