| import os
|
| import shutil
|
| from pathlib import Path
|
| from typing import Callable, Dict, Any
|
|
|
|
|
| ffmpeg_path = shutil.which("ffmpeg") or shutil.which("ffmpeg.exe")
|
|
|
| if ffmpeg_path:
|
| os.environ["FFMPEG_BINARY"] = ffmpeg_path
|
|
|
| from pydub import AudioSegment
|
|
|
|
|
| class AudioConverter:
|
|
|
| SUPPORTED_FORMATS = {
|
| ".mp3",
|
| ".wav",
|
| ".flac",
|
| ".ogg",
|
| ".aac",
|
| ".m4a",
|
| ".wma"
|
| }
|
|
|
| def convert(
|
| self,
|
| input_path: str,
|
| output_path: str,
|
| options: Dict[str, Any] | None = None,
|
| progress_callback: Callable | None = None
|
| ) -> bool:
|
|
|
| options = options or {}
|
|
|
| try:
|
| self._update(progress_callback, 10)
|
|
|
|
|
| if not os.path.exists(input_path):
|
| raise FileNotFoundError(f"Input file not found: {input_path}")
|
|
|
| input_ext = Path(input_path).suffix.lower()
|
| output_ext = Path(output_path).suffix.lower()
|
|
|
|
|
| if input_ext not in self.SUPPORTED_FORMATS:
|
| raise ValueError(f"Unsupported input format: {input_ext}")
|
|
|
| if output_ext not in self.SUPPORTED_FORMATS:
|
| raise ValueError(f"Unsupported output format: {output_ext}")
|
|
|
|
|
| Path(output_path).parent.mkdir(
|
| parents=True,
|
| exist_ok=True
|
| )
|
|
|
|
|
| audio = AudioSegment.from_file(input_path)
|
|
|
| self._update(progress_callback, 35)
|
|
|
|
|
| start = int(options.get("start_ms", 0))
|
| end = int(options.get("end_ms", len(audio)))
|
|
|
| if start < 0:
|
| start = 0
|
|
|
| if end > len(audio):
|
| end = len(audio)
|
|
|
| audio = audio[start:end]
|
|
|
| self._update(progress_callback, 55)
|
|
|
|
|
| if options.get("normalize", True):
|
|
|
|
|
| if audio.dBFS != float("-inf"):
|
|
|
| target_dbfs = options.get("target_dbfs", -20.0)
|
|
|
| change = target_dbfs - audio.dBFS
|
|
|
| audio = audio.apply_gain(change)
|
|
|
| self._update(progress_callback, 75)
|
|
|
|
|
| bitrate = options.get("bitrate", "192k")
|
|
|
|
|
| export_format = output_ext.replace(".", "")
|
|
|
| export_args = {}
|
|
|
|
|
| if export_format != "wav":
|
| export_args["bitrate"] = bitrate
|
|
|
|
|
| if export_format == "aac":
|
| export_format = "adts"
|
|
|
|
|
| audio.export(
|
| output_path,
|
| format=export_format,
|
| **export_args
|
| )
|
|
|
| self._update(progress_callback, 100)
|
|
|
| return True
|
|
|
| except Exception as e:
|
| print(f"Audio conversion error: {e}")
|
| return False
|
|
|
| def _update(self, callback, value):
|
|
|
| try:
|
| if callback:
|
| callback(value)
|
| except Exception as e:
|
| print(f"Progress callback error: {e}") |