Spaces:
Runtime error
Runtime error
| """Audio processing utilities for OutofLipSync""" | |
| import os | |
| import subprocess | |
| from ffmpy import FFmpeg, FFRuntimeError | |
| def get_audio_duration(audio_path: str, max_duration: float = 30.0) -> float: | |
| """Get audio file duration, raise error if exceeds max_duration | |
| Args: | |
| audio_path: Path to audio file | |
| max_duration: Maximum duration in seconds (default 30) | |
| Returns: | |
| Duration in seconds | |
| Raises: | |
| ValueError: If audio duration exceeds max_duration | |
| """ | |
| cmd = [ | |
| "ffprobe", | |
| "-v", | |
| "error", | |
| "-show_entries", | |
| "format=duration", | |
| "-of", | |
| "default=noprint_wrappers=1:nokey=1", | |
| audio_path, | |
| ] | |
| result = subprocess.run(cmd, capture_output=True, text=True, check=True) | |
| duration = float(result.stdout.strip()) | |
| return duration | |
| # def prepare_target_audio(audio_path: str, output_dir: str) -> tuple: | |
| # """Prepare target audio for lipsync (DEPRECATED - use prepare_audio_for_lipsync instead) | |
| # | |
| # Args: | |
| # audio_path: Path to target audio | |
| # output_dir: Output directory | |
| # | |
| # Returns: | |
| # (audio_16k_path, audio_upsampled_path) | |
| # """ | |
| # audio_16k = os.path.join(output_dir, "audio_16k.wav") | |
| # audio_upsampled = os.path.join(output_dir, "audio_upsampled.wav") | |
| # | |
| # ffmpeg1 = FFmpeg( | |
| # inputs={audio_path: None}, | |
| # outputs={ | |
| # audio_16k: [ | |
| # "-ar", | |
| # "16000", | |
| # "-ac", | |
| # "1", | |
| # "-acodec", | |
| # "pcm_s16le", | |
| # "-loglevel", | |
| # "error", | |
| # "-y", | |
| # ] | |
| # }, | |
| # ) | |
| # try: | |
| # ffmpeg1.run() | |
| # except FFRuntimeError as e: | |
| # raise Exception(f"FFmpeg failed to convert to 16k: {e}") | |
| # | |
| # ffmpeg2 = FFmpeg( | |
| # inputs={audio_16k: None}, | |
| # outputs={ | |
| # audio_upsampled: [ | |
| # "-ar", | |
| # "48000", | |
| # "-ac", | |
| # "1", | |
| # "-acodec", | |
| # "pcm_s16le", | |
| # "-loglevel", | |
| # "error", | |
| # "-y", | |
| # ] | |
| # }, | |
| # ) | |
| # try: | |
| # ffmpeg2.run() | |
| # except FFRuntimeError as e: | |
| # raise Exception(f"FFmpeg failed to upsample to 48k: {e}") | |
| # | |
| # return audio_16k, audio_upsampled | |
| def prepare_audio_for_lipsync(audio_path: str, output_dir: str) -> str: | |
| """Chuẩn bị audio 16kHz mono cho lipsync pipeline | |
| Args: | |
| audio_path: Path audio gốc | |
| output_dir: Output directory | |
| Returns: | |
| Path audio 16k WAV | |
| """ | |
| audio_16k = os.path.join(output_dir, "audio_16k.wav") | |
| ffmpeg = FFmpeg( | |
| inputs={audio_path: None}, | |
| outputs={ | |
| audio_16k: [ | |
| "-ar", | |
| "16000", | |
| "-ac", | |
| "1", | |
| "-acodec", | |
| "pcm_s16le", | |
| "-loglevel", | |
| "error", | |
| "-y", | |
| ] | |
| }, | |
| ) | |
| try: | |
| ffmpeg.run() | |
| except FFRuntimeError as e: | |
| raise Exception(f"FFmpeg failed to convert to 16k: {e}") | |
| return audio_16k | |
| def prepare_audio_for_youtube_aac(audio_path: str, output_dir: str) -> str: | |
| """Chuẩn bị audio theo chuẩn YouTube (AAC) | |
| Args: | |
| audio_path: Path audio gốc | |
| output_dir: Output directory | |
| Returns: | |
| Path audio YouTube (AAC) | |
| """ | |
| from config import ( | |
| YOUTUBE_AUDIO_CODEC, | |
| YOUTUBE_AUDIO_BITRATE, | |
| YOUTUBE_AUDIO_SAMPLE_RATE, | |
| ) | |
| output_path = os.path.join(output_dir, "audio_youtube.aac") | |
| ffmpeg = FFmpeg( | |
| inputs={audio_path: None}, | |
| outputs={ | |
| output_path: [ | |
| "-ar", | |
| str(YOUTUBE_AUDIO_SAMPLE_RATE), | |
| "-ac", | |
| "2", | |
| "-acodec", | |
| YOUTUBE_AUDIO_CODEC, | |
| "-b:a", | |
| YOUTUBE_AUDIO_BITRATE, | |
| "-loglevel", | |
| "error", | |
| "-y", | |
| ] | |
| }, | |
| ) | |
| try: | |
| ffmpeg.run() | |
| except FFRuntimeError as e: | |
| raise Exception(f"FFmpeg failed to prepare audio for YouTube: {e}") | |
| return output_path | |
| def prepare_audio_for_youtube(audio_path: str, output_dir: str) -> str: | |
| """ | |
| Chuẩn bị audio tối ưu cho YouTube | |
| Args: | |
| audio_path: Path to audio file (WAV) | |
| output_dir: Output directory | |
| Returns: | |
| Path to audio file (WAV 48kHz PCM) | |
| """ | |
| output_path = os.path.join(output_dir, "audio_final.wav") | |
| ffmpeg = FFmpeg( | |
| inputs={audio_path: None}, | |
| outputs={ | |
| output_path: [ | |
| "-ar", | |
| "48000", | |
| "-ac", | |
| "2", | |
| "-acodec", | |
| "pcm_s16le", | |
| "-loglevel", | |
| "error", | |
| "-y", | |
| ] | |
| }, | |
| ) | |
| try: | |
| ffmpeg.run() | |
| except FFRuntimeError as e: | |
| raise Exception(f"FFmpeg failed to prepare audio for YouTube: {e}") | |
| return output_path | |