#!/usr/bin/env python3 """Helper utilities for transcribe.sh - replaces inline Python snippets.""" import argparse import math import os import sys import array import wave def get_python_version() -> str: """Return Python major.minor version string.""" return f"{sys.version_info.major}.{sys.version_info.minor}" def check_faster_whisper() -> bool: """Check if faster_whisper is importable. Exit 7 if not.""" try: import faster_whisper # noqa: F401 return True except ImportError: return False def check_diarization_deps() -> bool: """Check if diarization dependencies are available. Returns False with warning if missing.""" try: import soundfile # noqa: F401 import speechbrain # noqa: F401 import torch # noqa: F401 return True except Exception as e: print(f"[WARN] Diarization deps missing offline ({e}); speaker labels will be skipped.") return False def check_ctranslate2() -> bool: """Check if ctranslate2 is importable.""" try: import ctranslate2 # noqa: F401 return True except ImportError: return False def print_deps_installed(): """Print confirmation that Python dependencies are installed.""" print(f"[PY] Python {sys.version.split()[0]} dependencies installed.") def generate_sine_wav(outfile: str, frequency: float = 1000.0, duration: int = 3, sample_rate: int = 16000, amplitude: float = 0.3) -> bool: """Generate a sine wave WAV file using only Python stdlib. Args: outfile: Output WAV file path frequency: Tone frequency in Hz (default: 1000) duration: Duration in seconds (default: 3) sample_rate: Sample rate in Hz (default: 16000) amplitude: Amplitude 0.0-1.0 (default: 0.3) Returns: True on success, False on failure """ try: n_samples = sample_rate * duration data = array.array("h", [ int(max(-1.0, min(1.0, amplitude * math.sin(2 * math.pi * frequency * (i / sample_rate)))) * 32767) for i in range(n_samples) ]) with wave.open(outfile, "w") as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(sample_rate) wf.writeframes(data.tobytes()) return True except Exception as e: print(f"[ERROR] Failed to generate WAV: {e}", file=sys.stderr) return False def prepare_model(model_name: str, model_dir: str) -> bool: """Download a whisper model for offline use. Args: model_name: Model name (tiny, base, small, medium, large-v3, etc.) model_dir: Directory to store the model Returns: True on success, False on failure """ try: from faster_whisper import WhisperModel # Enable HuggingFace Hub progress bars for model download try: from huggingface_hub import logging as hf_logging hf_logging.set_verbosity_info() import huggingface_hub huggingface_hub.constants.HF_HUB_DISABLE_PROGRESS_BARS = False os.environ["HF_HUB_DISABLE_PROGRESS_BARS"] = "0" except ImportError: pass print(f"[PY] Preparing model '{model_name}' into {model_dir}") print("[INFO] Downloading model files (progress bar should appear below)...", flush=True) WhisperModel(model_name, device="cpu", compute_type="int8", download_root=model_dir) print("[PY] Model prepared.") return True except Exception as e: print(f"[ERROR] Failed to prepare model: {e}", file=sys.stderr) return False def test_cuda() -> bool: """Test CUDA initialization with faster-whisper. Returns: True if CUDA works, False otherwise """ try: from faster_whisper import WhisperModel WhisperModel("tiny", device="cuda", compute_type="float16") print("[PY] CUDA test init succeeded.") return True except Exception as e: print(f"[ERROR] CUDA test failed: {e}", file=sys.stderr) return False def main(): parser = argparse.ArgumentParser( description="Helper utilities for transcribe.sh", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Commands: python-version Print Python major.minor version check-faster-whisper Check if faster_whisper is installed (exit 7 if not) check-diarization Check diarization deps (warn if missing) check-ctranslate2 Check if ctranslate2 is installed (exit 1 if not) deps-installed Print deps installed confirmation message generate-wav FILE Generate a 3s 1kHz sine wave WAV file prepare-model Download model for offline use (requires --model and --model-dir) test-cuda Test CUDA initialization """) parser.add_argument("command", choices=[ "python-version", "check-faster-whisper", "check-diarization", "check-ctranslate2", "deps-installed", "generate-wav", "prepare-model", "test-cuda", ], help="Command to run") parser.add_argument("--file", help="Output file path (for generate-wav)") parser.add_argument("--model", help="Model name (for prepare-model)") parser.add_argument("--model-dir", help="Model directory (for prepare-model)") args = parser.parse_args() if args.command == "python-version": print(get_python_version()) elif args.command == "check-faster-whisper": if not check_faster_whisper(): print("Python dependency 'faster_whisper' not found in offline mode. Run with --online to install.", file=sys.stderr) sys.exit(7) elif args.command == "check-diarization": check_diarization_deps() elif args.command == "check-ctranslate2": if not check_ctranslate2(): sys.exit(1) elif args.command == "deps-installed": print_deps_installed() elif args.command == "generate-wav": if not args.file: print("--file is required for generate-wav", file=sys.stderr) sys.exit(2) if not generate_sine_wav(args.file): sys.exit(1) elif args.command == "prepare-model": if not args.model or not args.model_dir: print("--model and --model-dir are required for prepare-model", file=sys.stderr) sys.exit(2) if not prepare_model(args.model, args.model_dir): sys.exit(1) elif args.command == "test-cuda": if not test_cuda(): sys.exit(1) if __name__ == "__main__": main()