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
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
+
<!-- Load Inter font to match karaoke-gen styling -->
|
|
6
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
7
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
5
9
|
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
|
6
10
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
|
7
11
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
|
@@ -9,8 +13,8 @@
|
|
|
9
13
|
<link rel="icon" type="image/png" sizes="192x192" href="/android-chrome-192x192.png" />
|
|
10
14
|
<link rel="icon" type="image/png" sizes="512x512" href="/android-chrome-512x512.png" />
|
|
11
15
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
12
|
-
<title>Nomad Karaoke: Lyrics Review</title>
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
16
|
+
<title>Nomad Karaoke: Lyrics Transcription Review</title>
|
|
17
|
+
<script type="module" crossorigin src="/assets/index-BSMgOq4Z.js"></script>
|
|
14
18
|
</head>
|
|
15
19
|
<body>
|
|
16
20
|
<div id="root"></div>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="45 60 255 135">
|
|
4
|
+
<g data-v-70b83f88="" fill="#ff7acc" class="basesvg" transform="translate(48.368003845214844,62.62239074707031)"><g fill-rule="" class="tp-name" transform="translate(0,0)"><g transform="scale(1.4000000000000004)"><g stroke="#ff7acc" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" paint-order="stroke" data-gra-attr="stroke" fill-opacity="0"><path d="M1.4-33.44L1.4 0C31.48 0 3.96 0 34.04 0L34.04-32.64 17.81-32.64 17.81-20.61 1.4-33.44ZM52.21-33.76C42.6-33.76 34.81-25.97 34.81-16.37 34.81-6.76 42.6 1.03 52.21 1.03 61.81 1.03 69.6-6.76 69.6-16.37 69.6-25.97 61.81-33.76 52.21-33.76ZM70.7 0L103.34 0 103.25-33.9 86.93-21.54 70.61-33.9 70.7 0ZM102.34 0C138.2 0 107.52 0 143.56 0L122.95-34.93 102.34 0ZM158.84 0.05C167.84 0.05 175.16-7.27 175.16-16.27 175.16-25.27 167.84-32.64 158.84-32.64L142.52-32.64C142.52 0.75 142.52-33.34 142.52 0.05L158.84 0.05Z" transform="translate(-1.399999976158142, 34.93000030517578)"></path></g><g transform="translate(0,38.959999084472656)"><g stroke="#ff7acc" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" paint-order="stroke" data-gra-attr="stroke" fill="#ff7acc" fill-opacity="0" transform="scale(1.02719)"><path d="M24.43 0L17.67 0 7.98-11.87 7.98 0 2.63 0 2.63-26.64 7.98-26.64 7.98-14.69 17.67-26.64 24.12-26.64 13.13-13.44 24.43 0ZM45.03 0L43.27-5.08 32.66-5.08 30.91 0 25.3 0 34.88-26.68 41.1-26.68 50.68 0 45.03 0ZM34.11-9.35L41.82-9.35 37.97-20.5 34.11-9.35ZM73.19 0L67.01 0 61.13-10.38 58.61-10.38 58.61 0 53.27 0 53.27-26.64 63.27-26.64Q66.36-26.64 68.53-25.55 70.71-24.47 71.8-22.61 72.89-20.76 72.89-18.47L72.89-18.47Q72.89-15.84 71.36-13.72 69.83-11.6 66.82-10.8L66.82-10.8 73.19 0ZM58.61-22.21L58.61-14.39 63.08-14.39Q65.25-14.39 66.32-15.44 67.39-16.49 67.39-18.36L67.39-18.36Q67.39-20.19 66.32-21.2 65.25-22.21 63.08-22.21L63.08-22.21 58.61-22.21ZM94.82 0L93.07-5.08 82.46-5.08 80.7 0 75.09 0 84.67-26.68 90.89-26.68 100.47 0 94.82 0ZM83.91-9.35L91.62-9.35 87.76-20.5 83.91-9.35ZM115.43 0.27Q111.69 0.27 108.56-1.49 105.43-3.24 103.6-6.35 101.76-9.47 101.76-13.4L101.76-13.4Q101.76-17.29 103.6-20.4 105.43-23.51 108.56-25.27 111.69-27.02 115.43-27.02L115.43-27.02Q119.21-27.02 122.32-25.27 125.43-23.51 127.24-20.4 129.05-17.29 129.05-13.4L129.05-13.4Q129.05-9.47 127.24-6.35 125.43-3.24 122.3-1.49 119.17 0.27 115.43 0.27L115.43 0.27ZM115.43-4.5Q117.83-4.5 119.66-5.59 121.5-6.68 122.53-8.7 123.56-10.73 123.56-13.4L123.56-13.4Q123.56-16.07 122.53-18.07 121.5-20.08 119.66-21.15 117.83-22.21 115.43-22.21L115.43-22.21Q113.02-22.21 111.17-21.15 109.32-20.08 108.29-18.07 107.26-16.07 107.26-13.4L107.26-13.4Q107.26-10.73 108.29-8.7 109.32-6.68 111.17-5.59 113.02-4.5 115.43-4.5L115.43-4.5ZM153.82 0L147.06 0 137.37-11.87 137.37 0 132.02 0 132.02-26.64 137.37-26.64 137.37-14.69 147.06-26.64 153.51-26.64 142.52-13.44 153.82 0ZM171.79-22.33L161.67-22.33 161.67-15.65 170.64-15.65 170.64-11.41 161.67-11.41 161.67-4.35 171.79-4.35 171.79 0 156.33 0 156.33-26.68 171.79-26.68 171.79-22.33Z" transform="translate(-2.630000114440918, 27.020000457763672)"></path></g></g></g></g> <g data-gra="path-slogan" fill-rule="" class="tp-slogan" fill="#ffdf6b" transform="translate(5,111.788818359375)"><rect x="0" height="1" y="5.9832000732421875" width="14.278396606445312"></rect> <rect height="1" y="5.9832000732421875" width="14.278396606445312" x="218.985595703125"></rect> <g transform="translate(17.278396606445312,0)"><g transform="scale(1.2800000000000002)"><path d="M10.21-8.38L12.02-8.38L9.68 0L7.70 0L6.13-5.96L4.49 0L2.52 0.01L0.26-8.38L2.06-8.38L3.54-1.87L5.24-8.38L7.12-8.38L8.72-1.91L10.21-8.38ZM18.38-8.38L20.06-8.38L20.06 0L18.38 0L18.38-3.56L14.80-3.56L14.80 0L13.12 0L13.12-8.38L14.80-8.38L14.80-4.93L18.38-4.93L18.38-8.38ZM26.58-7.02L23.40-7.02L23.40-4.92L26.22-4.92L26.22-3.59L23.40-3.59L23.40-1.37L26.58-1.37L26.58 0L21.72 0L21.72-8.39L26.58-8.39L26.58-7.02ZM34.37 0L32.42 0L30.58-3.26L29.78-3.26L29.78 0L28.10 0L28.10-8.38L31.25-8.38Q32.22-8.38 32.90-8.03Q33.59-7.69 33.93-7.11Q34.27-6.53 34.27-5.81L34.27-5.81Q34.27-4.98 33.79-4.31Q33.31-3.65 32.36-3.40L32.36-3.40L34.37 0ZM29.78-6.98L29.78-4.52L31.19-4.52Q31.87-4.52 32.21-4.85Q32.54-5.18 32.54-5.77L32.54-5.77Q32.54-6.35 32.21-6.67Q31.87-6.98 31.19-6.98L31.19-6.98L29.78-6.98ZM40.66-7.02L37.48-7.02L37.48-4.92L40.30-4.92L40.30-3.59L37.48-3.59L37.48-1.37L40.66-1.37L40.66 0L35.80 0L35.80-8.39L40.66-8.39L40.66-7.02ZM47.92-8.38L49.70-8.38L46.63 0L44.59 0L41.52-8.38L43.32-8.38L45.62-1.72L47.92-8.38ZM55.57-7.02L52.39-7.02L52.39-4.92L55.21-4.92L55.21-3.59L52.39-3.59L52.39-1.37L55.57-1.37L55.57 0L50.71 0L50.71-8.39L55.57-8.39L55.57-7.02ZM63.36 0L61.42 0L59.57-3.26L58.78-3.26L58.78 0L57.10 0L57.10-8.38L60.24-8.38Q61.21-8.38 61.90-8.03Q62.58-7.69 62.92-7.11Q63.26-6.53 63.26-5.81L63.26-5.81Q63.26-4.98 62.78-4.31Q62.30-3.65 61.36-3.40L61.36-3.40L63.36 0ZM58.78-6.98L58.78-4.52L60.18-4.52Q60.86-4.52 61.20-4.85Q61.54-5.18 61.54-5.77L61.54-5.77Q61.54-6.35 61.20-6.67Q60.86-6.98 60.18-6.98L60.18-6.98L58.78-6.98ZM72.43-8.38L74.30-8.38L71.47-2.92L71.47 0L69.79 0L69.79-2.92L66.95-8.38L68.84-8.38L70.64-4.55L72.43-8.38ZM79.16 0.08Q77.99 0.08 77.00-0.47Q76.02-1.02 75.44-2.00Q74.87-2.98 74.87-4.21L74.87-4.21Q74.87-5.44 75.44-6.41Q76.02-7.39 77.00-7.94Q77.99-8.50 79.16-8.50L79.16-8.50Q80.35-8.50 81.33-7.94Q82.31-7.39 82.88-6.41Q83.45-5.44 83.45-4.21L83.45-4.21Q83.45-2.98 82.88-2.00Q82.31-1.02 81.32-0.47Q80.34 0.08 79.16 0.08L79.16 0.08ZM79.16-1.42Q79.92-1.42 80.50-1.76Q81.07-2.10 81.40-2.74Q81.72-3.37 81.72-4.21L81.72-4.21Q81.72-5.05 81.40-5.68Q81.07-6.31 80.50-6.65Q79.92-6.98 79.16-6.98L79.16-6.98Q78.41-6.98 77.83-6.65Q77.24-6.31 76.92-5.68Q76.60-5.05 76.60-4.21L76.60-4.21Q76.60-3.37 76.92-2.74Q77.24-2.10 77.83-1.76Q78.41-1.42 79.16-1.42L79.16-1.42ZM84.67-8.38L86.35-8.38L86.35-3.19Q86.35-2.34 86.80-1.89Q87.24-1.44 88.04-1.44L88.04-1.44Q88.86-1.44 89.30-1.89Q89.75-2.34 89.75-3.19L89.75-3.19L89.75-8.38L91.44-8.38L91.44-3.20Q91.44-2.14 90.98-1.40Q90.52-0.66 89.74-0.29Q88.97 0.08 88.02 0.08L88.02 0.08Q87.08 0.08 86.32-0.29Q85.56-0.66 85.12-1.40Q84.67-2.14 84.67-3.20L84.67-3.20L84.67-8.38ZM101.60 0L101.05-1.60L97.72-1.60L97.16 0L95.40 0L98.41-8.39L100.37-8.39L103.38 0L101.60 0ZM98.17-2.94L100.60-2.94L99.38-6.44L98.17-2.94ZM110.77 0L108.83 0L106.98-3.26L106.19-3.26L106.19 0L104.51 0L104.51-8.38L107.65-8.38Q108.62-8.38 109.31-8.03Q109.99-7.69 110.33-7.11Q110.68-6.53 110.68-5.81L110.68-5.81Q110.68-4.98 110.20-4.31Q109.72-3.65 108.77-3.40L108.77-3.40L110.77 0ZM106.19-6.98L106.19-4.52L107.59-4.52Q108.28-4.52 108.61-4.85Q108.95-5.18 108.95-5.77L108.95-5.77Q108.95-6.35 108.61-6.67Q108.28-6.98 107.59-6.98L107.59-6.98L106.19-6.98ZM117.06-7.02L113.88-7.02L113.88-4.92L116.70-4.92L116.70-3.59L113.88-3.59L113.88-1.37L117.06-1.37L117.06 0L112.20 0L112.20-8.39L117.06-8.39L117.06-7.02ZM118.72-1.76L120.42-1.76L119.02 1.61L117.94 1.61L118.72-1.76ZM127.38 0.08Q126.50 0.08 125.80-0.22Q125.10-0.52 124.69-1.08Q124.28-1.64 124.27-2.41L124.27-2.41L126.07-2.41Q126.11-1.90 126.44-1.60Q126.77-1.30 127.34-1.30L127.34-1.30Q127.93-1.30 128.27-1.58Q128.60-1.86 128.60-2.32L128.60-2.32Q128.60-2.69 128.38-2.93Q128.15-3.17 127.81-3.31Q127.46-3.44 126.86-3.61L126.86-3.61Q126.05-3.85 125.54-4.09Q125.03-4.32 124.66-4.79Q124.30-5.27 124.30-6.06L124.30-6.06Q124.30-6.80 124.67-7.36Q125.04-7.91 125.71-8.20Q126.38-8.50 127.25-8.50L127.25-8.50Q128.54-8.50 129.35-7.87Q130.16-7.24 130.25-6.11L130.25-6.11L128.40-6.11Q128.38-6.54 128.03-6.82Q127.69-7.10 127.13-7.10L127.13-7.10Q126.64-7.10 126.34-6.85Q126.05-6.60 126.05-6.12L126.05-6.12Q126.05-5.78 126.27-5.56Q126.49-5.34 126.82-5.20Q127.15-5.06 127.75-4.88L127.75-4.88Q128.57-4.64 129.08-4.40Q129.60-4.16 129.97-3.68Q130.34-3.20 130.34-2.42L130.34-2.42Q130.34-1.75 130.00-1.18Q129.65-0.60 128.98-0.26Q128.30 0.08 127.38 0.08L127.38 0.08ZM131.80-8.38L133.48-8.38L133.48 0L131.80 0L131.80-8.38ZM142.30-8.39L142.30 0L140.62 0L136.81-5.75L136.81 0L135.13 0L135.13-8.39L136.81-8.39L140.62-2.63L140.62-8.39L142.30-8.39ZM151.72-5.86L149.78-5.86Q149.50-6.38 148.99-6.66Q148.49-6.94 147.82-6.94L147.82-6.94Q147.07-6.94 146.50-6.60Q145.92-6.26 145.60-5.64Q145.27-5.02 145.27-4.20L145.27-4.20Q145.27-3.36 145.60-2.74Q145.93-2.11 146.52-1.78Q147.11-1.44 147.89-1.44L147.89-1.44Q148.85-1.44 149.46-1.95Q150.07-2.46 150.26-3.37L150.26-3.37L147.38-3.37L147.38-4.66L151.92-4.66L151.92-3.19Q151.75-2.32 151.20-1.57Q150.65-0.83 149.78-0.38Q148.91 0.07 147.83 0.07L147.83 0.07Q146.62 0.07 145.64-0.47Q144.66-1.02 144.10-1.99Q143.54-2.96 143.54-4.20L143.54-4.20Q143.54-5.44 144.10-6.41Q144.66-7.39 145.64-7.94Q146.62-8.48 147.82-8.48L147.82-8.48Q149.23-8.48 150.28-7.79Q151.32-7.10 151.72-5.86L151.72-5.86ZM153.53-8.52L155.34-8.52L155.15-2.72L153.73-2.72L153.53-8.52ZM154.48 0.08Q154.02 0.08 153.73-0.20Q153.43-0.48 153.43-0.90L153.43-0.90Q153.43-1.32 153.73-1.60Q154.02-1.88 154.48-1.88L154.48-1.88Q154.92-1.88 155.21-1.60Q155.50-1.32 155.50-0.90L155.50-0.90Q155.50-0.48 155.21-0.20Q154.92 0.08 154.48 0.08L154.48 0.08Z" transform="translate(-0.264, 8.52)"></path></g></g></g></g>
|
|
5
|
+
</svg>
|
|
@@ -148,8 +148,21 @@ class OutputGenerator:
|
|
|
148
148
|
audio_filepath: str,
|
|
149
149
|
artist: Optional[str] = None,
|
|
150
150
|
title: Optional[str] = None,
|
|
151
|
+
ass_only: bool = False,
|
|
151
152
|
) -> OutputPaths:
|
|
152
|
-
"""Generate all requested output formats.
|
|
153
|
+
"""Generate all requested output formats.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
transcription_corrected: Corrected transcription data
|
|
157
|
+
lyrics_results: Lyrics data from various providers
|
|
158
|
+
output_prefix: Prefix for output filenames
|
|
159
|
+
audio_filepath: Path to audio file
|
|
160
|
+
artist: Optional artist name
|
|
161
|
+
title: Optional title
|
|
162
|
+
ass_only: If True (only in preview_mode), generate only ASS subtitles
|
|
163
|
+
without video encoding. Useful when video encoding is offloaded
|
|
164
|
+
to an external service.
|
|
165
|
+
"""
|
|
153
166
|
outputs = OutputPaths()
|
|
154
167
|
|
|
155
168
|
try:
|
|
@@ -165,8 +178,9 @@ class OutputGenerator:
|
|
|
165
178
|
# Generate ASS subtitles for preview
|
|
166
179
|
outputs.ass = self.subtitle.generate_ass(transcription_corrected.resized_segments, output_prefix, audio_filepath)
|
|
167
180
|
|
|
168
|
-
# Generate preview video
|
|
169
|
-
|
|
181
|
+
# Generate preview video (unless ass_only mode for GCE encoding)
|
|
182
|
+
if not ass_only:
|
|
183
|
+
outputs.video = self.video.generate_preview_video(outputs.ass, audio_filepath, output_prefix)
|
|
170
184
|
|
|
171
185
|
return outputs
|
|
172
186
|
|
|
@@ -249,6 +249,9 @@ class VideoGenerator:
|
|
|
249
249
|
def generate_preview_video(self, ass_path: str, audio_path: str, output_prefix: str) -> str:
|
|
250
250
|
"""Generate lower resolution MP4 preview video with lyrics overlay.
|
|
251
251
|
|
|
252
|
+
Delegates to LocalPreviewEncodingService for the actual encoding to ensure
|
|
253
|
+
consistent preview generation across local CLI, Cloud Run, and GCE worker.
|
|
254
|
+
|
|
252
255
|
Args:
|
|
253
256
|
ass_path: Path to ASS subtitles file
|
|
254
257
|
audio_path: Path to audio file
|
|
@@ -257,6 +260,11 @@ class VideoGenerator:
|
|
|
257
260
|
Returns:
|
|
258
261
|
Path to generated preview video file
|
|
259
262
|
"""
|
|
263
|
+
from backend.services.local_preview_encoding_service import (
|
|
264
|
+
LocalPreviewEncodingService,
|
|
265
|
+
PreviewEncodingConfig,
|
|
266
|
+
)
|
|
267
|
+
|
|
260
268
|
self.logger.info("Generating preview video with lyrics overlay")
|
|
261
269
|
output_path = os.path.join(self.cache_dir, f"{output_prefix}_preview.mp4")
|
|
262
270
|
|
|
@@ -269,17 +277,36 @@ class VideoGenerator:
|
|
|
269
277
|
try:
|
|
270
278
|
# Create a temporary copy of the ASS file with a unique filename
|
|
271
279
|
import time
|
|
280
|
+
import shutil
|
|
272
281
|
|
|
273
282
|
safe_prefix = "".join(c if c.isalnum() else "_" for c in output_prefix)
|
|
274
283
|
timestamp = int(time.time() * 1000)
|
|
275
284
|
temp_ass_path = os.path.join(self.cache_dir, f"temp_preview_subtitles_{safe_prefix}_{timestamp}.ass")
|
|
276
|
-
import shutil
|
|
277
285
|
|
|
278
286
|
shutil.copy2(ass_path, temp_ass_path)
|
|
279
287
|
self.logger.debug(f"Created temporary ASS file: {temp_ass_path}")
|
|
280
288
|
|
|
281
|
-
|
|
282
|
-
self.
|
|
289
|
+
# Get font path from styles configuration
|
|
290
|
+
karaoke_styles = self.styles.get("karaoke", {})
|
|
291
|
+
font_path = karaoke_styles.get("font_path")
|
|
292
|
+
|
|
293
|
+
# Build config for LocalPreviewEncodingService
|
|
294
|
+
config = PreviewEncodingConfig(
|
|
295
|
+
ass_path=temp_ass_path,
|
|
296
|
+
audio_path=audio_path,
|
|
297
|
+
output_path=output_path,
|
|
298
|
+
background_image_path=self.background_image,
|
|
299
|
+
background_color=self.background_color,
|
|
300
|
+
font_path=font_path if font_path and os.path.isfile(font_path) else None,
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Use LocalPreviewEncodingService for consistent encoding
|
|
304
|
+
service = LocalPreviewEncodingService(logger=self.logger)
|
|
305
|
+
result = service.encode_preview(config)
|
|
306
|
+
|
|
307
|
+
if not result.success:
|
|
308
|
+
raise RuntimeError(f"Preview encoding failed: {result.error}")
|
|
309
|
+
|
|
283
310
|
self.logger.info(f"Preview video generated: {output_path}")
|
|
284
311
|
|
|
285
312
|
# Clean up temporary file
|
|
@@ -352,19 +379,44 @@ class VideoGenerator:
|
|
|
352
379
|
self.logger.error(f"Failed to resize background image: {e.output}")
|
|
353
380
|
raise
|
|
354
381
|
|
|
382
|
+
def _escape_ffmpeg_filter_path(self, path: str) -> str:
|
|
383
|
+
"""Escape a path for FFmpeg filter expressions (for subprocess without shell).
|
|
384
|
+
|
|
385
|
+
When using subprocess with a command list (no shell), FFmpeg receives the
|
|
386
|
+
filter string directly. FFmpeg's filter parser requires escaping:
|
|
387
|
+
- Backslashes: double them (\ -> \\)
|
|
388
|
+
- Single quotes/apostrophes: escape with three backslashes (' -> \\')
|
|
389
|
+
- Spaces: escape with backslash ( -> \ )
|
|
390
|
+
|
|
391
|
+
Note: This is different from shell escaping. The '\\'\\''' pattern used for
|
|
392
|
+
shell escaping does NOT work when subprocess passes args directly to FFmpeg.
|
|
393
|
+
|
|
394
|
+
Example: "I'm With You" becomes "I\\\\'m\\ With\\ You"
|
|
395
|
+
"""
|
|
396
|
+
# First escape existing backslashes (\ -> \\)
|
|
397
|
+
escaped = path.replace("\\", "\\\\")
|
|
398
|
+
# Escape single quotes (' -> \\')
|
|
399
|
+
# In the actual string we need 3 backslashes before the quote
|
|
400
|
+
escaped = escaped.replace("'", "\\\\\\'")
|
|
401
|
+
# Escape spaces
|
|
402
|
+
escaped = escaped.replace(" ", "\\ ")
|
|
403
|
+
return escaped
|
|
404
|
+
|
|
355
405
|
def _build_ass_filter(self, ass_path: str) -> str:
|
|
356
406
|
"""Build ASS filter with font directory support."""
|
|
357
|
-
|
|
358
|
-
|
|
407
|
+
escaped_ass_path = self._escape_ffmpeg_filter_path(ass_path)
|
|
408
|
+
ass_filter = f"ass={escaped_ass_path}"
|
|
409
|
+
|
|
359
410
|
# Get font path from styles configuration
|
|
360
411
|
karaoke_styles = self.styles.get("karaoke", {})
|
|
361
412
|
font_path = karaoke_styles.get("font_path")
|
|
362
|
-
|
|
413
|
+
|
|
363
414
|
if font_path and os.path.isfile(font_path):
|
|
364
415
|
font_dir = os.path.dirname(font_path)
|
|
365
|
-
|
|
416
|
+
escaped_font_dir = self._escape_ffmpeg_filter_path(font_dir)
|
|
417
|
+
ass_filter += f":fontsdir={escaped_font_dir}"
|
|
366
418
|
self.logger.info(f"Returning ASS filter with fonts dir: {ass_filter}")
|
|
367
|
-
|
|
419
|
+
|
|
368
420
|
return ass_filter
|
|
369
421
|
|
|
370
422
|
def _build_ffmpeg_command(self, ass_path: str, audio_path: str, output_path: str) -> List[str]:
|
|
@@ -439,93 +491,6 @@ class VideoGenerator:
|
|
|
439
491
|
|
|
440
492
|
return cmd
|
|
441
493
|
|
|
442
|
-
def _build_preview_ffmpeg_command(self, ass_path: str, audio_path: str, output_path: str) -> List[str]:
|
|
443
|
-
"""Build FFmpeg command for preview video generation with hardware acceleration when available."""
|
|
444
|
-
# Use even lower resolution for preview (480x270 instead of 640x360 for faster encoding)
|
|
445
|
-
width, height = 480, 270
|
|
446
|
-
|
|
447
|
-
cmd = [
|
|
448
|
-
"ffmpeg",
|
|
449
|
-
"-hide_banner",
|
|
450
|
-
"-loglevel", "error",
|
|
451
|
-
"-r", "24", # Reduced frame rate to 24 fps for faster encoding
|
|
452
|
-
]
|
|
453
|
-
|
|
454
|
-
# Add hardware acceleration flags if available
|
|
455
|
-
cmd.extend(self.hwaccel_flags)
|
|
456
|
-
|
|
457
|
-
# Input source (background) - simplified for preview
|
|
458
|
-
if self.background_image:
|
|
459
|
-
# For preview, use the original image without resizing to save time
|
|
460
|
-
self.logger.debug(f"Using original background image for preview: {self.background_image}")
|
|
461
|
-
cmd.extend([
|
|
462
|
-
"-loop", "1", # Loop the image
|
|
463
|
-
"-i", self.background_image,
|
|
464
|
-
])
|
|
465
|
-
# Build video filter with scaling and ASS subtitles
|
|
466
|
-
video_filter = f"scale={width}:{height}:force_original_aspect_ratio=decrease,pad={width}:{height}:(ow-iw)/2:(oh-ih)/2,{self._build_ass_filter(ass_path)}"
|
|
467
|
-
else:
|
|
468
|
-
self.logger.debug(
|
|
469
|
-
f"Using solid {self.background_color} background "
|
|
470
|
-
f"with resolution: {width}x{height}"
|
|
471
|
-
)
|
|
472
|
-
cmd.extend([
|
|
473
|
-
"-f", "lavfi",
|
|
474
|
-
"-i", f"color=c={self.background_color}:s={width}x{height}:r=24",
|
|
475
|
-
])
|
|
476
|
-
# Build video filter with just ASS subtitles (no scaling needed)
|
|
477
|
-
video_filter = self._build_ass_filter(ass_path)
|
|
478
|
-
|
|
479
|
-
cmd.extend([
|
|
480
|
-
"-i", audio_path,
|
|
481
|
-
"-vf", video_filter, # Apply the video filter
|
|
482
|
-
"-c:a", "aac", # Use AAC for audio compatibility
|
|
483
|
-
"-b:a", "96k", # Reduced audio bitrate for faster encoding
|
|
484
|
-
"-c:v", self.video_encoder,
|
|
485
|
-
])
|
|
486
|
-
|
|
487
|
-
# Add encoder-specific settings for preview with maximum speed priority
|
|
488
|
-
if self.nvenc_available:
|
|
489
|
-
# NVENC settings optimized for maximum speed
|
|
490
|
-
cmd.extend([
|
|
491
|
-
"-preset", "p1", # Fastest NVENC preset
|
|
492
|
-
"-tune", "ll", # Low latency
|
|
493
|
-
"-rc", "cbr", # Constant bitrate for speed
|
|
494
|
-
"-b:v", "800k", # Lower bitrate for speed
|
|
495
|
-
"-profile:v", "baseline", # Most compatible profile
|
|
496
|
-
"-level", "3.1", # Lower level for speed
|
|
497
|
-
])
|
|
498
|
-
self.logger.debug("Using NVENC encoding with maximum speed settings for preview video generation")
|
|
499
|
-
else:
|
|
500
|
-
# Software encoding with maximum speed priority
|
|
501
|
-
cmd.extend([
|
|
502
|
-
"-profile:v", "baseline", # Most compatible H.264 profile
|
|
503
|
-
"-level", "3.0", # Compatibility level
|
|
504
|
-
"-preset", "superfast", # Even faster than ultrafast for preview
|
|
505
|
-
"-tune", "fastdecode", # Optimize for fast decoding
|
|
506
|
-
"-b:v", "600k", # Lower base bitrate for speed
|
|
507
|
-
"-maxrate", "800k", # Lower max bitrate
|
|
508
|
-
"-bufsize", "1200k", # Smaller buffer size
|
|
509
|
-
"-crf", "28", # Higher CRF for faster encoding (lower quality but faster)
|
|
510
|
-
])
|
|
511
|
-
self.logger.debug("Using software encoding with maximum speed settings for preview video generation")
|
|
512
|
-
|
|
513
|
-
cmd.extend([
|
|
514
|
-
"-pix_fmt", "yuv420p", # Required for browser compatibility
|
|
515
|
-
"-movflags", "+faststart+frag_keyframe+empty_moov+dash", # Enhanced streaming with dash for faster start
|
|
516
|
-
"-g", "48", # Keyframe every 48 frames (2 seconds at 24fps) - fewer keyframes for speed
|
|
517
|
-
"-keyint_min", "48", # Minimum keyframe interval
|
|
518
|
-
"-sc_threshold", "0", # Disable scene change detection for speed
|
|
519
|
-
"-threads", "0", # Use all available CPU threads
|
|
520
|
-
"-shortest", # End encoding after shortest stream
|
|
521
|
-
"-y" # Overwrite output without asking
|
|
522
|
-
])
|
|
523
|
-
|
|
524
|
-
# Add output path
|
|
525
|
-
cmd.append(output_path)
|
|
526
|
-
|
|
527
|
-
return cmd
|
|
528
|
-
|
|
529
494
|
def _get_video_codec(self) -> str:
|
|
530
495
|
"""Determine the best available video codec (legacy method - use video_encoder instead)."""
|
|
531
496
|
# This method is kept for backwards compatibility but is deprecated
|