karaoke-gen 0.57.0__py3-none-any.whl → 0.71.27__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.
- karaoke_gen/audio_fetcher.py +461 -0
- karaoke_gen/audio_processor.py +407 -30
- karaoke_gen/config.py +62 -113
- karaoke_gen/file_handler.py +32 -59
- karaoke_gen/karaoke_finalise/karaoke_finalise.py +148 -67
- karaoke_gen/karaoke_gen.py +270 -61
- karaoke_gen/lyrics_processor.py +13 -1
- karaoke_gen/metadata.py +78 -73
- karaoke_gen/pipeline/__init__.py +87 -0
- karaoke_gen/pipeline/base.py +215 -0
- karaoke_gen/pipeline/context.py +230 -0
- karaoke_gen/pipeline/executors/__init__.py +21 -0
- karaoke_gen/pipeline/executors/local.py +159 -0
- karaoke_gen/pipeline/executors/remote.py +257 -0
- karaoke_gen/pipeline/stages/__init__.py +27 -0
- karaoke_gen/pipeline/stages/finalize.py +202 -0
- karaoke_gen/pipeline/stages/render.py +165 -0
- karaoke_gen/pipeline/stages/screens.py +139 -0
- karaoke_gen/pipeline/stages/separation.py +191 -0
- karaoke_gen/pipeline/stages/transcription.py +191 -0
- karaoke_gen/style_loader.py +531 -0
- karaoke_gen/utils/bulk_cli.py +6 -0
- karaoke_gen/utils/cli_args.py +424 -0
- karaoke_gen/utils/gen_cli.py +26 -261
- karaoke_gen/utils/remote_cli.py +1965 -0
- karaoke_gen/video_background_processor.py +351 -0
- karaoke_gen-0.71.27.dist-info/METADATA +610 -0
- karaoke_gen-0.71.27.dist-info/RECORD +275 -0
- {karaoke_gen-0.57.0.dist-info → karaoke_gen-0.71.27.dist-info}/WHEEL +1 -1
- {karaoke_gen-0.57.0.dist-info → karaoke_gen-0.71.27.dist-info}/entry_points.txt +1 -0
- lyrics_transcriber/__init__.py +10 -0
- lyrics_transcriber/cli/__init__.py +0 -0
- lyrics_transcriber/cli/cli_main.py +285 -0
- lyrics_transcriber/core/__init__.py +0 -0
- lyrics_transcriber/core/config.py +50 -0
- lyrics_transcriber/core/controller.py +520 -0
- lyrics_transcriber/correction/__init__.py +0 -0
- lyrics_transcriber/correction/agentic/__init__.py +9 -0
- lyrics_transcriber/correction/agentic/adapter.py +71 -0
- lyrics_transcriber/correction/agentic/agent.py +313 -0
- lyrics_transcriber/correction/agentic/feedback/aggregator.py +12 -0
- lyrics_transcriber/correction/agentic/feedback/collector.py +17 -0
- lyrics_transcriber/correction/agentic/feedback/retention.py +24 -0
- lyrics_transcriber/correction/agentic/feedback/store.py +76 -0
- lyrics_transcriber/correction/agentic/handlers/__init__.py +24 -0
- lyrics_transcriber/correction/agentic/handlers/ambiguous.py +44 -0
- lyrics_transcriber/correction/agentic/handlers/background_vocals.py +68 -0
- lyrics_transcriber/correction/agentic/handlers/base.py +51 -0
- lyrics_transcriber/correction/agentic/handlers/complex_multi_error.py +46 -0
- lyrics_transcriber/correction/agentic/handlers/extra_words.py +74 -0
- lyrics_transcriber/correction/agentic/handlers/no_error.py +42 -0
- lyrics_transcriber/correction/agentic/handlers/punctuation.py +44 -0
- lyrics_transcriber/correction/agentic/handlers/registry.py +60 -0
- lyrics_transcriber/correction/agentic/handlers/repeated_section.py +44 -0
- lyrics_transcriber/correction/agentic/handlers/sound_alike.py +126 -0
- lyrics_transcriber/correction/agentic/models/__init__.py +5 -0
- lyrics_transcriber/correction/agentic/models/ai_correction.py +31 -0
- lyrics_transcriber/correction/agentic/models/correction_session.py +30 -0
- lyrics_transcriber/correction/agentic/models/enums.py +38 -0
- lyrics_transcriber/correction/agentic/models/human_feedback.py +30 -0
- lyrics_transcriber/correction/agentic/models/learning_data.py +26 -0
- lyrics_transcriber/correction/agentic/models/observability_metrics.py +28 -0
- lyrics_transcriber/correction/agentic/models/schemas.py +46 -0
- lyrics_transcriber/correction/agentic/models/utils.py +19 -0
- lyrics_transcriber/correction/agentic/observability/__init__.py +5 -0
- lyrics_transcriber/correction/agentic/observability/langfuse_integration.py +35 -0
- lyrics_transcriber/correction/agentic/observability/metrics.py +46 -0
- lyrics_transcriber/correction/agentic/observability/performance.py +19 -0
- lyrics_transcriber/correction/agentic/prompts/__init__.py +2 -0
- lyrics_transcriber/correction/agentic/prompts/classifier.py +227 -0
- lyrics_transcriber/correction/agentic/providers/__init__.py +6 -0
- lyrics_transcriber/correction/agentic/providers/base.py +36 -0
- lyrics_transcriber/correction/agentic/providers/circuit_breaker.py +145 -0
- lyrics_transcriber/correction/agentic/providers/config.py +73 -0
- lyrics_transcriber/correction/agentic/providers/constants.py +24 -0
- lyrics_transcriber/correction/agentic/providers/health.py +28 -0
- lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +212 -0
- lyrics_transcriber/correction/agentic/providers/model_factory.py +209 -0
- lyrics_transcriber/correction/agentic/providers/response_cache.py +218 -0
- lyrics_transcriber/correction/agentic/providers/response_parser.py +111 -0
- lyrics_transcriber/correction/agentic/providers/retry_executor.py +127 -0
- lyrics_transcriber/correction/agentic/router.py +35 -0
- lyrics_transcriber/correction/agentic/workflows/__init__.py +5 -0
- lyrics_transcriber/correction/agentic/workflows/consensus_workflow.py +24 -0
- lyrics_transcriber/correction/agentic/workflows/correction_graph.py +59 -0
- lyrics_transcriber/correction/agentic/workflows/feedback_workflow.py +24 -0
- lyrics_transcriber/correction/anchor_sequence.py +1043 -0
- lyrics_transcriber/correction/corrector.py +760 -0
- lyrics_transcriber/correction/feedback/__init__.py +2 -0
- lyrics_transcriber/correction/feedback/schemas.py +107 -0
- lyrics_transcriber/correction/feedback/store.py +236 -0
- lyrics_transcriber/correction/handlers/__init__.py +0 -0
- lyrics_transcriber/correction/handlers/base.py +52 -0
- lyrics_transcriber/correction/handlers/extend_anchor.py +149 -0
- lyrics_transcriber/correction/handlers/levenshtein.py +189 -0
- lyrics_transcriber/correction/handlers/llm.py +293 -0
- lyrics_transcriber/correction/handlers/llm_providers.py +60 -0
- lyrics_transcriber/correction/handlers/no_space_punct_match.py +154 -0
- lyrics_transcriber/correction/handlers/relaxed_word_count_match.py +85 -0
- lyrics_transcriber/correction/handlers/repeat.py +88 -0
- lyrics_transcriber/correction/handlers/sound_alike.py +259 -0
- lyrics_transcriber/correction/handlers/syllables_match.py +252 -0
- lyrics_transcriber/correction/handlers/word_count_match.py +80 -0
- lyrics_transcriber/correction/handlers/word_operations.py +187 -0
- lyrics_transcriber/correction/operations.py +352 -0
- lyrics_transcriber/correction/phrase_analyzer.py +435 -0
- lyrics_transcriber/correction/text_utils.py +30 -0
- lyrics_transcriber/frontend/.gitignore +23 -0
- lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +935 -0
- lyrics_transcriber/frontend/.yarnrc.yml +3 -0
- lyrics_transcriber/frontend/README.md +50 -0
- lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +210 -0
- lyrics_transcriber/frontend/__init__.py +25 -0
- lyrics_transcriber/frontend/eslint.config.js +28 -0
- lyrics_transcriber/frontend/index.html +18 -0
- lyrics_transcriber/frontend/package.json +42 -0
- lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
- lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
- lyrics_transcriber/frontend/public/apple-touch-icon.png +0 -0
- lyrics_transcriber/frontend/public/favicon-16x16.png +0 -0
- lyrics_transcriber/frontend/public/favicon-32x32.png +0 -0
- lyrics_transcriber/frontend/public/favicon.ico +0 -0
- lyrics_transcriber/frontend/public/nomad-karaoke-logo.png +0 -0
- lyrics_transcriber/frontend/src/App.tsx +212 -0
- lyrics_transcriber/frontend/src/api.ts +239 -0
- lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +77 -0
- lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +114 -0
- lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +204 -0
- lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +180 -0
- lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +167 -0
- lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +359 -0
- lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +281 -0
- lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +162 -0
- lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +257 -0
- lyrics_transcriber/frontend/src/components/EditActionBar.tsx +68 -0
- lyrics_transcriber/frontend/src/components/EditModal.tsx +702 -0
- lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +496 -0
- lyrics_transcriber/frontend/src/components/EditWordList.tsx +379 -0
- lyrics_transcriber/frontend/src/components/FileUpload.tsx +77 -0
- lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +467 -0
- lyrics_transcriber/frontend/src/components/Header.tsx +387 -0
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +1373 -0
- lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +51 -0
- lyrics_transcriber/frontend/src/components/ModeSelector.tsx +67 -0
- lyrics_transcriber/frontend/src/components/ModelSelector.tsx +23 -0
- lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +144 -0
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +268 -0
- lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +688 -0
- lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +354 -0
- lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +64 -0
- lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +376 -0
- lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +131 -0
- lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +256 -0
- lyrics_transcriber/frontend/src/components/WordDivider.tsx +187 -0
- lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +379 -0
- lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +56 -0
- lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +87 -0
- lyrics_transcriber/frontend/src/components/shared/constants.ts +20 -0
- lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +180 -0
- lyrics_transcriber/frontend/src/components/shared/styles.ts +13 -0
- lyrics_transcriber/frontend/src/components/shared/types.js +2 -0
- lyrics_transcriber/frontend/src/components/shared/types.ts +129 -0
- lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +177 -0
- lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +78 -0
- lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +75 -0
- lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +360 -0
- lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +110 -0
- lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +22 -0
- lyrics_transcriber/frontend/src/hooks/useManualSync.ts +435 -0
- lyrics_transcriber/frontend/src/main.tsx +17 -0
- lyrics_transcriber/frontend/src/theme.ts +177 -0
- lyrics_transcriber/frontend/src/types/global.d.ts +9 -0
- lyrics_transcriber/frontend/src/types.js +2 -0
- lyrics_transcriber/frontend/src/types.ts +199 -0
- lyrics_transcriber/frontend/src/validation.ts +132 -0
- lyrics_transcriber/frontend/src/vite-env.d.ts +1 -0
- lyrics_transcriber/frontend/tsconfig.app.json +26 -0
- lyrics_transcriber/frontend/tsconfig.json +25 -0
- lyrics_transcriber/frontend/tsconfig.node.json +23 -0
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -0
- lyrics_transcriber/frontend/update_version.js +11 -0
- lyrics_transcriber/frontend/vite.config.d.ts +2 -0
- lyrics_transcriber/frontend/vite.config.js +10 -0
- lyrics_transcriber/frontend/vite.config.ts +11 -0
- 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-DdJTDWH3.js +42039 -0
- lyrics_transcriber/frontend/web_assets/assets/index-DdJTDWH3.js.map +1 -0
- 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 +18 -0
- lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
- lyrics_transcriber/frontend/yarn.lock +3752 -0
- lyrics_transcriber/lyrics/__init__.py +0 -0
- lyrics_transcriber/lyrics/base_lyrics_provider.py +211 -0
- lyrics_transcriber/lyrics/file_provider.py +95 -0
- lyrics_transcriber/lyrics/genius.py +384 -0
- lyrics_transcriber/lyrics/lrclib.py +231 -0
- lyrics_transcriber/lyrics/musixmatch.py +156 -0
- lyrics_transcriber/lyrics/spotify.py +290 -0
- lyrics_transcriber/lyrics/user_input_provider.py +44 -0
- lyrics_transcriber/output/__init__.py +0 -0
- lyrics_transcriber/output/ass/__init__.py +21 -0
- lyrics_transcriber/output/ass/ass.py +2088 -0
- lyrics_transcriber/output/ass/ass_specs.txt +732 -0
- lyrics_transcriber/output/ass/config.py +180 -0
- lyrics_transcriber/output/ass/constants.py +23 -0
- lyrics_transcriber/output/ass/event.py +94 -0
- lyrics_transcriber/output/ass/formatters.py +132 -0
- lyrics_transcriber/output/ass/lyrics_line.py +265 -0
- lyrics_transcriber/output/ass/lyrics_screen.py +252 -0
- lyrics_transcriber/output/ass/section_detector.py +89 -0
- lyrics_transcriber/output/ass/section_screen.py +106 -0
- lyrics_transcriber/output/ass/style.py +187 -0
- lyrics_transcriber/output/cdg.py +619 -0
- lyrics_transcriber/output/cdgmaker/__init__.py +0 -0
- lyrics_transcriber/output/cdgmaker/cdg.py +262 -0
- lyrics_transcriber/output/cdgmaker/composer.py +2260 -0
- lyrics_transcriber/output/cdgmaker/config.py +151 -0
- lyrics_transcriber/output/cdgmaker/images/instrumental.png +0 -0
- lyrics_transcriber/output/cdgmaker/images/intro.png +0 -0
- lyrics_transcriber/output/cdgmaker/pack.py +507 -0
- lyrics_transcriber/output/cdgmaker/render.py +346 -0
- lyrics_transcriber/output/cdgmaker/transitions/centertexttoplogobottomtext.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/circlein.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/circleout.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/fizzle.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/largecentertexttoplogo.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/rectangle.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/spiral.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/topleftmusicalnotes.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/wipein.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/wipeleft.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/wipeout.png +0 -0
- lyrics_transcriber/output/cdgmaker/transitions/wiperight.png +0 -0
- lyrics_transcriber/output/cdgmaker/utils.py +132 -0
- lyrics_transcriber/output/countdown_processor.py +267 -0
- lyrics_transcriber/output/fonts/AvenirNext-Bold.ttf +0 -0
- lyrics_transcriber/output/fonts/DMSans-VariableFont_opsz,wght.ttf +0 -0
- lyrics_transcriber/output/fonts/DMSerifDisplay-Regular.ttf +0 -0
- lyrics_transcriber/output/fonts/Oswald-SemiBold.ttf +0 -0
- lyrics_transcriber/output/fonts/Zurich_Cn_BT_Bold.ttf +0 -0
- lyrics_transcriber/output/fonts/arial.ttf +0 -0
- lyrics_transcriber/output/fonts/georgia.ttf +0 -0
- lyrics_transcriber/output/fonts/verdana.ttf +0 -0
- lyrics_transcriber/output/generator.py +257 -0
- lyrics_transcriber/output/lrc_to_cdg.py +61 -0
- lyrics_transcriber/output/lyrics_file.py +102 -0
- lyrics_transcriber/output/plain_text.py +96 -0
- lyrics_transcriber/output/segment_resizer.py +431 -0
- lyrics_transcriber/output/subtitles.py +397 -0
- lyrics_transcriber/output/video.py +544 -0
- lyrics_transcriber/review/__init__.py +0 -0
- lyrics_transcriber/review/server.py +676 -0
- lyrics_transcriber/storage/__init__.py +0 -0
- lyrics_transcriber/storage/dropbox.py +225 -0
- lyrics_transcriber/transcribers/__init__.py +0 -0
- lyrics_transcriber/transcribers/audioshake.py +290 -0
- lyrics_transcriber/transcribers/base_transcriber.py +157 -0
- lyrics_transcriber/transcribers/whisper.py +330 -0
- lyrics_transcriber/types.py +648 -0
- lyrics_transcriber/utils/__init__.py +0 -0
- lyrics_transcriber/utils/word_utils.py +27 -0
- karaoke_gen-0.57.0.dist-info/METADATA +0 -167
- karaoke_gen-0.57.0.dist-info/RECORD +0 -23
- {karaoke_gen-0.57.0.dist-info → karaoke_gen-0.71.27.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Shared CLI argument parser for karaoke-gen and karaoke-gen-remote.
|
|
4
|
+
This module provides a common argument parser to ensure feature parity.
|
|
5
|
+
"""
|
|
6
|
+
import argparse
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
9
|
+
import tempfile
|
|
10
|
+
from importlib import metadata
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_version() -> str:
|
|
14
|
+
"""Get package version."""
|
|
15
|
+
try:
|
|
16
|
+
return metadata.version("karaoke-gen")
|
|
17
|
+
except metadata.PackageNotFoundError:
|
|
18
|
+
return "unknown"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def create_parser(prog: str = "karaoke-gen") -> argparse.ArgumentParser:
|
|
22
|
+
"""
|
|
23
|
+
Create the argument parser for karaoke-gen CLIs.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
prog: Program name for the parser
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Configured ArgumentParser instance
|
|
30
|
+
"""
|
|
31
|
+
parser = argparse.ArgumentParser(
|
|
32
|
+
prog=prog,
|
|
33
|
+
description="Generate karaoke videos with synchronized lyrics. Handles the entire process from downloading audio and lyrics to creating the final video.",
|
|
34
|
+
formatter_class=lambda prog: argparse.RawTextHelpFormatter(prog, max_help_position=54),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Basic information
|
|
38
|
+
parser.add_argument(
|
|
39
|
+
"args",
|
|
40
|
+
nargs="*",
|
|
41
|
+
help="[File path] [Artist] [Title] of song to process. If a local audio file is provided, Artist and Title are optional but increase chance of fetching the correct lyrics. If only Artist and Title are provided (no file), audio will be searched and downloaded using flacfetch.",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {get_version()}")
|
|
45
|
+
|
|
46
|
+
# Workflow control
|
|
47
|
+
workflow_group = parser.add_argument_group("Workflow Control")
|
|
48
|
+
workflow_group.add_argument(
|
|
49
|
+
"--prep-only",
|
|
50
|
+
action="store_true",
|
|
51
|
+
help="Only run the preparation phase (download audio, lyrics, separate stems, create title screens). Example: --prep-only",
|
|
52
|
+
)
|
|
53
|
+
workflow_group.add_argument(
|
|
54
|
+
"--finalise-only",
|
|
55
|
+
action="store_true",
|
|
56
|
+
help="Only run the finalisation phase (remux, encode, organize). Must be run in a directory prepared by the prep phase. Example: --finalise-only",
|
|
57
|
+
)
|
|
58
|
+
workflow_group.add_argument(
|
|
59
|
+
"--skip-transcription",
|
|
60
|
+
action="store_true",
|
|
61
|
+
help="Skip automatic lyrics transcription/synchronization. Use this to fall back to manual syncing. Example: --skip-transcription",
|
|
62
|
+
)
|
|
63
|
+
workflow_group.add_argument(
|
|
64
|
+
"--skip-separation",
|
|
65
|
+
action="store_true",
|
|
66
|
+
help="Skip audio separation process. Example: --skip-separation",
|
|
67
|
+
)
|
|
68
|
+
workflow_group.add_argument(
|
|
69
|
+
"--skip-lyrics",
|
|
70
|
+
action="store_true",
|
|
71
|
+
help="Skip fetching and processing lyrics. Example: --skip-lyrics",
|
|
72
|
+
)
|
|
73
|
+
workflow_group.add_argument(
|
|
74
|
+
"--lyrics-only",
|
|
75
|
+
action="store_true",
|
|
76
|
+
help="Only process lyrics, skipping audio separation and title/end screen generation. Example: --lyrics-only",
|
|
77
|
+
)
|
|
78
|
+
workflow_group.add_argument(
|
|
79
|
+
"--edit-lyrics",
|
|
80
|
+
action="store_true",
|
|
81
|
+
help="Edit lyrics of an existing track. This will backup existing outputs, re-run the lyrics transcription process, and update all outputs. Example: --edit-lyrics",
|
|
82
|
+
)
|
|
83
|
+
workflow_group.add_argument(
|
|
84
|
+
"--resume", "-r",
|
|
85
|
+
metavar="JOB_ID",
|
|
86
|
+
help="Resume monitoring an existing job (remote mode only). Example: --resume abc12345",
|
|
87
|
+
)
|
|
88
|
+
workflow_group.add_argument(
|
|
89
|
+
"--cancel",
|
|
90
|
+
metavar="JOB_ID",
|
|
91
|
+
help="Cancel a running job (remote mode only). Stops processing but keeps the job record. Example: --cancel abc12345",
|
|
92
|
+
)
|
|
93
|
+
workflow_group.add_argument(
|
|
94
|
+
"--retry",
|
|
95
|
+
metavar="JOB_ID",
|
|
96
|
+
help="Retry a failed job from the last successful checkpoint (remote mode only). Example: --retry abc12345",
|
|
97
|
+
)
|
|
98
|
+
workflow_group.add_argument(
|
|
99
|
+
"--delete",
|
|
100
|
+
metavar="JOB_ID",
|
|
101
|
+
help="Delete a job and all its files (remote mode only). Permanent removal. Example: --delete abc12345",
|
|
102
|
+
)
|
|
103
|
+
workflow_group.add_argument(
|
|
104
|
+
"--list", "--list-jobs",
|
|
105
|
+
action="store_true",
|
|
106
|
+
dest="list_jobs",
|
|
107
|
+
help="List all jobs (remote mode only). Shows job ID, status, artist, and title. Example: --list",
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Logging & Debugging
|
|
111
|
+
debug_group = parser.add_argument_group("Logging & Debugging")
|
|
112
|
+
debug_group.add_argument(
|
|
113
|
+
"--log_level",
|
|
114
|
+
default="info",
|
|
115
|
+
help="Optional: logging level, e.g. info, debug, warning (default: %(default)s). Example: --log_level=debug",
|
|
116
|
+
)
|
|
117
|
+
debug_group.add_argument(
|
|
118
|
+
"--dry_run",
|
|
119
|
+
action="store_true",
|
|
120
|
+
help="Optional: perform a dry run without making any changes. Example: --dry_run",
|
|
121
|
+
)
|
|
122
|
+
debug_group.add_argument(
|
|
123
|
+
"--render_bounding_boxes",
|
|
124
|
+
action="store_true",
|
|
125
|
+
help="Optional: render bounding boxes around text regions for debugging. Example: --render_bounding_boxes",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Input/Output Configuration
|
|
129
|
+
io_group = parser.add_argument_group("Input/Output Configuration")
|
|
130
|
+
io_group.add_argument(
|
|
131
|
+
"--filename_pattern",
|
|
132
|
+
help="Required if processing a folder: Python regex pattern to extract track names from filenames. Must contain a named group 'title'. Example: --filename_pattern='(?P<index>\\d+) - (?P<title>.+).mp3'",
|
|
133
|
+
)
|
|
134
|
+
io_group.add_argument(
|
|
135
|
+
"--output_dir",
|
|
136
|
+
default=".",
|
|
137
|
+
help="Optional: directory to write output files (default: <current dir>). Example: --output_dir=/app/karaoke",
|
|
138
|
+
)
|
|
139
|
+
io_group.add_argument(
|
|
140
|
+
"--no_track_subfolders",
|
|
141
|
+
action="store_false",
|
|
142
|
+
dest="no_track_subfolders",
|
|
143
|
+
help="Optional: do NOT create a named subfolder for each track. Example: --no_track_subfolders",
|
|
144
|
+
)
|
|
145
|
+
io_group.add_argument(
|
|
146
|
+
"--lossless_output_format",
|
|
147
|
+
default="FLAC",
|
|
148
|
+
help="Optional: lossless output format for separated audio (default: FLAC). Example: --lossless_output_format=WAV",
|
|
149
|
+
)
|
|
150
|
+
io_group.add_argument(
|
|
151
|
+
"--output_png",
|
|
152
|
+
type=lambda x: (str(x).lower() == "true"),
|
|
153
|
+
default=True,
|
|
154
|
+
help="Optional: output PNG format for title and end images (default: %(default)s). Example: --output_png=False",
|
|
155
|
+
)
|
|
156
|
+
io_group.add_argument(
|
|
157
|
+
"--output_jpg",
|
|
158
|
+
type=lambda x: (str(x).lower() == "true"),
|
|
159
|
+
default=True,
|
|
160
|
+
help="Optional: output JPG format for title and end images (default: %(default)s). Example: --output_jpg=False",
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Audio Fetching Configuration (flacfetch)
|
|
164
|
+
fetch_group = parser.add_argument_group("Audio Fetching Configuration")
|
|
165
|
+
fetch_group.add_argument(
|
|
166
|
+
"--auto-download",
|
|
167
|
+
action="store_true",
|
|
168
|
+
help="Optional: Automatically select best audio source when searching by artist/title. When disabled (default), presents options for manual selection. Example: --auto-download",
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Audio Processing Configuration
|
|
172
|
+
audio_group = parser.add_argument_group("Audio Processing Configuration")
|
|
173
|
+
audio_group.add_argument(
|
|
174
|
+
"--clean_instrumental_model",
|
|
175
|
+
default="model_bs_roformer_ep_317_sdr_12.9755.ckpt",
|
|
176
|
+
help="Optional: Model for clean instrumental separation (default: %(default)s).",
|
|
177
|
+
)
|
|
178
|
+
audio_group.add_argument(
|
|
179
|
+
"--backing_vocals_models",
|
|
180
|
+
nargs="+",
|
|
181
|
+
default=["mel_band_roformer_karaoke_aufr33_viperx_sdr_10.1956.ckpt"],
|
|
182
|
+
help="Optional: List of models for backing vocals separation (default: %(default)s).",
|
|
183
|
+
)
|
|
184
|
+
audio_group.add_argument(
|
|
185
|
+
"--other_stems_models",
|
|
186
|
+
nargs="+",
|
|
187
|
+
default=["htdemucs_6s.yaml"],
|
|
188
|
+
help="Optional: List of models for other stems separation (default: %(default)s).",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
default_model_dir_unix = "/tmp/audio-separator-models/"
|
|
192
|
+
if os.name == "posix" and os.path.exists(default_model_dir_unix):
|
|
193
|
+
default_model_dir = default_model_dir_unix
|
|
194
|
+
else:
|
|
195
|
+
default_model_dir = os.path.join(tempfile.gettempdir(), "audio-separator-models")
|
|
196
|
+
|
|
197
|
+
audio_group.add_argument(
|
|
198
|
+
"--model_file_dir",
|
|
199
|
+
default=default_model_dir,
|
|
200
|
+
help="Optional: model files directory (default: %(default)s). Example: --model_file_dir=/app/models",
|
|
201
|
+
)
|
|
202
|
+
audio_group.add_argument(
|
|
203
|
+
"--existing_instrumental",
|
|
204
|
+
help="Optional: Path to an existing instrumental audio file. If provided, audio separation will be skipped.",
|
|
205
|
+
)
|
|
206
|
+
audio_group.add_argument(
|
|
207
|
+
"--instrumental_format",
|
|
208
|
+
default="flac",
|
|
209
|
+
help="Optional: format / file extension for instrumental track to use for remux (default: %(default)s). Example: --instrumental_format=mp3",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# Lyrics Configuration
|
|
213
|
+
lyrics_group = parser.add_argument_group("Lyrics Configuration")
|
|
214
|
+
lyrics_group.add_argument(
|
|
215
|
+
"--lyrics_artist",
|
|
216
|
+
help="Optional: Override the artist name used for lyrics search. Example: --lyrics_artist='The Beatles'",
|
|
217
|
+
)
|
|
218
|
+
lyrics_group.add_argument(
|
|
219
|
+
"--lyrics_title",
|
|
220
|
+
help="Optional: Override the song title used for lyrics search. Example: --lyrics_title='Hey Jude'",
|
|
221
|
+
)
|
|
222
|
+
lyrics_group.add_argument(
|
|
223
|
+
"--lyrics_file",
|
|
224
|
+
help="Optional: Path to a file containing lyrics to use instead of fetching from online. Example: --lyrics_file='/path/to/lyrics.txt'",
|
|
225
|
+
)
|
|
226
|
+
lyrics_group.add_argument(
|
|
227
|
+
"--subtitle_offset_ms",
|
|
228
|
+
type=int,
|
|
229
|
+
default=0,
|
|
230
|
+
help="Optional: Adjust subtitle timing by N milliseconds (+ve delays, -ve advances). Example: --subtitle_offset_ms=500",
|
|
231
|
+
)
|
|
232
|
+
lyrics_group.add_argument(
|
|
233
|
+
"--skip_transcription_review",
|
|
234
|
+
action="store_true",
|
|
235
|
+
help="Optional: Skip the review step after transcription. Example: --skip_transcription_review",
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Style Configuration
|
|
239
|
+
style_group = parser.add_argument_group("Style Configuration")
|
|
240
|
+
style_group.add_argument(
|
|
241
|
+
"--style_params_json",
|
|
242
|
+
help="Optional: Path to JSON file containing style configuration. Example: --style_params_json='/path/to/style_params.json'",
|
|
243
|
+
)
|
|
244
|
+
style_group.add_argument(
|
|
245
|
+
"--style_override",
|
|
246
|
+
action="append",
|
|
247
|
+
help="Optional: Override a style parameter. Can be used multiple times. Example: --style_override 'intro.background_image=/path/to/new_image.png'",
|
|
248
|
+
)
|
|
249
|
+
style_group.add_argument(
|
|
250
|
+
"--background_video",
|
|
251
|
+
help="Optional: Path to video file to use as background instead of static image. Example: --background_video='/path/to/video.mp4'",
|
|
252
|
+
)
|
|
253
|
+
style_group.add_argument(
|
|
254
|
+
"--background_video_darkness",
|
|
255
|
+
type=int,
|
|
256
|
+
default=0,
|
|
257
|
+
help="Optional: Darkness overlay percentage (0-100) for video background (default: %(default)s). Example: --background_video_darkness=50",
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
# Finalisation Configuration
|
|
261
|
+
finalise_group = parser.add_argument_group("Finalisation Configuration")
|
|
262
|
+
finalise_group.add_argument(
|
|
263
|
+
"--enable_cdg",
|
|
264
|
+
action="store_true",
|
|
265
|
+
help="Optional: Enable CDG ZIP generation during finalisation. Example: --enable_cdg",
|
|
266
|
+
)
|
|
267
|
+
finalise_group.add_argument(
|
|
268
|
+
"--enable_txt",
|
|
269
|
+
action="store_true",
|
|
270
|
+
help="Optional: Enable TXT ZIP generation during finalisation. Example: --enable_txt",
|
|
271
|
+
)
|
|
272
|
+
finalise_group.add_argument(
|
|
273
|
+
"--brand_prefix",
|
|
274
|
+
help="Optional: Your brand prefix to calculate the next sequential number. Example: --brand_prefix=BRAND",
|
|
275
|
+
)
|
|
276
|
+
finalise_group.add_argument(
|
|
277
|
+
"--organised_dir",
|
|
278
|
+
help="Optional: Target directory where the processed folder will be moved. Example: --organised_dir='/path/to/Tracks-Organized'",
|
|
279
|
+
)
|
|
280
|
+
finalise_group.add_argument(
|
|
281
|
+
"--organised_dir_rclone_root",
|
|
282
|
+
help="Optional: Rclone path which maps to your organised_dir. Example: --organised_dir_rclone_root='dropbox:Media/Karaoke/Tracks-Organized'",
|
|
283
|
+
)
|
|
284
|
+
finalise_group.add_argument(
|
|
285
|
+
"--public_share_dir",
|
|
286
|
+
help="Optional: Public share directory for final files. Example: --public_share_dir='/path/to/Tracks-PublicShare'",
|
|
287
|
+
)
|
|
288
|
+
finalise_group.add_argument(
|
|
289
|
+
"--enable_youtube_upload",
|
|
290
|
+
action="store_true",
|
|
291
|
+
help="Optional: Enable YouTube upload. For remote mode, uses server-side credentials. Example: --enable_youtube_upload",
|
|
292
|
+
)
|
|
293
|
+
finalise_group.add_argument(
|
|
294
|
+
"--youtube_client_secrets_file",
|
|
295
|
+
help="Optional: Path to youtube client secrets file (local mode only). Example: --youtube_client_secrets_file='/path/to/client_secret.json'",
|
|
296
|
+
)
|
|
297
|
+
finalise_group.add_argument(
|
|
298
|
+
"--youtube_description_file",
|
|
299
|
+
help="Optional: Path to youtube description template. Example: --youtube_description_file='/path/to/description.txt'",
|
|
300
|
+
)
|
|
301
|
+
finalise_group.add_argument(
|
|
302
|
+
"--rclone_destination",
|
|
303
|
+
help="Optional: Rclone destination for public_share_dir sync (local mode). Example: --rclone_destination='googledrive:KaraokeFolder'",
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Native API distribution (for remote CLI - uses server-side credentials)
|
|
307
|
+
finalise_group.add_argument(
|
|
308
|
+
"--dropbox_path",
|
|
309
|
+
help="Optional: Dropbox folder path for organized output (remote mode). Example: --dropbox_path='/Karaoke/Tracks-Organized'",
|
|
310
|
+
)
|
|
311
|
+
finalise_group.add_argument(
|
|
312
|
+
"--gdrive_folder_id",
|
|
313
|
+
help="Optional: Google Drive folder ID for public share uploads (remote mode). Example: --gdrive_folder_id='1abc123xyz'",
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
finalise_group.add_argument(
|
|
317
|
+
"--discord_webhook_url",
|
|
318
|
+
help="Optional: Discord webhook URL for notifications. Example: --discord_webhook_url='https://discord.com/api/webhooks/...'",
|
|
319
|
+
)
|
|
320
|
+
finalise_group.add_argument(
|
|
321
|
+
"--email_template_file",
|
|
322
|
+
help="Optional: Path to email template file. Example: --email_template_file='/path/to/template.txt'",
|
|
323
|
+
)
|
|
324
|
+
finalise_group.add_argument(
|
|
325
|
+
"--keep-brand-code",
|
|
326
|
+
action="store_true",
|
|
327
|
+
help="Optional: Use existing brand code from current directory instead of generating new one. Example: --keep-brand-code",
|
|
328
|
+
)
|
|
329
|
+
finalise_group.add_argument(
|
|
330
|
+
"-y",
|
|
331
|
+
"--yes",
|
|
332
|
+
action="store_true",
|
|
333
|
+
help="Optional: Run in non-interactive mode, assuming yes to all prompts. Example: -y",
|
|
334
|
+
)
|
|
335
|
+
finalise_group.add_argument(
|
|
336
|
+
"--test_email_template",
|
|
337
|
+
action="store_true",
|
|
338
|
+
help="Optional: Test the email template functionality with fake data. Example: --test_email_template",
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
# Remote CLI specific options
|
|
342
|
+
remote_group = parser.add_argument_group("Remote Processing Options (karaoke-gen-remote only)")
|
|
343
|
+
remote_group.add_argument(
|
|
344
|
+
"--service-url",
|
|
345
|
+
default=os.environ.get('KARAOKE_GEN_URL', ''),
|
|
346
|
+
help="Backend service URL (or set KARAOKE_GEN_URL env var). Required for remote mode.",
|
|
347
|
+
)
|
|
348
|
+
remote_group.add_argument(
|
|
349
|
+
"--review-ui-url",
|
|
350
|
+
default=os.environ.get('REVIEW_UI_URL', 'https://lyrics.nomadkaraoke.com'),
|
|
351
|
+
help="Lyrics review UI URL (default: https://lyrics.nomadkaraoke.com)",
|
|
352
|
+
)
|
|
353
|
+
remote_group.add_argument(
|
|
354
|
+
"--poll-interval",
|
|
355
|
+
type=int,
|
|
356
|
+
default=int(os.environ.get('POLL_INTERVAL', '5')),
|
|
357
|
+
help="Seconds between status polls (default: 5)",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Job tracking and filtering options (remote mode)
|
|
361
|
+
tracking_group = parser.add_argument_group("Job Tracking Options (karaoke-gen-remote only)")
|
|
362
|
+
tracking_group.add_argument(
|
|
363
|
+
"--environment",
|
|
364
|
+
default=os.environ.get('KARAOKE_GEN_ENVIRONMENT', ''),
|
|
365
|
+
help="Tag jobs with environment (test/production/development). Sent as X-Environment header. Can also set via KARAOKE_GEN_ENVIRONMENT env var. Example: --environment=test",
|
|
366
|
+
)
|
|
367
|
+
tracking_group.add_argument(
|
|
368
|
+
"--client-id",
|
|
369
|
+
default=os.environ.get('KARAOKE_GEN_CLIENT_ID', ''),
|
|
370
|
+
help="Tag jobs with client identifier for filtering. Sent as X-Client-ID header. Can also set via KARAOKE_GEN_CLIENT_ID env var. Example: --client-id=my-user-id",
|
|
371
|
+
)
|
|
372
|
+
tracking_group.add_argument(
|
|
373
|
+
"--filter-environment",
|
|
374
|
+
help="Filter jobs by environment when using --list. Example: --list --filter-environment=test",
|
|
375
|
+
)
|
|
376
|
+
tracking_group.add_argument(
|
|
377
|
+
"--filter-client-id",
|
|
378
|
+
help="Filter jobs by client ID when using --list. Example: --list --filter-client-id=my-user-id",
|
|
379
|
+
)
|
|
380
|
+
tracking_group.add_argument(
|
|
381
|
+
"--bulk-delete",
|
|
382
|
+
action="store_true",
|
|
383
|
+
help="Delete all jobs matching filters. Requires --filter-environment or --filter-client-id. Example: --bulk-delete --filter-environment=test",
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
return parser
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def process_style_overrides(style_override_list, logger=None):
|
|
390
|
+
"""
|
|
391
|
+
Process style override arguments into a dictionary.
|
|
392
|
+
|
|
393
|
+
Args:
|
|
394
|
+
style_override_list: List of override strings in 'key=value' format
|
|
395
|
+
logger: Optional logger instance
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
Dictionary of style overrides
|
|
399
|
+
|
|
400
|
+
Raises:
|
|
401
|
+
ValueError: If override format is invalid
|
|
402
|
+
"""
|
|
403
|
+
style_overrides = {}
|
|
404
|
+
if style_override_list:
|
|
405
|
+
for override in style_override_list:
|
|
406
|
+
try:
|
|
407
|
+
key, value = override.split("=", 1)
|
|
408
|
+
style_overrides[key] = value
|
|
409
|
+
except ValueError:
|
|
410
|
+
error_msg = f"Invalid style override format: {override}. Must be in 'key=value' format."
|
|
411
|
+
if logger:
|
|
412
|
+
logger.error(error_msg)
|
|
413
|
+
raise ValueError(error_msg)
|
|
414
|
+
return style_overrides
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def is_url(string: str) -> bool:
|
|
418
|
+
"""Simple check to determine if a string is a URL."""
|
|
419
|
+
return string.startswith("http://") or string.startswith("https://")
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def is_file(string: str) -> bool:
|
|
423
|
+
"""Check if a string is a valid file."""
|
|
424
|
+
return os.path.isfile(string)
|