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.

Files changed (287) hide show
  1. karaoke_gen/__init__.py +38 -0
  2. karaoke_gen/audio_fetcher.py +1614 -0
  3. karaoke_gen/audio_processor.py +790 -0
  4. karaoke_gen/config.py +83 -0
  5. karaoke_gen/file_handler.py +387 -0
  6. karaoke_gen/instrumental_review/__init__.py +45 -0
  7. karaoke_gen/instrumental_review/analyzer.py +408 -0
  8. karaoke_gen/instrumental_review/editor.py +322 -0
  9. karaoke_gen/instrumental_review/models.py +171 -0
  10. karaoke_gen/instrumental_review/server.py +475 -0
  11. karaoke_gen/instrumental_review/static/index.html +1529 -0
  12. karaoke_gen/instrumental_review/waveform.py +409 -0
  13. karaoke_gen/karaoke_finalise/__init__.py +1 -0
  14. karaoke_gen/karaoke_finalise/karaoke_finalise.py +1833 -0
  15. karaoke_gen/karaoke_gen.py +1026 -0
  16. karaoke_gen/lyrics_processor.py +474 -0
  17. karaoke_gen/metadata.py +160 -0
  18. karaoke_gen/pipeline/__init__.py +87 -0
  19. karaoke_gen/pipeline/base.py +215 -0
  20. karaoke_gen/pipeline/context.py +230 -0
  21. karaoke_gen/pipeline/executors/__init__.py +21 -0
  22. karaoke_gen/pipeline/executors/local.py +159 -0
  23. karaoke_gen/pipeline/executors/remote.py +257 -0
  24. karaoke_gen/pipeline/stages/__init__.py +27 -0
  25. karaoke_gen/pipeline/stages/finalize.py +202 -0
  26. karaoke_gen/pipeline/stages/render.py +165 -0
  27. karaoke_gen/pipeline/stages/screens.py +139 -0
  28. karaoke_gen/pipeline/stages/separation.py +191 -0
  29. karaoke_gen/pipeline/stages/transcription.py +191 -0
  30. karaoke_gen/resources/AvenirNext-Bold.ttf +0 -0
  31. karaoke_gen/resources/Montserrat-Bold.ttf +0 -0
  32. karaoke_gen/resources/Oswald-Bold.ttf +0 -0
  33. karaoke_gen/resources/Oswald-SemiBold.ttf +0 -0
  34. karaoke_gen/resources/Zurich_Cn_BT_Bold.ttf +0 -0
  35. karaoke_gen/style_loader.py +531 -0
  36. karaoke_gen/utils/__init__.py +18 -0
  37. karaoke_gen/utils/bulk_cli.py +492 -0
  38. karaoke_gen/utils/cli_args.py +432 -0
  39. karaoke_gen/utils/gen_cli.py +978 -0
  40. karaoke_gen/utils/remote_cli.py +3268 -0
  41. karaoke_gen/video_background_processor.py +351 -0
  42. karaoke_gen/video_generator.py +424 -0
  43. karaoke_gen-0.75.54.dist-info/METADATA +718 -0
  44. karaoke_gen-0.75.54.dist-info/RECORD +287 -0
  45. karaoke_gen-0.75.54.dist-info/WHEEL +4 -0
  46. karaoke_gen-0.75.54.dist-info/entry_points.txt +5 -0
  47. karaoke_gen-0.75.54.dist-info/licenses/LICENSE +21 -0
  48. lyrics_transcriber/__init__.py +10 -0
  49. lyrics_transcriber/cli/__init__.py +0 -0
  50. lyrics_transcriber/cli/cli_main.py +285 -0
  51. lyrics_transcriber/core/__init__.py +0 -0
  52. lyrics_transcriber/core/config.py +50 -0
  53. lyrics_transcriber/core/controller.py +594 -0
  54. lyrics_transcriber/correction/__init__.py +0 -0
  55. lyrics_transcriber/correction/agentic/__init__.py +9 -0
  56. lyrics_transcriber/correction/agentic/adapter.py +71 -0
  57. lyrics_transcriber/correction/agentic/agent.py +313 -0
  58. lyrics_transcriber/correction/agentic/feedback/aggregator.py +12 -0
  59. lyrics_transcriber/correction/agentic/feedback/collector.py +17 -0
  60. lyrics_transcriber/correction/agentic/feedback/retention.py +24 -0
  61. lyrics_transcriber/correction/agentic/feedback/store.py +76 -0
  62. lyrics_transcriber/correction/agentic/handlers/__init__.py +24 -0
  63. lyrics_transcriber/correction/agentic/handlers/ambiguous.py +44 -0
  64. lyrics_transcriber/correction/agentic/handlers/background_vocals.py +68 -0
  65. lyrics_transcriber/correction/agentic/handlers/base.py +51 -0
  66. lyrics_transcriber/correction/agentic/handlers/complex_multi_error.py +46 -0
  67. lyrics_transcriber/correction/agentic/handlers/extra_words.py +74 -0
  68. lyrics_transcriber/correction/agentic/handlers/no_error.py +42 -0
  69. lyrics_transcriber/correction/agentic/handlers/punctuation.py +44 -0
  70. lyrics_transcriber/correction/agentic/handlers/registry.py +60 -0
  71. lyrics_transcriber/correction/agentic/handlers/repeated_section.py +44 -0
  72. lyrics_transcriber/correction/agentic/handlers/sound_alike.py +126 -0
  73. lyrics_transcriber/correction/agentic/models/__init__.py +5 -0
  74. lyrics_transcriber/correction/agentic/models/ai_correction.py +31 -0
  75. lyrics_transcriber/correction/agentic/models/correction_session.py +30 -0
  76. lyrics_transcriber/correction/agentic/models/enums.py +38 -0
  77. lyrics_transcriber/correction/agentic/models/human_feedback.py +30 -0
  78. lyrics_transcriber/correction/agentic/models/learning_data.py +26 -0
  79. lyrics_transcriber/correction/agentic/models/observability_metrics.py +28 -0
  80. lyrics_transcriber/correction/agentic/models/schemas.py +46 -0
  81. lyrics_transcriber/correction/agentic/models/utils.py +19 -0
  82. lyrics_transcriber/correction/agentic/observability/__init__.py +5 -0
  83. lyrics_transcriber/correction/agentic/observability/langfuse_integration.py +35 -0
  84. lyrics_transcriber/correction/agentic/observability/metrics.py +46 -0
  85. lyrics_transcriber/correction/agentic/observability/performance.py +19 -0
  86. lyrics_transcriber/correction/agentic/prompts/__init__.py +2 -0
  87. lyrics_transcriber/correction/agentic/prompts/classifier.py +227 -0
  88. lyrics_transcriber/correction/agentic/providers/__init__.py +6 -0
  89. lyrics_transcriber/correction/agentic/providers/base.py +36 -0
  90. lyrics_transcriber/correction/agentic/providers/circuit_breaker.py +145 -0
  91. lyrics_transcriber/correction/agentic/providers/config.py +73 -0
  92. lyrics_transcriber/correction/agentic/providers/constants.py +24 -0
  93. lyrics_transcriber/correction/agentic/providers/health.py +28 -0
  94. lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +212 -0
  95. lyrics_transcriber/correction/agentic/providers/model_factory.py +209 -0
  96. lyrics_transcriber/correction/agentic/providers/response_cache.py +218 -0
  97. lyrics_transcriber/correction/agentic/providers/response_parser.py +111 -0
  98. lyrics_transcriber/correction/agentic/providers/retry_executor.py +127 -0
  99. lyrics_transcriber/correction/agentic/router.py +35 -0
  100. lyrics_transcriber/correction/agentic/workflows/__init__.py +5 -0
  101. lyrics_transcriber/correction/agentic/workflows/consensus_workflow.py +24 -0
  102. lyrics_transcriber/correction/agentic/workflows/correction_graph.py +59 -0
  103. lyrics_transcriber/correction/agentic/workflows/feedback_workflow.py +24 -0
  104. lyrics_transcriber/correction/anchor_sequence.py +919 -0
  105. lyrics_transcriber/correction/corrector.py +760 -0
  106. lyrics_transcriber/correction/feedback/__init__.py +2 -0
  107. lyrics_transcriber/correction/feedback/schemas.py +107 -0
  108. lyrics_transcriber/correction/feedback/store.py +236 -0
  109. lyrics_transcriber/correction/handlers/__init__.py +0 -0
  110. lyrics_transcriber/correction/handlers/base.py +52 -0
  111. lyrics_transcriber/correction/handlers/extend_anchor.py +149 -0
  112. lyrics_transcriber/correction/handlers/levenshtein.py +189 -0
  113. lyrics_transcriber/correction/handlers/llm.py +293 -0
  114. lyrics_transcriber/correction/handlers/llm_providers.py +60 -0
  115. lyrics_transcriber/correction/handlers/no_space_punct_match.py +154 -0
  116. lyrics_transcriber/correction/handlers/relaxed_word_count_match.py +85 -0
  117. lyrics_transcriber/correction/handlers/repeat.py +88 -0
  118. lyrics_transcriber/correction/handlers/sound_alike.py +259 -0
  119. lyrics_transcriber/correction/handlers/syllables_match.py +252 -0
  120. lyrics_transcriber/correction/handlers/word_count_match.py +80 -0
  121. lyrics_transcriber/correction/handlers/word_operations.py +187 -0
  122. lyrics_transcriber/correction/operations.py +352 -0
  123. lyrics_transcriber/correction/phrase_analyzer.py +435 -0
  124. lyrics_transcriber/correction/text_utils.py +30 -0
  125. lyrics_transcriber/frontend/.gitignore +23 -0
  126. lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +935 -0
  127. lyrics_transcriber/frontend/.yarnrc.yml +3 -0
  128. lyrics_transcriber/frontend/README.md +50 -0
  129. lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +210 -0
  130. lyrics_transcriber/frontend/__init__.py +25 -0
  131. lyrics_transcriber/frontend/eslint.config.js +28 -0
  132. lyrics_transcriber/frontend/index.html +18 -0
  133. lyrics_transcriber/frontend/package.json +42 -0
  134. lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
  135. lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
  136. lyrics_transcriber/frontend/public/apple-touch-icon.png +0 -0
  137. lyrics_transcriber/frontend/public/favicon-16x16.png +0 -0
  138. lyrics_transcriber/frontend/public/favicon-32x32.png +0 -0
  139. lyrics_transcriber/frontend/public/favicon.ico +0 -0
  140. lyrics_transcriber/frontend/public/nomad-karaoke-logo.png +0 -0
  141. lyrics_transcriber/frontend/src/App.tsx +214 -0
  142. lyrics_transcriber/frontend/src/api.ts +254 -0
  143. lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +77 -0
  144. lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +114 -0
  145. lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +204 -0
  146. lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +180 -0
  147. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +167 -0
  148. lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +359 -0
  149. lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +281 -0
  150. lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +162 -0
  151. lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +257 -0
  152. lyrics_transcriber/frontend/src/components/EditActionBar.tsx +68 -0
  153. lyrics_transcriber/frontend/src/components/EditModal.tsx +702 -0
  154. lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +496 -0
  155. lyrics_transcriber/frontend/src/components/EditWordList.tsx +379 -0
  156. lyrics_transcriber/frontend/src/components/FileUpload.tsx +77 -0
  157. lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +467 -0
  158. lyrics_transcriber/frontend/src/components/Header.tsx +413 -0
  159. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +1387 -0
  160. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +185 -0
  161. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +704 -0
  162. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +80 -0
  163. lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +905 -0
  164. lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +51 -0
  165. lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +127 -0
  166. lyrics_transcriber/frontend/src/components/ModeSelector.tsx +67 -0
  167. lyrics_transcriber/frontend/src/components/ModelSelector.tsx +23 -0
  168. lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +144 -0
  169. lyrics_transcriber/frontend/src/components/ReferenceView.tsx +268 -0
  170. lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +336 -0
  171. lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +354 -0
  172. lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +64 -0
  173. lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +376 -0
  174. lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +131 -0
  175. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +256 -0
  176. lyrics_transcriber/frontend/src/components/WordDivider.tsx +187 -0
  177. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +379 -0
  178. lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +56 -0
  179. lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +87 -0
  180. lyrics_transcriber/frontend/src/components/shared/constants.ts +20 -0
  181. lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +180 -0
  182. lyrics_transcriber/frontend/src/components/shared/styles.ts +13 -0
  183. lyrics_transcriber/frontend/src/components/shared/types.js +2 -0
  184. lyrics_transcriber/frontend/src/components/shared/types.ts +129 -0
  185. lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +177 -0
  186. lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +78 -0
  187. lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +75 -0
  188. lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +360 -0
  189. lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +110 -0
  190. lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +22 -0
  191. lyrics_transcriber/frontend/src/hooks/useManualSync.ts +435 -0
  192. lyrics_transcriber/frontend/src/main.tsx +17 -0
  193. lyrics_transcriber/frontend/src/theme.ts +177 -0
  194. lyrics_transcriber/frontend/src/types/global.d.ts +9 -0
  195. lyrics_transcriber/frontend/src/types.js +2 -0
  196. lyrics_transcriber/frontend/src/types.ts +199 -0
  197. lyrics_transcriber/frontend/src/validation.ts +132 -0
  198. lyrics_transcriber/frontend/src/vite-env.d.ts +1 -0
  199. lyrics_transcriber/frontend/tsconfig.app.json +26 -0
  200. lyrics_transcriber/frontend/tsconfig.json +25 -0
  201. lyrics_transcriber/frontend/tsconfig.node.json +23 -0
  202. lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -0
  203. lyrics_transcriber/frontend/update_version.js +11 -0
  204. lyrics_transcriber/frontend/vite.config.d.ts +2 -0
  205. lyrics_transcriber/frontend/vite.config.js +10 -0
  206. lyrics_transcriber/frontend/vite.config.ts +11 -0
  207. lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
  208. lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
  209. lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
  210. lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js +43288 -0
  211. lyrics_transcriber/frontend/web_assets/assets/index-BECn1o8Q.js.map +1 -0
  212. lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
  213. lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
  214. lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
  215. lyrics_transcriber/frontend/web_assets/index.html +18 -0
  216. lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
  217. lyrics_transcriber/frontend/yarn.lock +3752 -0
  218. lyrics_transcriber/lyrics/__init__.py +0 -0
  219. lyrics_transcriber/lyrics/base_lyrics_provider.py +211 -0
  220. lyrics_transcriber/lyrics/file_provider.py +95 -0
  221. lyrics_transcriber/lyrics/genius.py +384 -0
  222. lyrics_transcriber/lyrics/lrclib.py +231 -0
  223. lyrics_transcriber/lyrics/musixmatch.py +156 -0
  224. lyrics_transcriber/lyrics/spotify.py +290 -0
  225. lyrics_transcriber/lyrics/user_input_provider.py +44 -0
  226. lyrics_transcriber/output/__init__.py +0 -0
  227. lyrics_transcriber/output/ass/__init__.py +21 -0
  228. lyrics_transcriber/output/ass/ass.py +2088 -0
  229. lyrics_transcriber/output/ass/ass_specs.txt +732 -0
  230. lyrics_transcriber/output/ass/config.py +180 -0
  231. lyrics_transcriber/output/ass/constants.py +23 -0
  232. lyrics_transcriber/output/ass/event.py +94 -0
  233. lyrics_transcriber/output/ass/formatters.py +132 -0
  234. lyrics_transcriber/output/ass/lyrics_line.py +265 -0
  235. lyrics_transcriber/output/ass/lyrics_screen.py +252 -0
  236. lyrics_transcriber/output/ass/section_detector.py +89 -0
  237. lyrics_transcriber/output/ass/section_screen.py +106 -0
  238. lyrics_transcriber/output/ass/style.py +187 -0
  239. lyrics_transcriber/output/cdg.py +619 -0
  240. lyrics_transcriber/output/cdgmaker/__init__.py +0 -0
  241. lyrics_transcriber/output/cdgmaker/cdg.py +262 -0
  242. lyrics_transcriber/output/cdgmaker/composer.py +2260 -0
  243. lyrics_transcriber/output/cdgmaker/config.py +151 -0
  244. lyrics_transcriber/output/cdgmaker/images/instrumental.png +0 -0
  245. lyrics_transcriber/output/cdgmaker/images/intro.png +0 -0
  246. lyrics_transcriber/output/cdgmaker/pack.py +507 -0
  247. lyrics_transcriber/output/cdgmaker/render.py +346 -0
  248. lyrics_transcriber/output/cdgmaker/transitions/centertexttoplogobottomtext.png +0 -0
  249. lyrics_transcriber/output/cdgmaker/transitions/circlein.png +0 -0
  250. lyrics_transcriber/output/cdgmaker/transitions/circleout.png +0 -0
  251. lyrics_transcriber/output/cdgmaker/transitions/fizzle.png +0 -0
  252. lyrics_transcriber/output/cdgmaker/transitions/largecentertexttoplogo.png +0 -0
  253. lyrics_transcriber/output/cdgmaker/transitions/rectangle.png +0 -0
  254. lyrics_transcriber/output/cdgmaker/transitions/spiral.png +0 -0
  255. lyrics_transcriber/output/cdgmaker/transitions/topleftmusicalnotes.png +0 -0
  256. lyrics_transcriber/output/cdgmaker/transitions/wipein.png +0 -0
  257. lyrics_transcriber/output/cdgmaker/transitions/wipeleft.png +0 -0
  258. lyrics_transcriber/output/cdgmaker/transitions/wipeout.png +0 -0
  259. lyrics_transcriber/output/cdgmaker/transitions/wiperight.png +0 -0
  260. lyrics_transcriber/output/cdgmaker/utils.py +132 -0
  261. lyrics_transcriber/output/countdown_processor.py +306 -0
  262. lyrics_transcriber/output/fonts/AvenirNext-Bold.ttf +0 -0
  263. lyrics_transcriber/output/fonts/DMSans-VariableFont_opsz,wght.ttf +0 -0
  264. lyrics_transcriber/output/fonts/DMSerifDisplay-Regular.ttf +0 -0
  265. lyrics_transcriber/output/fonts/Oswald-SemiBold.ttf +0 -0
  266. lyrics_transcriber/output/fonts/Zurich_Cn_BT_Bold.ttf +0 -0
  267. lyrics_transcriber/output/fonts/arial.ttf +0 -0
  268. lyrics_transcriber/output/fonts/georgia.ttf +0 -0
  269. lyrics_transcriber/output/fonts/verdana.ttf +0 -0
  270. lyrics_transcriber/output/generator.py +257 -0
  271. lyrics_transcriber/output/lrc_to_cdg.py +61 -0
  272. lyrics_transcriber/output/lyrics_file.py +102 -0
  273. lyrics_transcriber/output/plain_text.py +96 -0
  274. lyrics_transcriber/output/segment_resizer.py +431 -0
  275. lyrics_transcriber/output/subtitles.py +397 -0
  276. lyrics_transcriber/output/video.py +544 -0
  277. lyrics_transcriber/review/__init__.py +0 -0
  278. lyrics_transcriber/review/server.py +676 -0
  279. lyrics_transcriber/storage/__init__.py +0 -0
  280. lyrics_transcriber/storage/dropbox.py +225 -0
  281. lyrics_transcriber/transcribers/__init__.py +0 -0
  282. lyrics_transcriber/transcribers/audioshake.py +379 -0
  283. lyrics_transcriber/transcribers/base_transcriber.py +157 -0
  284. lyrics_transcriber/transcribers/whisper.py +330 -0
  285. lyrics_transcriber/types.py +650 -0
  286. lyrics_transcriber/utils/__init__.py +0 -0
  287. 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
+ ]