karaoke-gen 0.71.29__tar.gz → 0.72.2__tar.gz

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.
Files changed (290) hide show
  1. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/PKG-INFO +4 -1
  2. karaoke_gen-0.72.2/karaoke_gen/__init__.py +38 -0
  3. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/audio_fetcher.py +291 -34
  4. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/audio_processor.py +11 -3
  5. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/file_handler.py +192 -0
  6. karaoke_gen-0.72.2/karaoke_gen/instrumental_review/__init__.py +45 -0
  7. karaoke_gen-0.72.2/karaoke_gen/instrumental_review/analyzer.py +408 -0
  8. karaoke_gen-0.72.2/karaoke_gen/instrumental_review/editor.py +322 -0
  9. karaoke_gen-0.72.2/karaoke_gen/instrumental_review/models.py +171 -0
  10. karaoke_gen-0.72.2/karaoke_gen/instrumental_review/server.py +423 -0
  11. karaoke_gen-0.72.2/karaoke_gen/instrumental_review/static/index.html +1490 -0
  12. karaoke_gen-0.72.2/karaoke_gen/instrumental_review/waveform.py +409 -0
  13. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/karaoke_finalise/karaoke_finalise.py +62 -1
  14. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/karaoke_gen.py +114 -1
  15. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/lyrics_processor.py +81 -4
  16. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/utils/bulk_cli.py +3 -0
  17. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/utils/cli_args.py +9 -2
  18. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/utils/gen_cli.py +369 -2
  19. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/utils/remote_cli.py +1040 -74
  20. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/anchor_sequence.py +226 -350
  21. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/package.json +1 -1
  22. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/Header.tsx +38 -12
  23. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +17 -3
  24. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +185 -0
  25. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +704 -0
  26. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +80 -0
  27. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +905 -0
  28. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +127 -0
  29. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +336 -0
  30. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -0
  31. karaoke_gen-0.71.29/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/assets/index-DdJTDWH3.js → karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/assets/index-COYImAcx.js +1722 -489
  32. karaoke_gen-0.72.2/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/assets/index-COYImAcx.js.map +1 -0
  33. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/index.html +1 -1
  34. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/review/server.py +5 -5
  35. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/pyproject.toml +5 -1
  36. karaoke_gen-0.71.29/karaoke_gen/__init__.py +0 -7
  37. karaoke_gen-0.71.29/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +0 -688
  38. karaoke_gen-0.71.29/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.tsbuildinfo +0 -1
  39. karaoke_gen-0.71.29/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/assets/index-DdJTDWH3.js.map +0 -1
  40. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/LICENSE +0 -0
  41. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/README.md +0 -0
  42. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/config.py +0 -0
  43. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/karaoke_finalise/__init__.py +0 -0
  44. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/metadata.py +0 -0
  45. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/__init__.py +0 -0
  46. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/base.py +0 -0
  47. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/context.py +0 -0
  48. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/executors/__init__.py +0 -0
  49. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/executors/local.py +0 -0
  50. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/executors/remote.py +0 -0
  51. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/stages/__init__.py +0 -0
  52. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/stages/finalize.py +0 -0
  53. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/stages/render.py +0 -0
  54. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/stages/screens.py +0 -0
  55. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/stages/separation.py +0 -0
  56. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/pipeline/stages/transcription.py +0 -0
  57. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/resources/AvenirNext-Bold.ttf +0 -0
  58. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/resources/Montserrat-Bold.ttf +0 -0
  59. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/resources/Oswald-Bold.ttf +0 -0
  60. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/resources/Oswald-SemiBold.ttf +0 -0
  61. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/resources/Zurich_Cn_BT_Bold.ttf +0 -0
  62. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/style_loader.py +0 -0
  63. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/utils/__init__.py +0 -0
  64. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/video_background_processor.py +0 -0
  65. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/karaoke_gen/video_generator.py +0 -0
  66. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/__init__.py +0 -0
  67. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/cli/__init__.py +0 -0
  68. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/cli/cli_main.py +0 -0
  69. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/core/__init__.py +0 -0
  70. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/core/config.py +0 -0
  71. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/core/controller.py +0 -0
  72. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/__init__.py +0 -0
  73. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/__init__.py +0 -0
  74. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/adapter.py +0 -0
  75. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/agent.py +0 -0
  76. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/aggregator.py +0 -0
  77. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/collector.py +0 -0
  78. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/retention.py +0 -0
  79. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/store.py +0 -0
  80. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/__init__.py +0 -0
  81. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/ambiguous.py +0 -0
  82. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/background_vocals.py +0 -0
  83. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/base.py +0 -0
  84. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/complex_multi_error.py +0 -0
  85. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/extra_words.py +0 -0
  86. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/no_error.py +0 -0
  87. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/punctuation.py +0 -0
  88. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/registry.py +0 -0
  89. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/repeated_section.py +0 -0
  90. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/sound_alike.py +0 -0
  91. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/__init__.py +0 -0
  92. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/ai_correction.py +0 -0
  93. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/correction_session.py +0 -0
  94. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/enums.py +0 -0
  95. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/human_feedback.py +0 -0
  96. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/learning_data.py +0 -0
  97. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/observability_metrics.py +0 -0
  98. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/schemas.py +0 -0
  99. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/utils.py +0 -0
  100. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/__init__.py +0 -0
  101. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/langfuse_integration.py +0 -0
  102. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/metrics.py +0 -0
  103. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/performance.py +0 -0
  104. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/prompts/__init__.py +0 -0
  105. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/prompts/classifier.py +0 -0
  106. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/__init__.py +0 -0
  107. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/base.py +0 -0
  108. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/circuit_breaker.py +0 -0
  109. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/config.py +0 -0
  110. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/constants.py +0 -0
  111. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/health.py +0 -0
  112. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +0 -0
  113. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/model_factory.py +0 -0
  114. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/response_cache.py +0 -0
  115. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/response_parser.py +0 -0
  116. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/retry_executor.py +0 -0
  117. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/router.py +0 -0
  118. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/__init__.py +0 -0
  119. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/consensus_workflow.py +0 -0
  120. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/correction_graph.py +0 -0
  121. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/feedback_workflow.py +0 -0
  122. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/corrector.py +0 -0
  123. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/feedback/__init__.py +0 -0
  124. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/feedback/schemas.py +0 -0
  125. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/feedback/store.py +0 -0
  126. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/__init__.py +0 -0
  127. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/base.py +0 -0
  128. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/extend_anchor.py +0 -0
  129. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/levenshtein.py +0 -0
  130. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/llm.py +0 -0
  131. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/llm_providers.py +0 -0
  132. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/no_space_punct_match.py +0 -0
  133. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/relaxed_word_count_match.py +0 -0
  134. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/repeat.py +0 -0
  135. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/sound_alike.py +0 -0
  136. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/syllables_match.py +0 -0
  137. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/word_count_match.py +0 -0
  138. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/word_operations.py +0 -0
  139. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/operations.py +0 -0
  140. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/phrase_analyzer.py +0 -0
  141. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/text_utils.py +0 -0
  142. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/.gitignore +0 -0
  143. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +0 -0
  144. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/.yarnrc.yml +0 -0
  145. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/README.md +0 -0
  146. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +0 -0
  147. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/__init__.py +0 -0
  148. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/eslint.config.js +0 -0
  149. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/index.html +0 -0
  150. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
  151. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
  152. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/apple-touch-icon.png +0 -0
  153. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/favicon-16x16.png +0 -0
  154. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/favicon-32x32.png +0 -0
  155. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/favicon.ico +0 -0
  156. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/nomad-karaoke-logo.png +0 -0
  157. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/App.tsx +0 -0
  158. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/api.ts +0 -0
  159. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +0 -0
  160. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +0 -0
  161. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +0 -0
  162. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +0 -0
  163. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +0 -0
  164. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +0 -0
  165. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +0 -0
  166. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +0 -0
  167. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +0 -0
  168. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditActionBar.tsx +0 -0
  169. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditModal.tsx +0 -0
  170. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +0 -0
  171. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditWordList.tsx +0 -0
  172. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/FileUpload.tsx +0 -0
  173. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +0 -0
  174. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +0 -0
  175. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ModeSelector.tsx +0 -0
  176. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ModelSelector.tsx +0 -0
  177. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +0 -0
  178. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ReferenceView.tsx +0 -0
  179. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +0 -0
  180. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +0 -0
  181. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +0 -0
  182. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +0 -0
  183. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +0 -0
  184. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/WordDivider.tsx +0 -0
  185. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +0 -0
  186. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +0 -0
  187. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +0 -0
  188. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/constants.ts +0 -0
  189. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +0 -0
  190. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/styles.ts +0 -0
  191. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/types.js +0 -0
  192. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/types.ts +0 -0
  193. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +0 -0
  194. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +0 -0
  195. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +0 -0
  196. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +0 -0
  197. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +0 -0
  198. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +0 -0
  199. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/hooks/useManualSync.ts +0 -0
  200. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/main.tsx +0 -0
  201. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/theme.ts +0 -0
  202. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/types/global.d.ts +0 -0
  203. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/types.js +0 -0
  204. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/types.ts +0 -0
  205. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/validation.ts +0 -0
  206. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/vite-env.d.ts +0 -0
  207. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.app.json +0 -0
  208. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.json +0 -0
  209. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.node.json +0 -0
  210. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/update_version.js +0 -0
  211. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/vite.config.d.ts +0 -0
  212. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/vite.config.js +0 -0
  213. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/vite.config.ts +0 -0
  214. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
  215. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
  216. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
  217. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
  218. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
  219. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
  220. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
  221. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/yarn.lock +0 -0
  222. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/__init__.py +0 -0
  223. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/base_lyrics_provider.py +0 -0
  224. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/file_provider.py +0 -0
  225. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/genius.py +0 -0
  226. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/lrclib.py +0 -0
  227. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/musixmatch.py +0 -0
  228. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/spotify.py +0 -0
  229. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/user_input_provider.py +0 -0
  230. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/__init__.py +0 -0
  231. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/__init__.py +0 -0
  232. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/ass.py +0 -0
  233. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/ass_specs.txt +0 -0
  234. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/config.py +0 -0
  235. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/constants.py +0 -0
  236. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/event.py +0 -0
  237. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/formatters.py +0 -0
  238. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/lyrics_line.py +0 -0
  239. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/lyrics_screen.py +0 -0
  240. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/section_detector.py +0 -0
  241. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/section_screen.py +0 -0
  242. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/style.py +0 -0
  243. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdg.py +0 -0
  244. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/__init__.py +0 -0
  245. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/cdg.py +0 -0
  246. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/composer.py +0 -0
  247. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/config.py +0 -0
  248. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/images/instrumental.png +0 -0
  249. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/images/intro.png +0 -0
  250. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/pack.py +0 -0
  251. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/render.py +0 -0
  252. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/centertexttoplogobottomtext.png +0 -0
  253. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/circlein.png +0 -0
  254. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/circleout.png +0 -0
  255. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/fizzle.png +0 -0
  256. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/largecentertexttoplogo.png +0 -0
  257. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/rectangle.png +0 -0
  258. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/spiral.png +0 -0
  259. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/topleftmusicalnotes.png +0 -0
  260. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wipein.png +0 -0
  261. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wipeleft.png +0 -0
  262. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wipeout.png +0 -0
  263. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wiperight.png +0 -0
  264. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/utils.py +0 -0
  265. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/countdown_processor.py +0 -0
  266. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/AvenirNext-Bold.ttf +0 -0
  267. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/DMSans-VariableFont_opsz,wght.ttf +0 -0
  268. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/DMSerifDisplay-Regular.ttf +0 -0
  269. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/Oswald-SemiBold.ttf +0 -0
  270. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/Zurich_Cn_BT_Bold.ttf +0 -0
  271. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/arial.ttf +0 -0
  272. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/georgia.ttf +0 -0
  273. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/verdana.ttf +0 -0
  274. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/generator.py +0 -0
  275. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/lrc_to_cdg.py +0 -0
  276. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/lyrics_file.py +0 -0
  277. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/plain_text.py +0 -0
  278. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/segment_resizer.py +0 -0
  279. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/subtitles.py +0 -0
  280. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/output/video.py +0 -0
  281. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/review/__init__.py +0 -0
  282. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/storage/__init__.py +0 -0
  283. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/storage/dropbox.py +0 -0
  284. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/__init__.py +0 -0
  285. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/audioshake.py +0 -0
  286. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/base_transcriber.py +0 -0
  287. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/whisper.py +0 -0
  288. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/types.py +0 -0
  289. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/utils/__init__.py +0 -0
  290. {karaoke_gen-0.71.29 → karaoke_gen-0.72.2}/lyrics_transcriber_temp/lyrics_transcriber/utils/word_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: karaoke-gen
3
- Version: 0.71.29
3
+ Version: 0.72.2
4
4
  Summary: Generate karaoke videos with synchronized lyrics. Handles the entire process from downloading audio and lyrics to creating the final video with title screens.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -29,8 +29,10 @@ Requires-Dist: google-auth
29
29
  Requires-Dist: google-auth-httplib2
30
30
  Requires-Dist: google-auth-oauthlib
31
31
  Requires-Dist: google-cloud-firestore (>=2.14.0)
32
+ Requires-Dist: google-cloud-run (>=0.10.0)
32
33
  Requires-Dist: google-cloud-secret-manager (>=2.18.0)
33
34
  Requires-Dist: google-cloud-storage (>=2.14.0)
35
+ Requires-Dist: google-cloud-tasks (>=2.16.0)
34
36
  Requires-Dist: httpx (>=0.25.0)
35
37
  Requires-Dist: jiwer (>=3.0.0)
36
38
  Requires-Dist: karaoke-lyrics-processor (>=0.6)
@@ -82,6 +84,7 @@ Requires-Dist: torch (>=2.7)
82
84
  Requires-Dist: tqdm (>=4.67)
83
85
  Requires-Dist: transformers (>=4.47)
84
86
  Requires-Dist: uvicorn[standard] (>=0.24.0)
87
+ Requires-Dist: yt-dlp (>=2024.0.0)
85
88
  Project-URL: Documentation, https://github.com/nomadkaraoke/karaoke-gen/blob/main/README.md
86
89
  Project-URL: Homepage, https://github.com/nomadkaraoke/karaoke-gen
87
90
  Project-URL: Repository, https://github.com/nomadkaraoke/karaoke-gen
@@ -0,0 +1,38 @@
1
+ import warnings
2
+
3
+ # Suppress specific SyntaxWarnings from third-party packages
4
+ warnings.filterwarnings("ignore", category=SyntaxWarning, module="pydub.*")
5
+ warnings.filterwarnings("ignore", category=SyntaxWarning, module="syrics.*")
6
+
7
+ # Lazy imports to avoid loading heavy dependencies when not needed.
8
+ # KaraokePrep has many dependencies (lyrics_transcriber, etc.) which may not
9
+ # be available in all contexts (e.g., backend which only needs audio_fetcher).
10
+ #
11
+ # Use explicit imports instead:
12
+ # from karaoke_gen.karaoke_gen import KaraokePrep
13
+ # from karaoke_gen.audio_fetcher import FlacFetcher, AudioSearchResult, ...
14
+
15
+ __all__ = [
16
+ "KaraokePrep",
17
+ # Audio fetcher
18
+ "FlacFetcher",
19
+ "AudioSearchResult",
20
+ "AudioFetchResult",
21
+ "AudioFetcherError",
22
+ "NoResultsError",
23
+ "DownloadError",
24
+ "UserCancelledError",
25
+ ]
26
+
27
+
28
+ def __getattr__(name):
29
+ """Lazy import for heavy modules."""
30
+ if name == "KaraokePrep":
31
+ from .karaoke_gen import KaraokePrep
32
+ return KaraokePrep
33
+ elif name in ("FlacFetcher", "AudioSearchResult", "AudioFetchResult",
34
+ "AudioFetcherError", "NoResultsError", "DownloadError",
35
+ "UserCancelledError"):
36
+ from . import audio_fetcher
37
+ return getattr(audio_fetcher, name)
38
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -7,15 +7,26 @@ using flacfetch, replacing the previous direct yt-dlp usage.
7
7
 
8
8
  import logging
9
9
  import os
10
+ import signal
11
+ import sys
10
12
  import tempfile
13
+ import threading
11
14
  from abc import ABC, abstractmethod
12
- from dataclasses import dataclass
13
- from typing import List, Optional
15
+ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError
16
+ from dataclasses import dataclass, asdict, field
17
+ from typing import List, Optional, Dict, Any
18
+
19
+ # Global flag to track if user requested cancellation via Ctrl+C
20
+ _interrupt_requested = False
14
21
 
15
22
 
16
23
  @dataclass
17
24
  class AudioSearchResult:
18
- """Represents a single search result for audio."""
25
+ """Represents a single search result for audio.
26
+
27
+ Used by both local CLI and cloud backend. Supports serialization
28
+ for Firestore storage via to_dict()/from_dict().
29
+ """
19
30
 
20
31
  title: str
21
32
  artist: str
@@ -24,13 +35,49 @@ class AudioSearchResult:
24
35
  duration: Optional[int] = None # Duration in seconds
25
36
  quality: Optional[str] = None # e.g., "FLAC", "320kbps", etc.
26
37
  source_id: Optional[str] = None # Unique ID from the source
27
- # Raw result object from the provider (for download)
28
- raw_result: Optional[object] = None
38
+ index: int = 0 # Index in the results list (for API selection)
39
+ # Raw result object from the provider (for download) - not serialized
40
+ raw_result: Optional[object] = field(default=None, repr=False)
41
+
42
+ def to_dict(self) -> Dict[str, Any]:
43
+ """Convert to dict for JSON/Firestore serialization.
44
+
45
+ Note: raw_result is excluded as it's not serializable.
46
+ """
47
+ return {
48
+ "title": self.title,
49
+ "artist": self.artist,
50
+ "url": self.url,
51
+ "provider": self.provider,
52
+ "duration": self.duration,
53
+ "quality": self.quality,
54
+ "source_id": self.source_id,
55
+ "index": self.index,
56
+ }
57
+
58
+ @classmethod
59
+ def from_dict(cls, data: Dict[str, Any]) -> "AudioSearchResult":
60
+ """Create from dict (e.g., from Firestore)."""
61
+ return cls(
62
+ title=data.get("title", ""),
63
+ artist=data.get("artist", ""),
64
+ url=data.get("url", ""),
65
+ provider=data.get("provider", "Unknown"),
66
+ duration=data.get("duration"),
67
+ quality=data.get("quality"),
68
+ source_id=data.get("source_id"),
69
+ index=data.get("index", 0),
70
+ raw_result=None, # Not stored in serialized form
71
+ )
29
72
 
30
73
 
31
74
  @dataclass
32
75
  class AudioFetchResult:
33
- """Result of an audio fetch operation."""
76
+ """Result of an audio fetch operation.
77
+
78
+ Used by both local CLI and cloud backend. Supports serialization
79
+ for Firestore storage via to_dict()/from_dict().
80
+ """
34
81
 
35
82
  filepath: str
36
83
  artist: str
@@ -38,6 +85,22 @@ class AudioFetchResult:
38
85
  provider: str
39
86
  duration: Optional[int] = None
40
87
  quality: Optional[str] = None
88
+
89
+ def to_dict(self) -> Dict[str, Any]:
90
+ """Convert to dict for JSON/Firestore serialization."""
91
+ return asdict(self)
92
+
93
+ @classmethod
94
+ def from_dict(cls, data: Dict[str, Any]) -> "AudioFetchResult":
95
+ """Create from dict (e.g., from Firestore)."""
96
+ return cls(
97
+ filepath=data.get("filepath", ""),
98
+ artist=data.get("artist", ""),
99
+ title=data.get("title", ""),
100
+ provider=data.get("provider", "Unknown"),
101
+ duration=data.get("duration"),
102
+ quality=data.get("quality"),
103
+ )
41
104
 
42
105
 
43
106
  class AudioFetcherError(Exception):
@@ -58,6 +121,19 @@ class DownloadError(AudioFetcherError):
58
121
  pass
59
122
 
60
123
 
124
+ class UserCancelledError(AudioFetcherError):
125
+ """Raised when user explicitly cancels the operation (e.g., enters 0 or Ctrl+C)."""
126
+
127
+ pass
128
+
129
+
130
+ def _check_interrupt():
131
+ """Check if interrupt was requested and raise UserCancelledError if so."""
132
+ global _interrupt_requested
133
+ if _interrupt_requested:
134
+ raise UserCancelledError("Operation cancelled by user")
135
+
136
+
61
137
  class AudioFetcher(ABC):
62
138
  """Abstract base class for audio fetching implementations."""
63
139
 
@@ -140,6 +216,8 @@ class FlacFetchAudioFetcher(AudioFetcher):
140
216
 
141
217
  This provides access to multiple audio sources including private music trackers
142
218
  and YouTube, with intelligent prioritization of high-quality sources.
219
+
220
+ Also exported as FlacFetcher for shorter name.
143
221
  """
144
222
 
145
223
  def __init__(
@@ -169,25 +247,38 @@ class FlacFetchAudioFetcher(AudioFetcher):
169
247
  if self._manager is None:
170
248
  # Import flacfetch here to avoid import errors if not installed
171
249
  from flacfetch.core.manager import FetchManager
172
- from flacfetch.providers.youtube import YouTubeProvider
250
+ from flacfetch.providers.youtube import YoutubeProvider
251
+ from flacfetch.downloaders.youtube import YoutubeDownloader
252
+
253
+ # Try to import TorrentDownloader (has optional dependencies)
254
+ try:
255
+ from flacfetch.downloaders.torrent import TorrentDownloader
256
+ except ImportError:
257
+ TorrentDownloader = None
258
+ self.logger.debug("TorrentDownloader not available (missing dependencies)")
173
259
 
174
260
  self._manager = FetchManager()
175
261
 
176
- # Add providers based on available API keys
262
+ # Add providers and downloaders based on available API keys
177
263
  if self._redacted_api_key:
178
264
  from flacfetch.providers.redacted import RedactedProvider
179
265
 
180
266
  self._manager.add_provider(RedactedProvider(api_key=self._redacted_api_key))
267
+ if TorrentDownloader:
268
+ self._manager.register_downloader("Redacted", TorrentDownloader())
181
269
  self.logger.debug("Added Redacted provider")
182
270
 
183
271
  if self._ops_api_key:
184
272
  from flacfetch.providers.ops import OPSProvider
185
273
 
186
274
  self._manager.add_provider(OPSProvider(api_key=self._ops_api_key))
275
+ if TorrentDownloader:
276
+ self._manager.register_downloader("OPS", TorrentDownloader())
187
277
  self.logger.debug("Added OPS provider")
188
278
 
189
- # Always add YouTube as a fallback provider
190
- self._manager.add_provider(YouTubeProvider())
279
+ # Always add YouTube as a fallback provider with its downloader
280
+ self._manager.add_provider(YoutubeProvider())
281
+ self._manager.register_downloader("YouTube", YoutubeDownloader())
191
282
  self.logger.debug("Added YouTube provider")
192
283
 
193
284
  return self._manager
@@ -219,16 +310,21 @@ class FlacFetchAudioFetcher(AudioFetcher):
219
310
 
220
311
  # Convert to our AudioSearchResult format
221
312
  search_results = []
222
- for result in results:
313
+ for i, result in enumerate(results):
314
+ # Get quality as string if it's a Quality object
315
+ quality = getattr(result, "quality", None)
316
+ quality_str = str(quality) if quality else None
317
+
223
318
  search_results.append(
224
319
  AudioSearchResult(
225
320
  title=getattr(result, "title", title),
226
321
  artist=getattr(result, "artist", artist),
227
- url=getattr(result, "url", ""),
228
- provider=getattr(result, "provider", "Unknown"),
229
- duration=getattr(result, "duration", None),
230
- quality=getattr(result, "quality", None),
231
- source_id=getattr(result, "id", None),
322
+ url=getattr(result, "download_url", "") or "",
323
+ provider=getattr(result, "source_name", "Unknown"),
324
+ duration=getattr(result, "duration_seconds", None),
325
+ quality=quality_str,
326
+ source_id=getattr(result, "info_hash", None),
327
+ index=i, # Set index for API selection
232
328
  raw_result=result,
233
329
  )
234
330
  )
@@ -265,7 +361,7 @@ class FlacFetchAudioFetcher(AudioFetcher):
265
361
  if output_filename is None:
266
362
  output_filename = f"{result.artist} - {result.title}"
267
363
 
268
- self.logger.info(f"Downloading: {result.artist} - {result.title} from {result.provider}")
364
+ self.logger.info(f"Downloading: {result.artist} - {result.title} from {result.provider or 'Unknown'}")
269
365
 
270
366
  try:
271
367
  # Use flacfetch to download
@@ -292,6 +388,40 @@ class FlacFetchAudioFetcher(AudioFetcher):
292
388
  except Exception as e:
293
389
  raise DownloadError(f"Failed to download {result.artist} - {result.title}: {e}") from e
294
390
 
391
+ def select_best(self, results: List[AudioSearchResult]) -> int:
392
+ """
393
+ Select the best result from a list of search results.
394
+
395
+ Uses flacfetch's built-in quality ranking to determine the best source.
396
+ This is useful for automated/non-interactive usage.
397
+
398
+ Args:
399
+ results: List of AudioSearchResult objects from search()
400
+
401
+ Returns:
402
+ Index of the best result in the list
403
+ """
404
+ if not results:
405
+ return 0
406
+
407
+ manager = self._get_manager()
408
+
409
+ # Get raw results that have raw_result set
410
+ raw_results = [r.raw_result for r in results if r.raw_result is not None]
411
+
412
+ if raw_results:
413
+ try:
414
+ best = manager.select_best(raw_results)
415
+ # Find index of best result
416
+ for i, r in enumerate(results):
417
+ if r.raw_result == best:
418
+ return i
419
+ except Exception as e:
420
+ self.logger.warning(f"select_best failed, using first result: {e}")
421
+
422
+ # Fallback: return first result
423
+ return 0
424
+
295
425
  def search_and_download(
296
426
  self,
297
427
  artist: str,
@@ -319,6 +449,7 @@ class FlacFetchAudioFetcher(AudioFetcher):
319
449
  Raises:
320
450
  NoResultsError: If no results are found
321
451
  DownloadError: If download fails
452
+ UserCancelledError: If user cancels (Ctrl+C or enters 0)
322
453
  """
323
454
  from flacfetch.core.models import TrackQuery
324
455
 
@@ -326,7 +457,9 @@ class FlacFetchAudioFetcher(AudioFetcher):
326
457
  query = TrackQuery(artist=artist, title=title)
327
458
 
328
459
  self.logger.info(f"Searching for: {artist} - {title}")
329
- results = manager.search(query)
460
+
461
+ # Run search in a thread so we can handle Ctrl+C
462
+ results = self._interruptible_search(manager, query)
330
463
 
331
464
  if not results:
332
465
  raise NoResultsError(f"No results found for: {artist} - {title}")
@@ -336,11 +469,13 @@ class FlacFetchAudioFetcher(AudioFetcher):
336
469
  if auto_select:
337
470
  # Auto mode: select best result based on flacfetch's ranking
338
471
  selected = manager.select_best(results)
339
- self.logger.info(f"Auto-selected: {getattr(selected, 'title', title)} from {getattr(selected, 'provider', 'Unknown')}")
472
+ self.logger.info(f"Auto-selected: {getattr(selected, 'title', title)} from {getattr(selected, 'source_name', 'Unknown')}")
340
473
  else:
341
474
  # Interactive mode: present options to user
342
475
  selected = self._interactive_select(results, artist, title)
343
476
 
477
+ # Note: _interactive_select now raises UserCancelledError instead of returning None
478
+ # This check is kept as a safety net
344
479
  if selected is None:
345
480
  raise NoResultsError(f"No result selected for: {artist} - {title}")
346
481
 
@@ -351,7 +486,7 @@ class FlacFetchAudioFetcher(AudioFetcher):
351
486
  if output_filename is None:
352
487
  output_filename = f"{artist} - {title}"
353
488
 
354
- self.logger.info(f"Downloading from {getattr(selected, 'provider', 'Unknown')}...")
489
+ self.logger.info(f"Downloading from {getattr(selected, 'source_name', 'Unknown')}...")
355
490
 
356
491
  try:
357
492
  filepath = manager.download(
@@ -365,51 +500,169 @@ class FlacFetchAudioFetcher(AudioFetcher):
365
500
 
366
501
  self.logger.info(f"Downloaded to: {filepath}")
367
502
 
503
+ # Get quality as string if it's a Quality object
504
+ quality = getattr(selected, "quality", None)
505
+ quality_str = str(quality) if quality else None
506
+
368
507
  return AudioFetchResult(
369
508
  filepath=filepath,
370
509
  artist=artist,
371
510
  title=title,
372
- provider=getattr(selected, "provider", "Unknown"),
373
- duration=getattr(selected, "duration", None),
374
- quality=getattr(selected, "quality", None),
511
+ provider=getattr(selected, "source_name", "Unknown"),
512
+ duration=getattr(selected, "duration_seconds", None),
513
+ quality=quality_str,
375
514
  )
376
515
 
516
+ except (UserCancelledError, KeyboardInterrupt):
517
+ # Let cancellation exceptions propagate without wrapping
518
+ raise
377
519
  except Exception as e:
378
520
  raise DownloadError(f"Failed to download {artist} - {title}: {e}") from e
379
521
 
522
+ def _interruptible_search(self, manager, query) -> list:
523
+ """
524
+ Run search in a way that can be interrupted by Ctrl+C.
525
+
526
+ The flacfetch search is a blocking network operation that doesn't
527
+ respond to SIGINT while running. This method runs it in a background
528
+ thread and periodically checks for interrupts.
529
+
530
+ Args:
531
+ manager: The FetchManager instance
532
+ query: The TrackQuery to search for
533
+
534
+ Returns:
535
+ List of search results
536
+
537
+ Raises:
538
+ UserCancelledError: If user presses Ctrl+C during search
539
+ """
540
+ global _interrupt_requested
541
+ _interrupt_requested = False
542
+ result_container = {"results": None, "error": None}
543
+
544
+ def do_search():
545
+ try:
546
+ result_container["results"] = manager.search(query)
547
+ except Exception as e:
548
+ result_container["error"] = e
549
+
550
+ # Set up signal handler for immediate response to Ctrl+C
551
+ original_handler = signal.getsignal(signal.SIGINT)
552
+
553
+ def interrupt_handler(signum, frame):
554
+ global _interrupt_requested
555
+ _interrupt_requested = True
556
+ # Print immediately so user knows it was received
557
+ print("\nCancelling... please wait", file=sys.stderr)
558
+
559
+ signal.signal(signal.SIGINT, interrupt_handler)
560
+
561
+ try:
562
+ # Start search in background thread
563
+ search_thread = threading.Thread(target=do_search, daemon=True)
564
+ search_thread.start()
565
+
566
+ # Wait for completion with periodic interrupt checks
567
+ while search_thread.is_alive():
568
+ search_thread.join(timeout=0.1) # Check every 100ms
569
+ if _interrupt_requested:
570
+ # Don't wait for thread - it's a daemon and will be killed
571
+ raise UserCancelledError("Search cancelled by user (Ctrl+C)")
572
+
573
+ # Check for errors from the search
574
+ if result_container["error"] is not None:
575
+ raise result_container["error"]
576
+
577
+ return result_container["results"]
578
+
579
+ finally:
580
+ # Restore original signal handler
581
+ signal.signal(signal.SIGINT, original_handler)
582
+ _interrupt_requested = False
583
+
380
584
  def _interactive_select(self, results: list, artist: str, title: str) -> object:
381
585
  """
382
586
  Present search results to the user for interactive selection.
383
587
 
588
+ Uses flacfetch's built-in CLIHandler for rich, colorized output.
589
+
384
590
  Args:
385
- results: List of search results from flacfetch
591
+ results: List of Release objects from flacfetch
386
592
  artist: The artist name being searched
387
593
  title: The track title being searched
388
594
 
389
595
  Returns:
390
- The selected result object
596
+ The selected Release object
597
+
598
+ Raises:
599
+ UserCancelledError: If user cancels selection
600
+ """
601
+ try:
602
+ # Use flacfetch's built-in CLIHandler for rich display
603
+ from flacfetch.interface.cli import CLIHandler
604
+
605
+ handler = CLIHandler(target_artist=artist)
606
+ result = handler.select_release(results)
607
+ if result is None:
608
+ # User selected 0 to cancel
609
+ raise UserCancelledError("Selection cancelled by user")
610
+ return result
611
+ except ImportError:
612
+ # Fallback to basic display if CLIHandler not available
613
+ return self._basic_interactive_select(results, artist, title)
614
+ except (KeyboardInterrupt, EOFError):
615
+ raise UserCancelledError("Selection cancelled by user (Ctrl+C)")
616
+ except (AttributeError, TypeError):
617
+ # Fallback if results aren't proper Release objects (e.g., in tests)
618
+ return self._basic_interactive_select(results, artist, title)
619
+
620
+ def _basic_interactive_select(self, results: list, artist: str, title: str) -> object:
621
+ """
622
+ Basic fallback for interactive selection without rich formatting.
623
+
624
+ Args:
625
+ results: List of Release objects from flacfetch
626
+ artist: The artist name being searched
627
+ title: The track title being searched
628
+
629
+ Returns:
630
+ The selected Release object
631
+
632
+ Raises:
633
+ UserCancelledError: If user cancels selection
391
634
  """
392
635
  print(f"\n{'=' * 60}")
393
636
  print(f"Search Results for: {artist} - {title}")
394
637
  print(f"{'=' * 60}\n")
395
638
 
396
639
  for i, result in enumerate(results, 1):
397
- provider = getattr(result, "provider", "Unknown")
640
+ # Use correct flacfetch attribute names
641
+ source_name = getattr(result, "source_name", "Unknown")
398
642
  result_title = getattr(result, "title", "Unknown")
399
643
  result_artist = getattr(result, "artist", "Unknown")
400
- quality = getattr(result, "quality", "")
401
- duration = getattr(result, "duration", None)
644
+ quality = getattr(result, "quality", None)
645
+ duration_seconds = getattr(result, "duration_seconds", None)
646
+ seeders = getattr(result, "seeders", None)
647
+ target_file = getattr(result, "target_file", None)
402
648
 
403
649
  # Format duration if available
404
650
  duration_str = ""
405
- if duration:
406
- minutes = duration // 60
407
- seconds = duration % 60
651
+ if duration_seconds:
652
+ minutes = duration_seconds // 60
653
+ seconds = duration_seconds % 60
408
654
  duration_str = f" [{minutes}:{seconds:02d}]"
409
655
 
656
+ # Format quality - it's a Quality object with __str__
410
657
  quality_str = f" ({quality})" if quality else ""
411
658
 
412
- print(f" {i}. [{provider}] {result_artist} - {result_title}{quality_str}{duration_str}")
659
+ # Format seeders for torrent sources
660
+ seeders_str = f" Seeders: {seeders}" if seeders is not None else ""
661
+
662
+ # Format target file
663
+ file_str = f' "{target_file}"' if target_file else ""
664
+
665
+ print(f" {i}. [{source_name}] {result_artist} - {result_title}{quality_str}{duration_str}{seeders_str}{file_str}")
413
666
 
414
667
  print()
415
668
  print(" 0. Cancel")
@@ -421,7 +674,7 @@ class FlacFetchAudioFetcher(AudioFetcher):
421
674
 
422
675
  if choice == "0":
423
676
  self.logger.info("User cancelled selection")
424
- return None
677
+ raise UserCancelledError("Selection cancelled by user")
425
678
 
426
679
  choice_num = int(choice)
427
680
  if 1 <= choice_num <= len(results):
@@ -435,7 +688,11 @@ class FlacFetchAudioFetcher(AudioFetcher):
435
688
  print("Please enter a valid number")
436
689
  except KeyboardInterrupt:
437
690
  print("\nCancelled")
438
- return None
691
+ raise UserCancelledError("Selection cancelled by user (Ctrl+C)")
692
+
693
+
694
+ # Alias for shorter name - used by backend and other consumers
695
+ FlacFetcher = FlacFetchAudioFetcher
439
696
 
440
697
 
441
698
  def create_audio_fetcher(
@@ -771,8 +771,16 @@ class AudioProcessor:
771
771
  padded_result["other_stems"] = separation_result.get("other_stems", {})
772
772
  padded_result["backing_vocals"] = separation_result.get("backing_vocals", {})
773
773
 
774
- self.logger.info(
775
- f"✓ Countdown padding applied to {len(padded_result['combined_instrumentals']) + 1} instrumental file(s)"
776
- )
774
+ # Count actual padded files (don't assume clean instrumental was padded)
775
+ padded_count = (1 if padded_result["clean_instrumental"].get("instrumental") else 0) + len(padded_result["combined_instrumentals"])
776
+
777
+ if padded_count > 0:
778
+ self.logger.info(
779
+ f"✓ Countdown padding applied to {padded_count} instrumental file(s)"
780
+ )
781
+ else:
782
+ self.logger.warning(
783
+ "No instrumental files found to pad. Check that audio separation completed successfully."
784
+ )
777
785
 
778
786
  return padded_result