karaoke-gen 0.90.1__py3-none-any.whl → 0.96.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/.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 +742 -0
- backend/api/routes/audio_search.py +903 -0
- backend/api/routes/auth.py +348 -0
- backend/api/routes/file_upload.py +2076 -0
- backend/api/routes/health.py +344 -0
- backend/api/routes/internal.py +435 -0
- backend/api/routes/jobs.py +1610 -0
- backend/api/routes/review.py +652 -0
- backend/api/routes/themes.py +162 -0
- backend/api/routes/users.py +1014 -0
- backend/config.py +172 -0
- backend/main.py +133 -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 +405 -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 +842 -0
- backend/services/job_notification_service.py +271 -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/packaging_service.py +287 -0
- backend/services/rclone_service.py +106 -0
- backend/services/storage_service.py +209 -0
- backend/services/stripe_service.py +275 -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 +88 -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 +339 -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 +273 -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_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/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 +525 -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.96.0.dist-info}/METADATA +1 -1
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/RECORD +186 -41
- lyrics_transcriber/correction/agentic/providers/config.py +9 -5
- lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +1 -51
- lyrics_transcriber/correction/corrector.py +192 -130
- lyrics_transcriber/correction/operations.py +24 -9
- 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.96.0.dist-info}/WHEEL +0 -0
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/entry_points.txt +0 -0
- {karaoke_gen-0.90.1.dist-info → karaoke_gen-0.96.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -39,6 +39,7 @@ interface HeaderProps {
|
|
|
39
39
|
onFindReplace?: () => void
|
|
40
40
|
onEditAll?: () => void
|
|
41
41
|
onUnCorrectAll?: () => void
|
|
42
|
+
onResetCorrections?: () => void
|
|
42
43
|
onTimingOffset?: () => void
|
|
43
44
|
timingOffsetMs?: number
|
|
44
45
|
onUndo: () => void
|
|
@@ -72,6 +73,7 @@ export default function Header({
|
|
|
72
73
|
onFindReplace,
|
|
73
74
|
onEditAll,
|
|
74
75
|
onUnCorrectAll,
|
|
76
|
+
onResetCorrections,
|
|
75
77
|
onTimingOffset,
|
|
76
78
|
timingOffsetMs = 0,
|
|
77
79
|
onUndo,
|
|
@@ -144,68 +146,44 @@ export default function Header({
|
|
|
144
146
|
</Box>
|
|
145
147
|
)}
|
|
146
148
|
|
|
149
|
+
{/* Review Mode toggle and Load File button */}
|
|
147
150
|
<Box sx={{
|
|
148
151
|
display: 'flex',
|
|
149
|
-
flexDirection: isMobile ? 'column' : 'row',
|
|
150
152
|
gap: 1,
|
|
151
|
-
justifyContent: '
|
|
152
|
-
alignItems:
|
|
153
|
+
justifyContent: 'flex-end',
|
|
154
|
+
alignItems: 'center',
|
|
153
155
|
mb: 1
|
|
154
156
|
}}>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
label={reviewMode ? "Review Mode" : "Review Off"}
|
|
167
|
-
onClick={() => onReviewModeToggle(!reviewMode)}
|
|
168
|
-
color={reviewMode ? "secondary" : "default"}
|
|
169
|
-
variant={reviewMode ? "filled" : "outlined"}
|
|
170
|
-
size="small"
|
|
171
|
-
sx={{
|
|
172
|
-
cursor: 'pointer',
|
|
173
|
-
'& .MuiChip-icon': { fontSize: '1rem' }
|
|
174
|
-
}}
|
|
175
|
-
/>
|
|
176
|
-
</Tooltip>
|
|
177
|
-
)}
|
|
178
|
-
{!isReadOnly && onAnnotationsToggle && (
|
|
179
|
-
<Tooltip title={annotationsEnabled
|
|
180
|
-
? "Click to disable annotation prompts when editing"
|
|
181
|
-
: "Click to enable annotation prompts when editing"
|
|
182
|
-
}>
|
|
183
|
-
<Chip
|
|
184
|
-
icon={<RateReviewIcon />}
|
|
185
|
-
label={annotationsEnabled ? "Feedback On" : "Feedback Off"}
|
|
186
|
-
onClick={() => onAnnotationsToggle(!annotationsEnabled)}
|
|
187
|
-
color={annotationsEnabled ? "primary" : "default"}
|
|
188
|
-
variant={annotationsEnabled ? "filled" : "outlined"}
|
|
189
|
-
size="small"
|
|
190
|
-
sx={{
|
|
191
|
-
cursor: 'pointer',
|
|
192
|
-
'& .MuiChip-icon': { fontSize: '1rem' }
|
|
193
|
-
}}
|
|
194
|
-
/>
|
|
195
|
-
</Tooltip>
|
|
196
|
-
)}
|
|
197
|
-
{isReadOnly && (
|
|
198
|
-
<Button
|
|
199
|
-
variant="outlined"
|
|
157
|
+
{!isReadOnly && isAgenticMode && onReviewModeToggle && (
|
|
158
|
+
<Tooltip title={reviewMode
|
|
159
|
+
? "Hide inline correction actions"
|
|
160
|
+
: "Show inline actions on all corrections for quick review"
|
|
161
|
+
}>
|
|
162
|
+
<Chip
|
|
163
|
+
icon={<VisibilityIcon />}
|
|
164
|
+
label={reviewMode ? "Review Mode" : "Review Off"}
|
|
165
|
+
onClick={() => onReviewModeToggle(!reviewMode)}
|
|
166
|
+
color={reviewMode ? "secondary" : "default"}
|
|
167
|
+
variant={reviewMode ? "filled" : "outlined"}
|
|
200
168
|
size="small"
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
169
|
+
sx={{
|
|
170
|
+
cursor: 'pointer',
|
|
171
|
+
'& .MuiChip-icon': { fontSize: '1rem' }
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
</Tooltip>
|
|
175
|
+
)}
|
|
176
|
+
{isReadOnly && (
|
|
177
|
+
<Button
|
|
178
|
+
variant="outlined"
|
|
179
|
+
size="small"
|
|
180
|
+
startIcon={<UploadFileIcon />}
|
|
181
|
+
onClick={onFileLoad}
|
|
182
|
+
fullWidth={isMobile}
|
|
183
|
+
>
|
|
184
|
+
Load File
|
|
185
|
+
</Button>
|
|
186
|
+
)}
|
|
209
187
|
</Box>
|
|
210
188
|
|
|
211
189
|
<Box sx={{
|
|
@@ -213,12 +191,14 @@ export default function Header({
|
|
|
213
191
|
gap: 1,
|
|
214
192
|
mb: 1,
|
|
215
193
|
flexDirection: isMobile ? 'column' : 'row',
|
|
216
|
-
height: '140px'
|
|
194
|
+
height: isMobile ? 'auto' : '140px',
|
|
195
|
+
minHeight: isMobile ? 'auto' : '140px'
|
|
217
196
|
}}>
|
|
218
197
|
<Box sx={{
|
|
219
|
-
width: '280px',
|
|
198
|
+
width: isMobile ? '100%' : '280px',
|
|
199
|
+
minWidth: isMobile ? '100%' : '280px',
|
|
220
200
|
position: 'relative',
|
|
221
|
-
height: '100%'
|
|
201
|
+
height: isMobile ? 'auto' : '100%'
|
|
222
202
|
}}>
|
|
223
203
|
{isAgenticMode ? (
|
|
224
204
|
<AgenticCorrectionMetrics
|
|
@@ -385,15 +365,30 @@ export default function Header({
|
|
|
385
365
|
}}>
|
|
386
366
|
<Box sx={{
|
|
387
367
|
display: 'flex',
|
|
388
|
-
gap:
|
|
389
|
-
flexDirection:
|
|
390
|
-
|
|
391
|
-
|
|
368
|
+
gap: 0.5,
|
|
369
|
+
flexDirection: 'row',
|
|
370
|
+
flexWrap: 'wrap',
|
|
371
|
+
alignItems: 'center',
|
|
372
|
+
minHeight: '32px'
|
|
392
373
|
}}>
|
|
393
374
|
<ModeSelector
|
|
394
375
|
effectiveMode={effectiveMode}
|
|
395
376
|
onChange={onModeChange}
|
|
396
377
|
/>
|
|
378
|
+
{!isReadOnly && onResetCorrections && (
|
|
379
|
+
<Tooltip title="Undo all your changes">
|
|
380
|
+
<Button
|
|
381
|
+
variant="outlined"
|
|
382
|
+
size="small"
|
|
383
|
+
color="warning"
|
|
384
|
+
onClick={onResetCorrections}
|
|
385
|
+
startIcon={<UndoIcon />}
|
|
386
|
+
sx={{ minWidth: 'fit-content', height: '32px' }}
|
|
387
|
+
>
|
|
388
|
+
Undo All
|
|
389
|
+
</Button>
|
|
390
|
+
</Tooltip>
|
|
391
|
+
)}
|
|
397
392
|
{!isReadOnly && (
|
|
398
393
|
<Box sx={{ display: 'flex', height: '32px' }}>
|
|
399
394
|
<Tooltip title="Undo">
|
|
@@ -457,15 +452,17 @@ export default function Header({
|
|
|
457
452
|
</Button>
|
|
458
453
|
)}
|
|
459
454
|
{!isReadOnly && onUnCorrectAll && (
|
|
460
|
-
<
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
455
|
+
<Tooltip title="Revert only automatic AI corrections (keeps your manual edits)">
|
|
456
|
+
<Button
|
|
457
|
+
variant="outlined"
|
|
458
|
+
size="small"
|
|
459
|
+
onClick={onUnCorrectAll}
|
|
460
|
+
startIcon={<RestoreIcon />}
|
|
461
|
+
sx={{ minWidth: 'fit-content', height: '32px' }}
|
|
462
|
+
>
|
|
463
|
+
Undo Auto Corrections
|
|
464
|
+
</Button>
|
|
465
|
+
</Tooltip>
|
|
469
466
|
)}
|
|
470
467
|
{!isReadOnly && onTimingOffset && (
|
|
471
468
|
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
|
@@ -480,10 +477,10 @@ export default function Header({
|
|
|
480
477
|
Timing Offset
|
|
481
478
|
</Button>
|
|
482
479
|
{timingOffsetMs !== 0 && (
|
|
483
|
-
<Typography
|
|
484
|
-
variant="body2"
|
|
485
|
-
sx={{
|
|
486
|
-
ml: 1,
|
|
480
|
+
<Typography
|
|
481
|
+
variant="body2"
|
|
482
|
+
sx={{
|
|
483
|
+
ml: 1,
|
|
487
484
|
fontWeight: 'bold',
|
|
488
485
|
color: theme.palette.secondary.main
|
|
489
486
|
}}
|
|
@@ -493,6 +490,23 @@ export default function Header({
|
|
|
493
490
|
)}
|
|
494
491
|
</Box>
|
|
495
492
|
)}
|
|
493
|
+
{!isReadOnly && onAnnotationsToggle && (
|
|
494
|
+
<Tooltip title={annotationsEnabled
|
|
495
|
+
? "Click to disable annotation prompts when editing"
|
|
496
|
+
: "Click to enable annotation prompts when editing"
|
|
497
|
+
}>
|
|
498
|
+
<Button
|
|
499
|
+
variant="outlined"
|
|
500
|
+
size="small"
|
|
501
|
+
onClick={() => onAnnotationsToggle(!annotationsEnabled)}
|
|
502
|
+
startIcon={<RateReviewIcon />}
|
|
503
|
+
color={annotationsEnabled ? "primary" : "inherit"}
|
|
504
|
+
sx={{ minWidth: 'fit-content', height: '32px' }}
|
|
505
|
+
>
|
|
506
|
+
{annotationsEnabled ? "Feedback On" : "Feedback Off"}
|
|
507
|
+
</Button>
|
|
508
|
+
</Tooltip>
|
|
509
|
+
)}
|
|
496
510
|
<AudioPlayer
|
|
497
511
|
apiClient={apiClient}
|
|
498
512
|
onTimeUpdate={onTimeUpdate}
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
WordCorrection,
|
|
11
11
|
CorrectionAnnotation
|
|
12
12
|
} from '../types'
|
|
13
|
-
import { Box, Button, Grid, useMediaQuery, useTheme } from '@mui/material'
|
|
13
|
+
import { Box, Button, Grid, Typography, useMediaQuery, useTheme } from '@mui/material'
|
|
14
14
|
import { ApiClient } from '../api'
|
|
15
15
|
import ReferenceView from './ReferenceView'
|
|
16
16
|
import TranscriptionView from './TranscriptionView'
|
|
@@ -33,7 +33,7 @@ import { setupKeyboardHandlers, setModalHandler, getModalState } from './shared/
|
|
|
33
33
|
import Header from './Header'
|
|
34
34
|
import { getWordsFromIds } from './shared/utils/wordUtils'
|
|
35
35
|
import AddLyricsModal from './AddLyricsModal'
|
|
36
|
-
import {
|
|
36
|
+
import { OndemandVideo } from '@mui/icons-material'
|
|
37
37
|
import FindReplaceModal from './FindReplaceModal'
|
|
38
38
|
import TimingOffsetModal from './TimingOffsetModal'
|
|
39
39
|
import { applyOffsetToCorrectionData, applyOffsetToSegment } from './shared/utils/timingUtils'
|
|
@@ -216,6 +216,7 @@ interface MemoizedHeaderProps {
|
|
|
216
216
|
canUndo: boolean
|
|
217
217
|
canRedo: boolean
|
|
218
218
|
onUnCorrectAll: () => void
|
|
219
|
+
onResetCorrections: () => void
|
|
219
220
|
annotationsEnabled: boolean
|
|
220
221
|
onAnnotationsToggle: (enabled: boolean) => void
|
|
221
222
|
// Review mode props
|
|
@@ -250,6 +251,7 @@ const MemoizedHeader = memo(function MemoizedHeader({
|
|
|
250
251
|
canUndo,
|
|
251
252
|
canRedo,
|
|
252
253
|
onUnCorrectAll,
|
|
254
|
+
onResetCorrections,
|
|
253
255
|
annotationsEnabled,
|
|
254
256
|
onAnnotationsToggle,
|
|
255
257
|
reviewMode,
|
|
@@ -281,6 +283,7 @@ const MemoizedHeader = memo(function MemoizedHeader({
|
|
|
281
283
|
canUndo={canUndo}
|
|
282
284
|
canRedo={canRedo}
|
|
283
285
|
onUnCorrectAll={onUnCorrectAll}
|
|
286
|
+
onResetCorrections={onResetCorrections}
|
|
284
287
|
annotationsEnabled={annotationsEnabled}
|
|
285
288
|
onAnnotationsToggle={onAnnotationsToggle}
|
|
286
289
|
reviewMode={reviewMode}
|
|
@@ -1255,8 +1258,6 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
|
|
|
1255
1258
|
|
|
1256
1259
|
return (
|
|
1257
1260
|
<Box sx={{
|
|
1258
|
-
p: 1,
|
|
1259
|
-
pb: 3,
|
|
1260
1261
|
maxWidth: '100%',
|
|
1261
1262
|
overflowX: 'hidden'
|
|
1262
1263
|
}}>
|
|
@@ -1282,6 +1283,7 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
|
|
|
1282
1283
|
canUndo={canUndo}
|
|
1283
1284
|
canRedo={canRedo}
|
|
1284
1285
|
onUnCorrectAll={handleUnCorrectAll}
|
|
1286
|
+
onResetCorrections={handleResetCorrections}
|
|
1285
1287
|
annotationsEnabled={annotationsEnabled}
|
|
1286
1288
|
onAnnotationsToggle={handleAnnotationsToggle}
|
|
1287
1289
|
reviewMode={reviewMode}
|
|
@@ -1291,7 +1293,7 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
|
|
|
1291
1293
|
onRevertAllCorrections={handleRevertAllCorrections}
|
|
1292
1294
|
/>
|
|
1293
1295
|
|
|
1294
|
-
<Grid container direction={isMobile ? 'column' : 'row'}>
|
|
1296
|
+
<Grid container direction={isMobile ? 'column' : 'row'} spacing={1}>
|
|
1295
1297
|
<Grid item xs={12} md={6}>
|
|
1296
1298
|
<MemoizedTranscriptionView
|
|
1297
1299
|
data={displayData}
|
|
@@ -1316,32 +1318,6 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
|
|
|
1316
1318
|
onAcceptCorrection={handleAcceptCorrection}
|
|
1317
1319
|
onShowCorrectionDetail={handleShowCorrectionDetail}
|
|
1318
1320
|
/>
|
|
1319
|
-
{!isReadOnly && apiClient && (
|
|
1320
|
-
<Box sx={{
|
|
1321
|
-
mt: 2,
|
|
1322
|
-
mb: 3,
|
|
1323
|
-
display: 'flex',
|
|
1324
|
-
justifyContent: 'space-between',
|
|
1325
|
-
width: '100%'
|
|
1326
|
-
}}>
|
|
1327
|
-
<Button
|
|
1328
|
-
variant="outlined"
|
|
1329
|
-
color="warning"
|
|
1330
|
-
onClick={handleResetCorrections}
|
|
1331
|
-
startIcon={<RestoreFromTrash />}
|
|
1332
|
-
>
|
|
1333
|
-
Reset Corrections
|
|
1334
|
-
</Button>
|
|
1335
|
-
<Button
|
|
1336
|
-
variant="contained"
|
|
1337
|
-
onClick={handleFinishReview}
|
|
1338
|
-
disabled={isReviewComplete}
|
|
1339
|
-
endIcon={<OndemandVideo />}
|
|
1340
|
-
>
|
|
1341
|
-
{isReviewComplete ? 'Review Complete' : 'Preview Video'}
|
|
1342
|
-
</Button>
|
|
1343
|
-
</Box>
|
|
1344
|
-
)}
|
|
1345
1321
|
</Grid>
|
|
1346
1322
|
<Grid item xs={12} md={6}>
|
|
1347
1323
|
<MemoizedReferenceView
|
|
@@ -1362,6 +1338,52 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
|
|
|
1362
1338
|
</Grid>
|
|
1363
1339
|
</Grid>
|
|
1364
1340
|
|
|
1341
|
+
{/* Spacer for sticky footer */}
|
|
1342
|
+
{!isReadOnly && apiClient && <Box sx={{ height: 64 }} />}
|
|
1343
|
+
|
|
1344
|
+
{/* Sticky footer bar with Preview Video button */}
|
|
1345
|
+
{!isReadOnly && apiClient && (
|
|
1346
|
+
<Box
|
|
1347
|
+
sx={{
|
|
1348
|
+
position: 'fixed',
|
|
1349
|
+
bottom: 0,
|
|
1350
|
+
left: 0,
|
|
1351
|
+
right: 0,
|
|
1352
|
+
bgcolor: 'background.paper',
|
|
1353
|
+
borderTop: 1,
|
|
1354
|
+
borderColor: 'divider',
|
|
1355
|
+
boxShadow: '0 -2px 10px rgba(0,0,0,0.1)',
|
|
1356
|
+
py: 1.5,
|
|
1357
|
+
px: 2,
|
|
1358
|
+
zIndex: 1100,
|
|
1359
|
+
display: 'flex',
|
|
1360
|
+
justifyContent: 'center',
|
|
1361
|
+
alignItems: 'center',
|
|
1362
|
+
gap: 2
|
|
1363
|
+
}}
|
|
1364
|
+
>
|
|
1365
|
+
<Typography
|
|
1366
|
+
variant="body2"
|
|
1367
|
+
sx={{ color: 'text.secondary' }}
|
|
1368
|
+
>
|
|
1369
|
+
Lyrics look good?
|
|
1370
|
+
</Typography>
|
|
1371
|
+
<Button
|
|
1372
|
+
variant="contained"
|
|
1373
|
+
onClick={handleFinishReview}
|
|
1374
|
+
disabled={isReviewComplete}
|
|
1375
|
+
endIcon={<OndemandVideo />}
|
|
1376
|
+
sx={{
|
|
1377
|
+
px: 2.5,
|
|
1378
|
+
py: 0.75,
|
|
1379
|
+
fontWeight: 500
|
|
1380
|
+
}}
|
|
1381
|
+
>
|
|
1382
|
+
{isReviewComplete ? 'Review Complete' : 'Preview Video'}
|
|
1383
|
+
</Button>
|
|
1384
|
+
</Box>
|
|
1385
|
+
)}
|
|
1386
|
+
|
|
1365
1387
|
|
|
1366
1388
|
|
|
1367
1389
|
<EditModal
|
|
@@ -2,8 +2,7 @@ import { memo } from 'react'
|
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
4
|
Button,
|
|
5
|
-
Divider
|
|
6
|
-
Stack
|
|
5
|
+
Divider
|
|
7
6
|
} from '@mui/material'
|
|
8
7
|
import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'
|
|
9
8
|
import StopIcon from '@mui/icons-material/Stop'
|
|
@@ -14,6 +13,7 @@ import EditNoteIcon from '@mui/icons-material/EditNote'
|
|
|
14
13
|
import BlockIcon from '@mui/icons-material/Block'
|
|
15
14
|
import EditIcon from '@mui/icons-material/Edit'
|
|
16
15
|
import DeleteIcon from '@mui/icons-material/Delete'
|
|
16
|
+
import TouchAppIcon from '@mui/icons-material/TouchApp'
|
|
17
17
|
|
|
18
18
|
interface SyncControlsProps {
|
|
19
19
|
// Main buttons
|
|
@@ -24,21 +24,27 @@ interface SyncControlsProps {
|
|
|
24
24
|
onResumeSync: () => void
|
|
25
25
|
onClearSync: () => void
|
|
26
26
|
onEditLyrics: () => void
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
// Playback controls
|
|
29
29
|
onPlay: () => void
|
|
30
30
|
onStop: () => void
|
|
31
31
|
isPlaying: boolean
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
// Word action buttons
|
|
34
34
|
hasSelectedWords: boolean
|
|
35
35
|
selectedWordCount: number
|
|
36
36
|
onUnsyncFromCursor: () => void
|
|
37
37
|
onEditSelectedWord: () => void
|
|
38
38
|
onDeleteSelected: () => void
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
// Additional state
|
|
41
41
|
canUnsyncFromCursor: boolean
|
|
42
|
+
|
|
43
|
+
// Mobile tap support
|
|
44
|
+
isMobile?: boolean
|
|
45
|
+
onTapStart?: () => void
|
|
46
|
+
onTapEnd?: () => void
|
|
47
|
+
isTapping?: boolean
|
|
42
48
|
}
|
|
43
49
|
|
|
44
50
|
const SyncControls = memo(function SyncControls({
|
|
@@ -57,12 +63,16 @@ const SyncControls = memo(function SyncControls({
|
|
|
57
63
|
onUnsyncFromCursor,
|
|
58
64
|
onEditSelectedWord,
|
|
59
65
|
onDeleteSelected,
|
|
60
|
-
canUnsyncFromCursor
|
|
66
|
+
canUnsyncFromCursor,
|
|
67
|
+
isMobile = false,
|
|
68
|
+
onTapStart,
|
|
69
|
+
onTapEnd,
|
|
70
|
+
isTapping = false
|
|
61
71
|
}: SyncControlsProps) {
|
|
62
72
|
return (
|
|
63
|
-
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1
|
|
73
|
+
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
|
64
74
|
{/* Row 1: Playback & Sync Mode Controls */}
|
|
65
|
-
<
|
|
75
|
+
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, alignItems: 'center' }}>
|
|
66
76
|
{/* Playback controls */}
|
|
67
77
|
<Button
|
|
68
78
|
variant="outlined"
|
|
@@ -85,7 +95,7 @@ const SyncControls = memo(function SyncControls({
|
|
|
85
95
|
Stop
|
|
86
96
|
</Button>
|
|
87
97
|
|
|
88
|
-
<Divider orientation="vertical" flexItem
|
|
98
|
+
{!isMobile && <Divider orientation="vertical" flexItem />}
|
|
89
99
|
|
|
90
100
|
{/* Sync Mode controls */}
|
|
91
101
|
{isManualSyncing ? (
|
|
@@ -108,6 +118,27 @@ const SyncControls = memo(function SyncControls({
|
|
|
108
118
|
>
|
|
109
119
|
{isPaused ? 'Resume' : 'Pause'}
|
|
110
120
|
</Button>
|
|
121
|
+
{/* TAP button for mobile - no spacebar on mobile */}
|
|
122
|
+
{isMobile && !isPaused && onTapStart && onTapEnd && (
|
|
123
|
+
<Button
|
|
124
|
+
variant="contained"
|
|
125
|
+
color={isTapping ? 'warning' : 'success'}
|
|
126
|
+
onTouchStart={(e) => {
|
|
127
|
+
e.preventDefault()
|
|
128
|
+
onTapStart()
|
|
129
|
+
}}
|
|
130
|
+
onTouchEnd={(e) => {
|
|
131
|
+
e.preventDefault()
|
|
132
|
+
onTapEnd()
|
|
133
|
+
}}
|
|
134
|
+
onMouseDown={onTapStart}
|
|
135
|
+
onMouseUp={onTapEnd}
|
|
136
|
+
startIcon={<TouchAppIcon />}
|
|
137
|
+
size="small"
|
|
138
|
+
>
|
|
139
|
+
{isTapping ? 'Release' : 'TAP'}
|
|
140
|
+
</Button>
|
|
141
|
+
)}
|
|
111
142
|
</>
|
|
112
143
|
) : (
|
|
113
144
|
<Button
|
|
@@ -120,10 +151,10 @@ const SyncControls = memo(function SyncControls({
|
|
|
120
151
|
Start Sync
|
|
121
152
|
</Button>
|
|
122
153
|
)}
|
|
123
|
-
</
|
|
154
|
+
</Box>
|
|
124
155
|
|
|
125
156
|
{/* Row 2: Editing & Word Actions */}
|
|
126
|
-
<
|
|
157
|
+
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 1, alignItems: 'center' }}>
|
|
127
158
|
<Button
|
|
128
159
|
variant="outlined"
|
|
129
160
|
color="warning"
|
|
@@ -145,7 +176,7 @@ const SyncControls = memo(function SyncControls({
|
|
|
145
176
|
Edit Lyrics
|
|
146
177
|
</Button>
|
|
147
178
|
|
|
148
|
-
<Divider orientation="vertical" flexItem
|
|
179
|
+
{!isMobile && <Divider orientation="vertical" flexItem />}
|
|
149
180
|
|
|
150
181
|
<Button
|
|
151
182
|
variant="outlined"
|
|
@@ -177,7 +208,7 @@ const SyncControls = memo(function SyncControls({
|
|
|
177
208
|
>
|
|
178
209
|
Delete{hasSelectedWords && selectedWordCount > 0 ? ` (${selectedWordCount})` : ''}
|
|
179
210
|
</Button>
|
|
180
|
-
</
|
|
211
|
+
</Box>
|
|
181
212
|
</Box>
|
|
182
213
|
)
|
|
183
214
|
})
|