from pathlib import Path import shutil import subprocess import tempfile try: import imageio_ffmpeg except ImportError: imageio_ffmpeg = None TARGET_SAMPLE_RATE = 22050 SUPPORTED_AUDIO_EXTENSIONS = {".wav", ".mp3", ".m4a", ".ogg", ".flac", ".webm", ".aac"} def validate_audio_extension(file_path): extension = Path(file_path).suffix.lower() if extension not in SUPPORTED_AUDIO_EXTENSIONS: allowed = ", ".join(sorted(SUPPORTED_AUDIO_EXTENSIONS)).upper().replace(".", "") raise ValueError(f"Format audio tidak didukung: {extension}. Format yang didukung: {allowed}") def create_temp_wav_path(): temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".wav") temp_path = Path(temp_file.name) temp_file.close() return temp_path def configure_ffmpeg(): if imageio_ffmpeg is None: raise RuntimeError( "ffmpeg belum tersedia. Jalankan `pip install imageio-ffmpeg` " "di virtual environment backend." ) try: return imageio_ffmpeg.get_ffmpeg_exe() except Exception as error: raise RuntimeError( "ffmpeg belum tersedia. Jalankan `pip install imageio-ffmpeg` " "atau install ffmpeg di sistem operasi." ) from error def convert_audio_to_wav(input_path, output_path=None): input_path = Path(input_path) validate_audio_extension(input_path) if not input_path.exists(): raise FileNotFoundError(f"File audio tidak ditemukan: {input_path}") if input_path.suffix.lower() == ".wav" and output_path is None: return input_path output_path = Path(output_path) if output_path else create_temp_wav_path() output_path.parent.mkdir(parents=True, exist_ok=True) if input_path.suffix.lower() == ".wav": if input_path.resolve() != output_path.resolve(): shutil.copyfile(input_path, output_path) return output_path command = [ configure_ffmpeg(), "-y", "-i", str(input_path), "-vn", "-ac", "1", "-ar", str(TARGET_SAMPLE_RATE), "-f", "wav", str(output_path), ] try: result = subprocess.run(command, capture_output=True, text=True, check=False) except FileNotFoundError as error: raise RuntimeError( "ffmpeg tidak bisa dijalankan. Pastikan dependency `imageio-ffmpeg` " "sudah terpasang di virtual environment backend." ) from error if result.returncode != 0: error_message = result.stderr.strip() or "Tidak ada detail error dari ffmpeg." raise RuntimeError(f"Konversi audio gagal: {error_message}") if not output_path.exists(): raise RuntimeError("Konversi audio gagal: file WAV output tidak terbentuk.") return output_path def cleanup_temp_files(*paths): for path in paths: if path: Path(path).unlink(missing_ok=True)