karaoke-gen 0.75.54__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.
Potentially problematic release.
This version of karaoke-gen might be problematic. Click here for more details.
- karaoke_gen/__init__.py +38 -0
- karaoke_gen/audio_fetcher.py +1614 -0
- karaoke_gen/audio_processor.py +790 -0
- karaoke_gen/config.py +83 -0
- karaoke_gen/file_handler.py +387 -0
- karaoke_gen/instrumental_review/__init__.py +45 -0
- karaoke_gen/instrumental_review/analyzer.py +408 -0
- karaoke_gen/instrumental_review/editor.py +322 -0
- karaoke_gen/instrumental_review/models.py +171 -0
- karaoke_gen/instrumental_review/server.py +475 -0
- karaoke_gen/instrumental_review/static/index.html +1529 -0
- karaoke_gen/instrumental_review/waveform.py +409 -0
- karaoke_gen/karaoke_finalise/__init__.py +1 -0
- karaoke_gen/karaoke_finalise/karaoke_finalise.py +1833 -0
- karaoke_gen/karaoke_gen.py +1026 -0
- karaoke_gen/lyrics_processor.py +474 -0
- karaoke_gen/metadata.py +160 -0
- 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/resources/AvenirNext-Bold.ttf +0 -0
- karaoke_gen/resources/Montserrat-Bold.ttf +0 -0
- karaoke_gen/resources/Oswald-Bold.ttf +0 -0
- karaoke_gen/resources/Oswald-SemiBold.ttf +0 -0
- karaoke_gen/resources/Zurich_Cn_BT_Bold.ttf +0 -0
- karaoke_gen/style_loader.py +531 -0
- karaoke_gen/utils/__init__.py +18 -0
- karaoke_gen/utils/bulk_cli.py +492 -0
- karaoke_gen/utils/cli_args.py +432 -0
- karaoke_gen/utils/gen_cli.py +978 -0
- karaoke_gen/utils/remote_cli.py +3268 -0
- karaoke_gen/video_background_processor.py +351 -0
- karaoke_gen/video_generator.py +424 -0
- karaoke_gen-0.75.54.dist-info/METADATA +718 -0
- karaoke_gen-0.75.54.dist-info/RECORD +287 -0
- karaoke_gen-0.75.54.dist-info/WHEEL +4 -0
- karaoke_gen-0.75.54.dist-info/entry_points.txt +5 -0
- karaoke_gen-0.75.54.dist-info/licenses/LICENSE +21 -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 +594 -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 +919 -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 +214 -0
- lyrics_transcriber/frontend/src/api.ts +254 -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 +413 -0
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +1387 -0
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +185 -0
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +704 -0
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +80 -0
- lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +905 -0
- lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +51 -0
- lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +127 -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 +336 -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-BECn1o8Q.js +43288 -0
- lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.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 +306 -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 +379 -0
- lyrics_transcriber/transcribers/base_transcriber.py +157 -0
- lyrics_transcriber/transcribers/whisper.py +330 -0
- lyrics_transcriber/types.py +650 -0
- lyrics_transcriber/utils/__init__.py +0 -0
- lyrics_transcriber/utils/word_utils.py +27 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared pipeline architecture for karaoke-gen.
|
|
3
|
+
|
|
4
|
+
This module provides a unified pipeline abstraction that can be executed
|
|
5
|
+
either locally (in-process) or remotely (via backend workers).
|
|
6
|
+
|
|
7
|
+
The pipeline consists of stages:
|
|
8
|
+
- Separation: Audio separation into stems
|
|
9
|
+
- Transcription: Lyrics transcription and synchronization
|
|
10
|
+
- Screens: Title and end screen generation
|
|
11
|
+
- Render: Video rendering with synchronized lyrics
|
|
12
|
+
- Finalize: Encoding, packaging, and distribution
|
|
13
|
+
|
|
14
|
+
Each stage can be executed by either a LocalExecutor or RemoteExecutor,
|
|
15
|
+
allowing the same pipeline definition to work in both CLI and cloud contexts.
|
|
16
|
+
|
|
17
|
+
Example usage:
|
|
18
|
+
from karaoke_gen.pipeline import (
|
|
19
|
+
PipelineContext,
|
|
20
|
+
LocalExecutor,
|
|
21
|
+
SeparationStage,
|
|
22
|
+
TranscriptionStage,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Create context
|
|
26
|
+
context = PipelineContext(
|
|
27
|
+
job_id="my-job",
|
|
28
|
+
artist="Artist Name",
|
|
29
|
+
title="Song Title",
|
|
30
|
+
input_audio_path="/path/to/audio.flac",
|
|
31
|
+
output_dir="/path/to/output",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Create stages
|
|
35
|
+
stages = [
|
|
36
|
+
SeparationStage(),
|
|
37
|
+
TranscriptionStage(),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# Run pipeline
|
|
41
|
+
executor = LocalExecutor()
|
|
42
|
+
results = await executor.run_pipeline(stages, context)
|
|
43
|
+
"""
|
|
44
|
+
from karaoke_gen.pipeline.base import (
|
|
45
|
+
PipelineStage,
|
|
46
|
+
PipelineExecutor,
|
|
47
|
+
StageResult,
|
|
48
|
+
StageStatus,
|
|
49
|
+
)
|
|
50
|
+
from karaoke_gen.pipeline.context import PipelineContext
|
|
51
|
+
|
|
52
|
+
# Import stages
|
|
53
|
+
from karaoke_gen.pipeline.stages import (
|
|
54
|
+
SeparationStage,
|
|
55
|
+
TranscriptionStage,
|
|
56
|
+
ScreensStage,
|
|
57
|
+
RenderStage,
|
|
58
|
+
FinalizeStage,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Import executors
|
|
62
|
+
from karaoke_gen.pipeline.executors import (
|
|
63
|
+
LocalExecutor,
|
|
64
|
+
RemoteExecutor,
|
|
65
|
+
create_local_executor,
|
|
66
|
+
create_remote_executor,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
__all__ = [
|
|
70
|
+
# Base classes
|
|
71
|
+
"PipelineStage",
|
|
72
|
+
"PipelineExecutor",
|
|
73
|
+
"StageResult",
|
|
74
|
+
"StageStatus",
|
|
75
|
+
"PipelineContext",
|
|
76
|
+
# Stages
|
|
77
|
+
"SeparationStage",
|
|
78
|
+
"TranscriptionStage",
|
|
79
|
+
"ScreensStage",
|
|
80
|
+
"RenderStage",
|
|
81
|
+
"FinalizeStage",
|
|
82
|
+
# Executors
|
|
83
|
+
"LocalExecutor",
|
|
84
|
+
"RemoteExecutor",
|
|
85
|
+
"create_local_executor",
|
|
86
|
+
"create_remote_executor",
|
|
87
|
+
]
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base classes and protocols for the pipeline architecture.
|
|
3
|
+
|
|
4
|
+
This module defines the interfaces that all pipeline stages must implement,
|
|
5
|
+
ensuring consistent behavior across different execution contexts.
|
|
6
|
+
"""
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from karaoke_gen.pipeline.context import PipelineContext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StageStatus(str, Enum):
|
|
17
|
+
"""Status of a pipeline stage execution."""
|
|
18
|
+
PENDING = "pending"
|
|
19
|
+
RUNNING = "running"
|
|
20
|
+
COMPLETED = "completed"
|
|
21
|
+
FAILED = "failed"
|
|
22
|
+
SKIPPED = "skipped"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class StageResult:
|
|
27
|
+
"""
|
|
28
|
+
Result of a pipeline stage execution.
|
|
29
|
+
|
|
30
|
+
Contains the outputs produced by the stage, any error information,
|
|
31
|
+
and metadata about the execution.
|
|
32
|
+
"""
|
|
33
|
+
status: StageStatus
|
|
34
|
+
outputs: Dict[str, Any] = field(default_factory=dict)
|
|
35
|
+
error_message: Optional[str] = None
|
|
36
|
+
error_details: Optional[Dict[str, Any]] = None
|
|
37
|
+
duration_seconds: Optional[float] = None
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def success(self) -> bool:
|
|
41
|
+
"""Check if the stage completed successfully."""
|
|
42
|
+
return self.status == StageStatus.COMPLETED
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def failed(self) -> bool:
|
|
46
|
+
"""Check if the stage failed."""
|
|
47
|
+
return self.status == StageStatus.FAILED
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class PipelineStage(ABC):
|
|
51
|
+
"""
|
|
52
|
+
Abstract base class for pipeline stages.
|
|
53
|
+
|
|
54
|
+
Each stage represents a discrete unit of work in the karaoke generation
|
|
55
|
+
pipeline. Stages declare their dependencies (required inputs) and outputs,
|
|
56
|
+
allowing the pipeline executor to validate data flow.
|
|
57
|
+
|
|
58
|
+
Stages should be stateless - all state is passed through PipelineContext.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def name(self) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Unique name for this stage.
|
|
66
|
+
|
|
67
|
+
Used for logging, progress tracking, and output dictionary keys.
|
|
68
|
+
"""
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def required_inputs(self) -> List[str]:
|
|
73
|
+
"""
|
|
74
|
+
List of required input keys from previous stages.
|
|
75
|
+
|
|
76
|
+
Override this to declare dependencies on outputs from other stages.
|
|
77
|
+
The pipeline executor will validate that these keys exist in
|
|
78
|
+
context.stage_outputs before running this stage.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
List of required input keys (empty by default)
|
|
82
|
+
"""
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def optional_inputs(self) -> List[str]:
|
|
87
|
+
"""
|
|
88
|
+
List of optional input keys from previous stages.
|
|
89
|
+
|
|
90
|
+
These inputs will be used if available but are not required.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
List of optional input keys (empty by default)
|
|
94
|
+
"""
|
|
95
|
+
return []
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def output_keys(self) -> List[str]:
|
|
99
|
+
"""
|
|
100
|
+
List of output keys this stage produces.
|
|
101
|
+
|
|
102
|
+
Override this to declare what outputs this stage will add to
|
|
103
|
+
context.stage_outputs. This is used for documentation and
|
|
104
|
+
validation purposes.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
List of output keys this stage produces
|
|
108
|
+
"""
|
|
109
|
+
return []
|
|
110
|
+
|
|
111
|
+
def validate_inputs(self, context: "PipelineContext") -> bool:
|
|
112
|
+
"""
|
|
113
|
+
Validate that all required inputs are present.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
context: Pipeline context with stage outputs from previous stages
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
True if all required inputs are present, False otherwise
|
|
120
|
+
"""
|
|
121
|
+
for key in self.required_inputs:
|
|
122
|
+
if key not in context.stage_outputs:
|
|
123
|
+
return False
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
def get_missing_inputs(self, context: "PipelineContext") -> List[str]:
|
|
127
|
+
"""
|
|
128
|
+
Get list of missing required inputs.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
context: Pipeline context to check
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
List of missing input keys
|
|
135
|
+
"""
|
|
136
|
+
return [key for key in self.required_inputs if key not in context.stage_outputs]
|
|
137
|
+
|
|
138
|
+
@abstractmethod
|
|
139
|
+
async def execute(self, context: "PipelineContext") -> StageResult:
|
|
140
|
+
"""
|
|
141
|
+
Execute the stage.
|
|
142
|
+
|
|
143
|
+
This is the main entry point for stage execution. Implementations
|
|
144
|
+
should:
|
|
145
|
+
1. Read inputs from context.stage_outputs (using keys from required_inputs)
|
|
146
|
+
2. Perform the stage's work
|
|
147
|
+
3. Return a StageResult with outputs (using keys from output_keys)
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
context: Pipeline context with all job parameters and stage outputs
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
StageResult containing outputs or error information
|
|
154
|
+
"""
|
|
155
|
+
...
|
|
156
|
+
|
|
157
|
+
async def cleanup(self, context: "PipelineContext") -> None:
|
|
158
|
+
"""
|
|
159
|
+
Clean up any resources after stage execution.
|
|
160
|
+
|
|
161
|
+
Override this to perform cleanup operations like removing
|
|
162
|
+
temporary files. Called by the executor after stage completion.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
context: Pipeline context
|
|
166
|
+
"""
|
|
167
|
+
pass
|
|
168
|
+
|
|
169
|
+
def __repr__(self) -> str:
|
|
170
|
+
return f"<{self.__class__.__name__}(name={self.name})>"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class PipelineExecutor(ABC):
|
|
174
|
+
"""
|
|
175
|
+
Abstract base class for pipeline executors.
|
|
176
|
+
|
|
177
|
+
Executors are responsible for running pipeline stages in a specific
|
|
178
|
+
context (local in-process, remote via API, etc.).
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
@abstractmethod
|
|
182
|
+
async def run_stage(
|
|
183
|
+
self,
|
|
184
|
+
stage: PipelineStage,
|
|
185
|
+
context: "PipelineContext",
|
|
186
|
+
) -> StageResult:
|
|
187
|
+
"""
|
|
188
|
+
Execute a single pipeline stage.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
stage: The stage to execute
|
|
192
|
+
context: Pipeline context
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Result of stage execution
|
|
196
|
+
"""
|
|
197
|
+
...
|
|
198
|
+
|
|
199
|
+
@abstractmethod
|
|
200
|
+
async def run_pipeline(
|
|
201
|
+
self,
|
|
202
|
+
stages: List[PipelineStage],
|
|
203
|
+
context: "PipelineContext",
|
|
204
|
+
) -> Dict[str, StageResult]:
|
|
205
|
+
"""
|
|
206
|
+
Execute a full pipeline of stages.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
stages: List of stages to execute in order
|
|
210
|
+
context: Pipeline context
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Dictionary mapping stage names to their results
|
|
214
|
+
"""
|
|
215
|
+
...
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pipeline context for passing state through pipeline stages.
|
|
3
|
+
|
|
4
|
+
The PipelineContext is the central data structure that flows through
|
|
5
|
+
all pipeline stages, carrying:
|
|
6
|
+
- Job metadata (ID, artist, title)
|
|
7
|
+
- File paths (input audio, output directory)
|
|
8
|
+
- Style configuration
|
|
9
|
+
- Outputs from each stage
|
|
10
|
+
- Logging and progress callbacks
|
|
11
|
+
"""
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
15
|
+
import logging
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class PipelineContext:
|
|
20
|
+
"""
|
|
21
|
+
Shared state passed through pipeline stages.
|
|
22
|
+
|
|
23
|
+
This context object carries all information needed by pipeline stages
|
|
24
|
+
to perform their work, and accumulates outputs from each stage.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Job identification
|
|
28
|
+
job_id: str
|
|
29
|
+
artist: str
|
|
30
|
+
title: str
|
|
31
|
+
|
|
32
|
+
# File paths
|
|
33
|
+
input_audio_path: str
|
|
34
|
+
output_dir: str
|
|
35
|
+
|
|
36
|
+
# Style configuration (loaded from style_params.json)
|
|
37
|
+
style_params: Dict[str, Any] = field(default_factory=dict)
|
|
38
|
+
|
|
39
|
+
# Processing options
|
|
40
|
+
enable_cdg: bool = True
|
|
41
|
+
enable_txt: bool = True
|
|
42
|
+
brand_prefix: Optional[str] = None
|
|
43
|
+
enable_youtube_upload: bool = False
|
|
44
|
+
discord_webhook_url: Optional[str] = None
|
|
45
|
+
|
|
46
|
+
# Distribution options (native API for remote, rclone for local)
|
|
47
|
+
dropbox_path: Optional[str] = None
|
|
48
|
+
gdrive_folder_id: Optional[str] = None
|
|
49
|
+
organised_dir_rclone_root: Optional[str] = None
|
|
50
|
+
rclone_destination: Optional[str] = None
|
|
51
|
+
|
|
52
|
+
# Accumulated outputs from each stage
|
|
53
|
+
# Keys are typically: separation, transcription, screens, render, finalize
|
|
54
|
+
stage_outputs: Dict[str, Any] = field(default_factory=dict)
|
|
55
|
+
|
|
56
|
+
# Progress tracking
|
|
57
|
+
current_stage: Optional[str] = None
|
|
58
|
+
progress_percent: int = 0
|
|
59
|
+
|
|
60
|
+
# Callbacks for progress updates and logging
|
|
61
|
+
on_progress: Optional[Callable[[str, int, str], None]] = None
|
|
62
|
+
on_log: Optional[Callable[[str, str, str], None]] = None
|
|
63
|
+
|
|
64
|
+
# Logger instance
|
|
65
|
+
logger: Optional[logging.Logger] = None
|
|
66
|
+
|
|
67
|
+
# Temporary files/directories to clean up
|
|
68
|
+
temp_paths: List[str] = field(default_factory=list)
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def base_name(self) -> str:
|
|
72
|
+
"""Standard base name for output files: 'Artist - Title'"""
|
|
73
|
+
return f"{self.artist} - {self.title}"
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def output_path(self) -> Path:
|
|
77
|
+
"""Output directory as Path object."""
|
|
78
|
+
return Path(self.output_dir)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def input_path(self) -> Path:
|
|
82
|
+
"""Input audio path as Path object."""
|
|
83
|
+
return Path(self.input_audio_path)
|
|
84
|
+
|
|
85
|
+
def get_stage_output(self, stage: str, key: str, default: Any = None) -> Any:
|
|
86
|
+
"""
|
|
87
|
+
Get a specific output from a stage.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
stage: Stage name
|
|
91
|
+
key: Output key within that stage
|
|
92
|
+
default: Default value if not found
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
The output value, or default if not found
|
|
96
|
+
"""
|
|
97
|
+
stage_data = self.stage_outputs.get(stage, {})
|
|
98
|
+
if isinstance(stage_data, dict):
|
|
99
|
+
return stage_data.get(key, default)
|
|
100
|
+
return default
|
|
101
|
+
|
|
102
|
+
def set_stage_output(self, stage: str, outputs: Dict[str, Any]) -> None:
|
|
103
|
+
"""
|
|
104
|
+
Set outputs for a stage.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
stage: Stage name
|
|
108
|
+
outputs: Dictionary of outputs to store
|
|
109
|
+
"""
|
|
110
|
+
if stage not in self.stage_outputs:
|
|
111
|
+
self.stage_outputs[stage] = {}
|
|
112
|
+
|
|
113
|
+
if isinstance(self.stage_outputs[stage], dict):
|
|
114
|
+
self.stage_outputs[stage].update(outputs)
|
|
115
|
+
else:
|
|
116
|
+
self.stage_outputs[stage] = outputs
|
|
117
|
+
|
|
118
|
+
def update_progress(self, stage: str, percent: int, message: str = "") -> None:
|
|
119
|
+
"""
|
|
120
|
+
Update progress and notify callback if set.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
stage: Current stage name
|
|
124
|
+
percent: Progress percentage (0-100)
|
|
125
|
+
message: Optional progress message
|
|
126
|
+
"""
|
|
127
|
+
self.current_stage = stage
|
|
128
|
+
self.progress_percent = percent
|
|
129
|
+
|
|
130
|
+
if self.on_progress:
|
|
131
|
+
self.on_progress(stage, percent, message)
|
|
132
|
+
|
|
133
|
+
def log(self, level: str, message: str) -> None:
|
|
134
|
+
"""
|
|
135
|
+
Log a message using the configured logger or callback.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
level: Log level (DEBUG, INFO, WARNING, ERROR)
|
|
139
|
+
message: Message to log
|
|
140
|
+
"""
|
|
141
|
+
if self.logger:
|
|
142
|
+
log_method = getattr(self.logger, level.lower(), self.logger.info)
|
|
143
|
+
log_method(f"[{self.current_stage or 'pipeline'}] {message}")
|
|
144
|
+
|
|
145
|
+
if self.on_log:
|
|
146
|
+
self.on_log(self.current_stage or "pipeline", level, message)
|
|
147
|
+
|
|
148
|
+
def add_temp_path(self, path: str) -> None:
|
|
149
|
+
"""
|
|
150
|
+
Register a temporary path for cleanup.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
path: Path to temporary file or directory
|
|
154
|
+
"""
|
|
155
|
+
self.temp_paths.append(path)
|
|
156
|
+
|
|
157
|
+
def cleanup_temp_paths(self) -> None:
|
|
158
|
+
"""Remove all registered temporary paths."""
|
|
159
|
+
import shutil
|
|
160
|
+
import os
|
|
161
|
+
|
|
162
|
+
for path in self.temp_paths:
|
|
163
|
+
try:
|
|
164
|
+
if os.path.isdir(path):
|
|
165
|
+
shutil.rmtree(path)
|
|
166
|
+
elif os.path.isfile(path):
|
|
167
|
+
os.remove(path)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass # Best effort cleanup
|
|
170
|
+
|
|
171
|
+
self.temp_paths.clear()
|
|
172
|
+
|
|
173
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
174
|
+
"""
|
|
175
|
+
Convert context to a dictionary for serialization.
|
|
176
|
+
|
|
177
|
+
Excludes non-serializable fields (callbacks, logger).
|
|
178
|
+
"""
|
|
179
|
+
return {
|
|
180
|
+
"job_id": self.job_id,
|
|
181
|
+
"artist": self.artist,
|
|
182
|
+
"title": self.title,
|
|
183
|
+
"input_audio_path": self.input_audio_path,
|
|
184
|
+
"output_dir": self.output_dir,
|
|
185
|
+
"style_params": self.style_params,
|
|
186
|
+
"enable_cdg": self.enable_cdg,
|
|
187
|
+
"enable_txt": self.enable_txt,
|
|
188
|
+
"brand_prefix": self.brand_prefix,
|
|
189
|
+
"enable_youtube_upload": self.enable_youtube_upload,
|
|
190
|
+
"discord_webhook_url": self.discord_webhook_url,
|
|
191
|
+
"dropbox_path": self.dropbox_path,
|
|
192
|
+
"gdrive_folder_id": self.gdrive_folder_id,
|
|
193
|
+
"organised_dir_rclone_root": self.organised_dir_rclone_root,
|
|
194
|
+
"rclone_destination": self.rclone_destination,
|
|
195
|
+
"stage_outputs": self.stage_outputs,
|
|
196
|
+
"current_stage": self.current_stage,
|
|
197
|
+
"progress_percent": self.progress_percent,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@classmethod
|
|
201
|
+
def from_dict(cls, data: Dict[str, Any]) -> "PipelineContext":
|
|
202
|
+
"""
|
|
203
|
+
Create a context from a dictionary.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
data: Dictionary with context fields
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
New PipelineContext instance
|
|
210
|
+
"""
|
|
211
|
+
return cls(
|
|
212
|
+
job_id=data.get("job_id", ""),
|
|
213
|
+
artist=data.get("artist", ""),
|
|
214
|
+
title=data.get("title", ""),
|
|
215
|
+
input_audio_path=data.get("input_audio_path", ""),
|
|
216
|
+
output_dir=data.get("output_dir", ""),
|
|
217
|
+
style_params=data.get("style_params", {}),
|
|
218
|
+
enable_cdg=data.get("enable_cdg", True),
|
|
219
|
+
enable_txt=data.get("enable_txt", True),
|
|
220
|
+
brand_prefix=data.get("brand_prefix"),
|
|
221
|
+
enable_youtube_upload=data.get("enable_youtube_upload", False),
|
|
222
|
+
discord_webhook_url=data.get("discord_webhook_url"),
|
|
223
|
+
dropbox_path=data.get("dropbox_path"),
|
|
224
|
+
gdrive_folder_id=data.get("gdrive_folder_id"),
|
|
225
|
+
organised_dir_rclone_root=data.get("organised_dir_rclone_root"),
|
|
226
|
+
rclone_destination=data.get("rclone_destination"),
|
|
227
|
+
stage_outputs=data.get("stage_outputs", {}),
|
|
228
|
+
current_stage=data.get("current_stage"),
|
|
229
|
+
progress_percent=data.get("progress_percent", 0),
|
|
230
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pipeline executors for running stages in different contexts.
|
|
3
|
+
|
|
4
|
+
Executors handle the actual execution of pipeline stages, providing
|
|
5
|
+
different implementations for different execution contexts:
|
|
6
|
+
|
|
7
|
+
- LocalExecutor: Runs stages directly in-process (for CLI)
|
|
8
|
+
- RemoteExecutor: Runs stages via backend API (for remote CLI)
|
|
9
|
+
|
|
10
|
+
Both executors use the same stage interface, ensuring consistent
|
|
11
|
+
behavior regardless of execution context.
|
|
12
|
+
"""
|
|
13
|
+
from karaoke_gen.pipeline.executors.local import LocalExecutor, create_local_executor
|
|
14
|
+
from karaoke_gen.pipeline.executors.remote import RemoteExecutor, create_remote_executor
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"LocalExecutor",
|
|
18
|
+
"RemoteExecutor",
|
|
19
|
+
"create_local_executor",
|
|
20
|
+
"create_remote_executor",
|
|
21
|
+
]
|