karaoke-gen 0.72.2__tar.gz → 0.75.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.
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/PKG-INFO +2 -1
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/audio_fetcher.py +211 -48
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/instrumental_review/server.py +56 -4
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/instrumental_review/static/index.html +17 -1
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/utils/remote_cli.py +112 -29
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/pyproject.toml +2 -1
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/LICENSE +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/README.md +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/audio_processor.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/config.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/file_handler.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/instrumental_review/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/instrumental_review/analyzer.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/instrumental_review/editor.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/instrumental_review/models.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/instrumental_review/waveform.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/karaoke_finalise/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/karaoke_finalise/karaoke_finalise.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/karaoke_gen.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/lyrics_processor.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/metadata.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/base.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/context.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/executors/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/executors/local.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/executors/remote.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/stages/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/stages/finalize.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/stages/render.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/stages/screens.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/stages/separation.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/pipeline/stages/transcription.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/resources/AvenirNext-Bold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/resources/Montserrat-Bold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/resources/Oswald-Bold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/resources/Oswald-SemiBold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/resources/Zurich_Cn_BT_Bold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/style_loader.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/utils/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/utils/bulk_cli.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/utils/cli_args.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/utils/gen_cli.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/video_background_processor.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/karaoke_gen/video_generator.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/cli/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/cli/cli_main.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/core/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/core/config.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/core/controller.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/adapter.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/agent.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/aggregator.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/collector.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/retention.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/feedback/store.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/ambiguous.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/background_vocals.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/base.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/complex_multi_error.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/extra_words.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/no_error.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/punctuation.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/registry.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/repeated_section.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/handlers/sound_alike.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/ai_correction.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/correction_session.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/enums.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/human_feedback.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/learning_data.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/observability_metrics.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/schemas.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/models/utils.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/langfuse_integration.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/metrics.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/observability/performance.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/prompts/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/prompts/classifier.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/base.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/circuit_breaker.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/config.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/constants.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/health.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/langchain_bridge.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/model_factory.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/response_cache.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/response_parser.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/providers/retry_executor.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/router.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/consensus_workflow.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/correction_graph.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/agentic/workflows/feedback_workflow.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/anchor_sequence.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/corrector.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/feedback/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/feedback/schemas.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/feedback/store.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/base.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/extend_anchor.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/levenshtein.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/llm.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/llm_providers.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/no_space_punct_match.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/relaxed_word_count_match.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/repeat.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/sound_alike.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/syllables_match.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/word_count_match.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/handlers/word_operations.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/operations.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/phrase_analyzer.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/correction/text_utils.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/.gitignore +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/.yarn/releases/yarn-4.7.0.cjs +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/.yarnrc.yml +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/README.md +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/eslint.config.js +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/index.html +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/package.json +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/android-chrome-192x192.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/android-chrome-512x512.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/apple-touch-icon.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/favicon-16x16.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/favicon-32x32.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/favicon.ico +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/public/nomad-karaoke-logo.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/App.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/api.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AIFeedbackModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AddLyricsModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AgenticCorrectionMetrics.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectionAnnotationModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectionDetailCard.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/CorrectionMetrics.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/DurationTimelineView.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditActionBar.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/EditWordList.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/FileUpload.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/FindReplaceModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/Header.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/SyncControls.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/TimelineCanvas.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/UpcomingWordsBar.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/LyricsSynchronizer/index.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/MetricsDashboard.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ModeSelectionModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ModeSelector.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ModelSelector.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/PreviewVideoSection.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ReferenceView.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/ReviewChangesModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/SegmentDetailsModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/TimingOffsetModal.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/WordDivider.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/components/SourceSelector.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/components/Word.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/constants.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/hooks/useWordClick.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/styles.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/types.js +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/types.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/keyboardHandlers.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/localStorage.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/referenceLineCalculator.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/segmentOperations.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/timingUtils.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/components/shared/utils/wordUtils.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/hooks/useManualSync.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/main.tsx +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/theme.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/types/global.d.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/types.js +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/types.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/validation.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/src/vite-env.d.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.app.json +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.json +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.node.json +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/tsconfig.tsbuildinfo +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/update_version.js +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/vite.config.d.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/vite.config.js +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/vite.config.ts +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/android-chrome-192x192.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/android-chrome-512x512.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/apple-touch-icon.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/assets/index-COYImAcx.js +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/assets/index-COYImAcx.js.map +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/favicon-16x16.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/favicon-32x32.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/favicon.ico +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/index.html +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/web_assets/nomad-karaoke-logo.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/frontend/yarn.lock +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/base_lyrics_provider.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/file_provider.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/genius.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/lrclib.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/musixmatch.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/spotify.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/lyrics/user_input_provider.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/ass.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/ass_specs.txt +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/config.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/constants.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/event.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/formatters.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/lyrics_line.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/lyrics_screen.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/section_detector.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/section_screen.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/ass/style.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdg.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/cdg.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/composer.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/config.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/images/instrumental.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/images/intro.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/pack.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/render.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/centertexttoplogobottomtext.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/circlein.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/circleout.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/fizzle.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/largecentertexttoplogo.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/rectangle.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/spiral.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/topleftmusicalnotes.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wipein.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wipeleft.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wipeout.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/transitions/wiperight.png +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/cdgmaker/utils.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/countdown_processor.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/AvenirNext-Bold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/DMSans-VariableFont_opsz,wght.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/DMSerifDisplay-Regular.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/Oswald-SemiBold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/Zurich_Cn_BT_Bold.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/arial.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/georgia.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/fonts/verdana.ttf +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/generator.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/lrc_to_cdg.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/lyrics_file.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/plain_text.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/segment_resizer.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/subtitles.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/output/video.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/review/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/review/server.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/storage/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/storage/dropbox.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/audioshake.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/base_transcriber.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/transcribers/whisper.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/types.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.2}/lyrics_transcriber_temp/lyrics_transcriber/utils/__init__.py +0 -0
- {karaoke_gen-0.72.2 → karaoke_gen-0.75.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.
|
|
3
|
+
Version: 0.75.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
|
|
@@ -48,6 +48,7 @@ Requires-Dist: lyrics-converter (>=0.2.1)
|
|
|
48
48
|
Requires-Dist: lyricsgenius (>=3)
|
|
49
49
|
Requires-Dist: matplotlib (>=3)
|
|
50
50
|
Requires-Dist: metaphone (>=0.6)
|
|
51
|
+
Requires-Dist: nest-asyncio (>=1.5)
|
|
51
52
|
Requires-Dist: nltk (>=3.9)
|
|
52
53
|
Requires-Dist: numpy (>=2)
|
|
53
54
|
Requires-Dist: ollama (>=0.4.7)
|
|
@@ -26,6 +26,9 @@ class AudioSearchResult:
|
|
|
26
26
|
|
|
27
27
|
Used by both local CLI and cloud backend. Supports serialization
|
|
28
28
|
for Firestore storage via to_dict()/from_dict().
|
|
29
|
+
|
|
30
|
+
For rich display, this class can serialize the full flacfetch Release
|
|
31
|
+
data so remote CLIs can use flacfetch's shared display functions.
|
|
29
32
|
"""
|
|
30
33
|
|
|
31
34
|
title: str
|
|
@@ -36,15 +39,18 @@ class AudioSearchResult:
|
|
|
36
39
|
quality: Optional[str] = None # e.g., "FLAC", "320kbps", etc.
|
|
37
40
|
source_id: Optional[str] = None # Unique ID from the source
|
|
38
41
|
index: int = 0 # Index in the results list (for API selection)
|
|
42
|
+
seeders: Optional[int] = None # Number of seeders (for torrent sources)
|
|
43
|
+
target_file: Optional[str] = None # Target filename in the release
|
|
39
44
|
# Raw result object from the provider (for download) - not serialized
|
|
40
45
|
raw_result: Optional[object] = field(default=None, repr=False)
|
|
41
46
|
|
|
42
47
|
def to_dict(self) -> Dict[str, Any]:
|
|
43
48
|
"""Convert to dict for JSON/Firestore serialization.
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
Includes full flacfetch Release data if available, enabling
|
|
51
|
+
remote CLIs to use flacfetch's shared display functions.
|
|
46
52
|
"""
|
|
47
|
-
|
|
53
|
+
result = {
|
|
48
54
|
"title": self.title,
|
|
49
55
|
"artist": self.artist,
|
|
50
56
|
"url": self.url,
|
|
@@ -53,7 +59,28 @@ class AudioSearchResult:
|
|
|
53
59
|
"quality": self.quality,
|
|
54
60
|
"source_id": self.source_id,
|
|
55
61
|
"index": self.index,
|
|
62
|
+
"seeders": self.seeders,
|
|
63
|
+
"target_file": self.target_file,
|
|
56
64
|
}
|
|
65
|
+
|
|
66
|
+
# If we have a raw_result (flacfetch Release), include its full data
|
|
67
|
+
# This enables rich display on the remote CLI
|
|
68
|
+
if self.raw_result:
|
|
69
|
+
try:
|
|
70
|
+
release_dict = self.raw_result.to_dict()
|
|
71
|
+
# Merge Release fields into result (they may override basic fields)
|
|
72
|
+
for key in ['year', 'label', 'edition_info', 'release_type', 'channel',
|
|
73
|
+
'view_count', 'size_bytes', 'target_file_size', 'track_pattern',
|
|
74
|
+
'match_score', 'formatted_size', 'formatted_duration',
|
|
75
|
+
'formatted_views', 'is_lossless', 'quality_str', 'quality']:
|
|
76
|
+
if key in release_dict:
|
|
77
|
+
# Use 'quality_data' for the quality dict to avoid confusion with quality string
|
|
78
|
+
result_key = 'quality_data' if key == 'quality' else key
|
|
79
|
+
result[result_key] = release_dict[key]
|
|
80
|
+
except AttributeError:
|
|
81
|
+
pass # raw_result doesn't have to_dict() method
|
|
82
|
+
|
|
83
|
+
return result
|
|
57
84
|
|
|
58
85
|
@classmethod
|
|
59
86
|
def from_dict(cls, data: Dict[str, Any]) -> "AudioSearchResult":
|
|
@@ -67,6 +94,8 @@ class AudioSearchResult:
|
|
|
67
94
|
quality=data.get("quality"),
|
|
68
95
|
source_id=data.get("source_id"),
|
|
69
96
|
index=data.get("index", 0),
|
|
97
|
+
seeders=data.get("seeders"),
|
|
98
|
+
target_file=data.get("target_file"),
|
|
70
99
|
raw_result=None, # Not stored in serialized form
|
|
71
100
|
)
|
|
72
101
|
|
|
@@ -241,6 +270,47 @@ class FlacFetchAudioFetcher(AudioFetcher):
|
|
|
241
270
|
self._ops_api_key = ops_api_key or os.environ.get("OPS_API_KEY")
|
|
242
271
|
self._provider_priority = provider_priority
|
|
243
272
|
self._manager = None
|
|
273
|
+
self._transmission_available = None # Cached result of Transmission check
|
|
274
|
+
|
|
275
|
+
def _check_transmission_available(self) -> bool:
|
|
276
|
+
"""
|
|
277
|
+
Check if Transmission daemon is available for torrent downloads.
|
|
278
|
+
|
|
279
|
+
This prevents adding tracker providers (Redacted/OPS) when Transmission
|
|
280
|
+
isn't running, which would result in search results that can't be downloaded.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
True if Transmission is available and responsive, False otherwise.
|
|
284
|
+
"""
|
|
285
|
+
if self._transmission_available is not None:
|
|
286
|
+
self.logger.info(f"[Transmission] Using cached status: available={self._transmission_available}")
|
|
287
|
+
return self._transmission_available
|
|
288
|
+
|
|
289
|
+
host = os.environ.get("TRANSMISSION_HOST", "localhost")
|
|
290
|
+
port = int(os.environ.get("TRANSMISSION_PORT", "9091"))
|
|
291
|
+
self.logger.info(f"[Transmission] Checking availability at {host}:{port}")
|
|
292
|
+
|
|
293
|
+
try:
|
|
294
|
+
import transmission_rpc
|
|
295
|
+
self.logger.info(f"[Transmission] transmission_rpc imported successfully")
|
|
296
|
+
|
|
297
|
+
client = transmission_rpc.Client(host=host, port=port, timeout=5)
|
|
298
|
+
self.logger.info(f"[Transmission] Client created, calling session_stats()...")
|
|
299
|
+
|
|
300
|
+
# Simple test to verify connection works
|
|
301
|
+
stats = client.session_stats()
|
|
302
|
+
self.logger.info(f"[Transmission] Connected! Download dir: {getattr(stats, 'download_dir', 'unknown')}")
|
|
303
|
+
|
|
304
|
+
self._transmission_available = True
|
|
305
|
+
except ImportError as e:
|
|
306
|
+
self._transmission_available = False
|
|
307
|
+
self.logger.warning(f"[Transmission] transmission_rpc not installed: {e}")
|
|
308
|
+
except Exception as e:
|
|
309
|
+
self._transmission_available = False
|
|
310
|
+
self.logger.warning(f"[Transmission] Connection failed to {host}:{port}: {type(e).__name__}: {e}")
|
|
311
|
+
|
|
312
|
+
self.logger.info(f"[Transmission] Final status: available={self._transmission_available}")
|
|
313
|
+
return self._transmission_available
|
|
244
314
|
|
|
245
315
|
def _get_manager(self):
|
|
246
316
|
"""Lazily initialize and return the FetchManager."""
|
|
@@ -251,30 +321,46 @@ class FlacFetchAudioFetcher(AudioFetcher):
|
|
|
251
321
|
from flacfetch.downloaders.youtube import YoutubeDownloader
|
|
252
322
|
|
|
253
323
|
# Try to import TorrentDownloader (has optional dependencies)
|
|
324
|
+
TorrentDownloader = None
|
|
254
325
|
try:
|
|
255
326
|
from flacfetch.downloaders.torrent import TorrentDownloader
|
|
256
327
|
except ImportError:
|
|
257
|
-
TorrentDownloader = None
|
|
258
328
|
self.logger.debug("TorrentDownloader not available (missing dependencies)")
|
|
259
329
|
|
|
260
330
|
self._manager = FetchManager()
|
|
261
331
|
|
|
332
|
+
# Only add tracker providers if we can actually download from them
|
|
333
|
+
# This requires both TorrentDownloader and a running Transmission daemon
|
|
334
|
+
has_torrent_downloader = TorrentDownloader is not None
|
|
335
|
+
transmission_available = self._check_transmission_available()
|
|
336
|
+
can_use_trackers = has_torrent_downloader and transmission_available
|
|
337
|
+
|
|
338
|
+
self.logger.info(
|
|
339
|
+
f"[FlacFetcher] Provider setup: TorrentDownloader={has_torrent_downloader}, "
|
|
340
|
+
f"Transmission={transmission_available}, can_use_trackers={can_use_trackers}"
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
if not can_use_trackers and (self._redacted_api_key or self._ops_api_key):
|
|
344
|
+
self.logger.warning(
|
|
345
|
+
"[FlacFetcher] Tracker providers (Redacted/OPS) DISABLED: "
|
|
346
|
+
f"TorrentDownloader={has_torrent_downloader}, Transmission={transmission_available}. "
|
|
347
|
+
"Only YouTube sources will be used."
|
|
348
|
+
)
|
|
349
|
+
|
|
262
350
|
# Add providers and downloaders based on available API keys
|
|
263
|
-
if self._redacted_api_key:
|
|
351
|
+
if self._redacted_api_key and can_use_trackers:
|
|
264
352
|
from flacfetch.providers.redacted import RedactedProvider
|
|
265
353
|
|
|
266
354
|
self._manager.add_provider(RedactedProvider(api_key=self._redacted_api_key))
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
self.logger.debug("Added Redacted provider")
|
|
355
|
+
self._manager.register_downloader("Redacted", TorrentDownloader())
|
|
356
|
+
self.logger.info("[FlacFetcher] Added Redacted provider with TorrentDownloader")
|
|
270
357
|
|
|
271
|
-
if self._ops_api_key:
|
|
358
|
+
if self._ops_api_key and can_use_trackers:
|
|
272
359
|
from flacfetch.providers.ops import OPSProvider
|
|
273
360
|
|
|
274
361
|
self._manager.add_provider(OPSProvider(api_key=self._ops_api_key))
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
self.logger.debug("Added OPS provider")
|
|
362
|
+
self._manager.register_downloader("OPS", TorrentDownloader())
|
|
363
|
+
self.logger.info("[FlacFetcher] Added OPS provider with TorrentDownloader")
|
|
278
364
|
|
|
279
365
|
# Always add YouTube as a fallback provider with its downloader
|
|
280
366
|
self._manager.add_provider(YoutubeProvider())
|
|
@@ -325,6 +411,8 @@ class FlacFetchAudioFetcher(AudioFetcher):
|
|
|
325
411
|
quality=quality_str,
|
|
326
412
|
source_id=getattr(result, "info_hash", None),
|
|
327
413
|
index=i, # Set index for API selection
|
|
414
|
+
seeders=getattr(result, "seeders", None),
|
|
415
|
+
target_file=getattr(result, "target_file", None),
|
|
328
416
|
raw_result=result,
|
|
329
417
|
)
|
|
330
418
|
)
|
|
@@ -489,13 +577,15 @@ class FlacFetchAudioFetcher(AudioFetcher):
|
|
|
489
577
|
self.logger.info(f"Downloading from {getattr(selected, 'source_name', 'Unknown')}...")
|
|
490
578
|
|
|
491
579
|
try:
|
|
492
|
-
|
|
580
|
+
# Use interruptible download so Ctrl+C works during torrent downloads
|
|
581
|
+
filepath = self._interruptible_download(
|
|
582
|
+
manager,
|
|
493
583
|
selected,
|
|
494
584
|
output_path=output_dir,
|
|
495
585
|
output_filename=output_filename,
|
|
496
586
|
)
|
|
497
587
|
|
|
498
|
-
if filepath
|
|
588
|
+
if not filepath:
|
|
499
589
|
raise DownloadError(f"Download returned no file path for: {artist} - {title}")
|
|
500
590
|
|
|
501
591
|
self.logger.info(f"Downloaded to: {filepath}")
|
|
@@ -581,6 +671,111 @@ class FlacFetchAudioFetcher(AudioFetcher):
|
|
|
581
671
|
signal.signal(signal.SIGINT, original_handler)
|
|
582
672
|
_interrupt_requested = False
|
|
583
673
|
|
|
674
|
+
def _interruptible_download(self, manager, selected, output_path: str, output_filename: str) -> str:
|
|
675
|
+
"""
|
|
676
|
+
Run download in a way that can be interrupted by Ctrl+C.
|
|
677
|
+
|
|
678
|
+
The flacfetch/transmission download is a blocking operation that doesn't
|
|
679
|
+
respond to SIGINT while running (especially for torrent downloads).
|
|
680
|
+
This method runs it in a background thread and periodically checks for interrupts.
|
|
681
|
+
|
|
682
|
+
Args:
|
|
683
|
+
manager: The FetchManager instance
|
|
684
|
+
selected: The selected result to download
|
|
685
|
+
output_path: Directory to save the file
|
|
686
|
+
output_filename: Filename to save as
|
|
687
|
+
|
|
688
|
+
Returns:
|
|
689
|
+
Path to the downloaded file
|
|
690
|
+
|
|
691
|
+
Raises:
|
|
692
|
+
UserCancelledError: If user presses Ctrl+C during download
|
|
693
|
+
DownloadError: If download fails
|
|
694
|
+
"""
|
|
695
|
+
global _interrupt_requested
|
|
696
|
+
_interrupt_requested = False
|
|
697
|
+
result_container = {"filepath": None, "error": None}
|
|
698
|
+
was_cancelled = False
|
|
699
|
+
|
|
700
|
+
def do_download():
|
|
701
|
+
try:
|
|
702
|
+
result_container["filepath"] = manager.download(
|
|
703
|
+
selected,
|
|
704
|
+
output_path=output_path,
|
|
705
|
+
output_filename=output_filename,
|
|
706
|
+
)
|
|
707
|
+
except Exception as e:
|
|
708
|
+
result_container["error"] = e
|
|
709
|
+
|
|
710
|
+
# Set up signal handler for immediate response to Ctrl+C
|
|
711
|
+
original_handler = signal.getsignal(signal.SIGINT)
|
|
712
|
+
|
|
713
|
+
def interrupt_handler(signum, frame):
|
|
714
|
+
global _interrupt_requested
|
|
715
|
+
_interrupt_requested = True
|
|
716
|
+
# Print immediately so user knows it was received
|
|
717
|
+
print("\nCancelling download... please wait (may take a few seconds)", file=sys.stderr)
|
|
718
|
+
|
|
719
|
+
signal.signal(signal.SIGINT, interrupt_handler)
|
|
720
|
+
|
|
721
|
+
try:
|
|
722
|
+
# Start download in background thread
|
|
723
|
+
download_thread = threading.Thread(target=do_download, daemon=True)
|
|
724
|
+
download_thread.start()
|
|
725
|
+
|
|
726
|
+
# Wait for completion with periodic interrupt checks
|
|
727
|
+
while download_thread.is_alive():
|
|
728
|
+
download_thread.join(timeout=0.2) # Check every 200ms
|
|
729
|
+
if _interrupt_requested:
|
|
730
|
+
was_cancelled = True
|
|
731
|
+
# Clean up any pending torrents before raising
|
|
732
|
+
self._cleanup_transmission_torrents(selected)
|
|
733
|
+
raise UserCancelledError("Download cancelled by user (Ctrl+C)")
|
|
734
|
+
|
|
735
|
+
# Check for errors from the download
|
|
736
|
+
if result_container["error"] is not None:
|
|
737
|
+
raise result_container["error"]
|
|
738
|
+
|
|
739
|
+
return result_container["filepath"]
|
|
740
|
+
|
|
741
|
+
finally:
|
|
742
|
+
# Restore original signal handler
|
|
743
|
+
signal.signal(signal.SIGINT, original_handler)
|
|
744
|
+
_interrupt_requested = False
|
|
745
|
+
|
|
746
|
+
def _cleanup_transmission_torrents(self, selected) -> None:
|
|
747
|
+
"""
|
|
748
|
+
Clean up any torrents in Transmission that were started for this download.
|
|
749
|
+
|
|
750
|
+
Called when a download is cancelled to remove incomplete torrents and their data.
|
|
751
|
+
|
|
752
|
+
Args:
|
|
753
|
+
selected: The selected result that was being downloaded
|
|
754
|
+
"""
|
|
755
|
+
try:
|
|
756
|
+
import transmission_rpc
|
|
757
|
+
host = os.environ.get("TRANSMISSION_HOST", "localhost")
|
|
758
|
+
port = int(os.environ.get("TRANSMISSION_PORT", "9091"))
|
|
759
|
+
client = transmission_rpc.Client(host=host, port=port, timeout=5)
|
|
760
|
+
|
|
761
|
+
# Get the release name to match against torrents
|
|
762
|
+
release_name = getattr(selected, 'name', None) or getattr(selected, 'title', None)
|
|
763
|
+
if not release_name:
|
|
764
|
+
self.logger.debug("[Transmission] No release name to match for cleanup")
|
|
765
|
+
return
|
|
766
|
+
|
|
767
|
+
# Find and remove matching incomplete torrents
|
|
768
|
+
torrents = client.get_torrents()
|
|
769
|
+
for torrent in torrents:
|
|
770
|
+
# Match by name similarity and incomplete status
|
|
771
|
+
if torrent.progress < 100 and release_name.lower() in torrent.name.lower():
|
|
772
|
+
self.logger.info(f"[Transmission] Removing cancelled torrent: {torrent.name}")
|
|
773
|
+
client.remove_torrent(torrent.id, delete_data=True)
|
|
774
|
+
|
|
775
|
+
except Exception as e:
|
|
776
|
+
# Don't fail the cancellation if cleanup fails
|
|
777
|
+
self.logger.debug(f"[Transmission] Cleanup failed (non-fatal): {e}")
|
|
778
|
+
|
|
584
779
|
def _interactive_select(self, results: list, artist: str, title: str) -> object:
|
|
585
780
|
"""
|
|
586
781
|
Present search results to the user for interactive selection.
|
|
@@ -632,41 +827,9 @@ class FlacFetchAudioFetcher(AudioFetcher):
|
|
|
632
827
|
Raises:
|
|
633
828
|
UserCancelledError: If user cancels selection
|
|
634
829
|
"""
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
for i, result in enumerate(results, 1):
|
|
640
|
-
# Use correct flacfetch attribute names
|
|
641
|
-
source_name = getattr(result, "source_name", "Unknown")
|
|
642
|
-
result_title = getattr(result, "title", "Unknown")
|
|
643
|
-
result_artist = getattr(result, "artist", "Unknown")
|
|
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)
|
|
648
|
-
|
|
649
|
-
# Format duration if available
|
|
650
|
-
duration_str = ""
|
|
651
|
-
if duration_seconds:
|
|
652
|
-
minutes = duration_seconds // 60
|
|
653
|
-
seconds = duration_seconds % 60
|
|
654
|
-
duration_str = f" [{minutes}:{seconds:02d}]"
|
|
655
|
-
|
|
656
|
-
# Format quality - it's a Quality object with __str__
|
|
657
|
-
quality_str = f" ({quality})" if quality else ""
|
|
658
|
-
|
|
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}")
|
|
666
|
-
|
|
667
|
-
print()
|
|
668
|
-
print(" 0. Cancel")
|
|
669
|
-
print()
|
|
830
|
+
# Use flacfetch's shared display function
|
|
831
|
+
from flacfetch import print_releases
|
|
832
|
+
print_releases(results, target_artist=artist, use_colors=True)
|
|
670
833
|
|
|
671
834
|
while True:
|
|
672
835
|
try:
|
|
@@ -11,6 +11,7 @@ Similar pattern to LyricsTranscriber's ReviewServer.
|
|
|
11
11
|
import logging
|
|
12
12
|
import os
|
|
13
13
|
from pathlib import Path
|
|
14
|
+
import socket
|
|
14
15
|
import threading
|
|
15
16
|
import webbrowser
|
|
16
17
|
from typing import List, Optional
|
|
@@ -358,23 +359,74 @@ class InstrumentalReviewServer:
|
|
|
358
359
|
</body>
|
|
359
360
|
</html>"""
|
|
360
361
|
|
|
362
|
+
@staticmethod
|
|
363
|
+
def _is_port_available(host: str, port: int) -> bool:
|
|
364
|
+
"""Check if a port is available for binding."""
|
|
365
|
+
try:
|
|
366
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
367
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
368
|
+
sock.bind((host, port))
|
|
369
|
+
return True
|
|
370
|
+
except OSError:
|
|
371
|
+
return False
|
|
372
|
+
|
|
373
|
+
@staticmethod
|
|
374
|
+
def _find_available_port(host: str, preferred_port: int, max_attempts: int = 100) -> int:
|
|
375
|
+
"""
|
|
376
|
+
Find an available port, starting with the preferred port.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
host: Host to bind to
|
|
380
|
+
preferred_port: The preferred port to try first
|
|
381
|
+
max_attempts: Maximum number of ports to try
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
An available port number
|
|
385
|
+
|
|
386
|
+
Raises:
|
|
387
|
+
RuntimeError: If no available port could be found
|
|
388
|
+
"""
|
|
389
|
+
# Try the preferred port first
|
|
390
|
+
if InstrumentalReviewServer._is_port_available(host, preferred_port):
|
|
391
|
+
return preferred_port
|
|
392
|
+
|
|
393
|
+
# Try subsequent ports
|
|
394
|
+
for offset in range(1, max_attempts):
|
|
395
|
+
port = preferred_port + offset
|
|
396
|
+
if port > 65535:
|
|
397
|
+
break
|
|
398
|
+
if InstrumentalReviewServer._is_port_available(host, port):
|
|
399
|
+
return port
|
|
400
|
+
|
|
401
|
+
# Last resort: let the OS assign a port
|
|
402
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
|
403
|
+
sock.bind((host, 0))
|
|
404
|
+
return sock.getsockname()[1]
|
|
405
|
+
|
|
361
406
|
def start_and_open_browser(self, port: int = 8765) -> str:
|
|
362
407
|
"""
|
|
363
408
|
Start server, open browser, and block until selection is submitted.
|
|
364
409
|
|
|
365
410
|
Args:
|
|
366
|
-
port:
|
|
411
|
+
port: Preferred port to run the server on. If unavailable, will
|
|
412
|
+
automatically find an available port.
|
|
367
413
|
|
|
368
414
|
Returns:
|
|
369
415
|
The user's selection ("clean", "with_backing", or "custom")
|
|
370
416
|
"""
|
|
371
417
|
self._app = self._create_app()
|
|
372
418
|
|
|
419
|
+
# Find an available port (handles concurrent CLI instances)
|
|
420
|
+
host = "127.0.0.1"
|
|
421
|
+
actual_port = self._find_available_port(host, port)
|
|
422
|
+
if actual_port != port:
|
|
423
|
+
logger.info(f"Port {port} in use, using port {actual_port} instead")
|
|
424
|
+
|
|
373
425
|
# Run uvicorn in a separate thread
|
|
374
426
|
config = uvicorn.Config(
|
|
375
427
|
self._app,
|
|
376
|
-
host=
|
|
377
|
-
port=
|
|
428
|
+
host=host,
|
|
429
|
+
port=actual_port,
|
|
378
430
|
log_level="warning",
|
|
379
431
|
)
|
|
380
432
|
server = uvicorn.Server(config)
|
|
@@ -389,7 +441,7 @@ class InstrumentalReviewServer:
|
|
|
389
441
|
import time
|
|
390
442
|
time.sleep(0.5)
|
|
391
443
|
|
|
392
|
-
url = f"http://localhost:{
|
|
444
|
+
url = f"http://localhost:{actual_port}/"
|
|
393
445
|
logger.info(f"Instrumental review server started at {url}")
|
|
394
446
|
|
|
395
447
|
# Open browser
|
|
@@ -1428,9 +1428,25 @@
|
|
|
1428
1428
|
<div class="success-screen">
|
|
1429
1429
|
<h2>✓ Selection Submitted</h2>
|
|
1430
1430
|
<p>You selected: <strong>${escapeHtml(selectionLabel)}</strong></p>
|
|
1431
|
-
<p style="color: var(--text-muted);">
|
|
1431
|
+
<p id="close-msg" style="color: var(--text-muted);">Closing in <span id="countdown">2</span>s...</p>
|
|
1432
1432
|
</div>
|
|
1433
1433
|
`;
|
|
1434
|
+
|
|
1435
|
+
// Auto-close window after 2 seconds
|
|
1436
|
+
let countdown = 2;
|
|
1437
|
+
const countdownEl = document.getElementById('countdown');
|
|
1438
|
+
const countdownInterval = setInterval(() => {
|
|
1439
|
+
countdown--;
|
|
1440
|
+
if (countdownEl) countdownEl.textContent = countdown;
|
|
1441
|
+
if (countdown <= 0) {
|
|
1442
|
+
clearInterval(countdownInterval);
|
|
1443
|
+
// Try to close the window (works for windows opened by script)
|
|
1444
|
+
window.close();
|
|
1445
|
+
// If window.close() didn't work, update message
|
|
1446
|
+
const msg = document.getElementById('close-msg');
|
|
1447
|
+
if (msg) msg.textContent = 'You can close this window now.';
|
|
1448
|
+
}
|
|
1449
|
+
}, 1000);
|
|
1434
1450
|
} catch (error) {
|
|
1435
1451
|
showError(error.message);
|
|
1436
1452
|
if (btn) {
|