Files
TalkEdit/backend/utils/audio_processing.py

75 lines
2.9 KiB
Python
Raw Permalink Normal View History

2025-01-28 17:00:03 -05:00
from pathlib import Path
import tempfile
import os
import logging
2025-01-28 17:00:03 -05:00
try:
from moviepy import AudioFileClip
except ImportError:
from moviepy.editor import AudioFileClip
logger = logging.getLogger(__name__)
_temp_audio_files = []
2025-01-28 17:00:03 -05:00
def extract_audio(video_path: Path):
"""Extract audio from a video file into a temp directory for automatic cleanup."""
2026-03-28 12:26:45 -06:00
logger.info(f"[extract_audio] Extracting audio from: {video_path}")
2025-01-28 17:00:03 -05:00
try:
audio = AudioFileClip(str(video_path))
2026-03-28 12:26:45 -06:00
if audio.duration is None or audio.duration == 0:
logger.error(f"[extract_audio] File has no audio track or zero duration: {video_path}")
raise RuntimeError(f"File has no audio track: {video_path}")
logger.info(f"[extract_audio] Duration: {audio.duration:.2f}s, fps: {audio.fps}")
temp_dir = tempfile.mkdtemp(prefix="videotranscriber_")
audio_path = Path(temp_dir) / f"{video_path.stem}_audio.wav"
try:
audio.write_audiofile(str(audio_path), logger=None)
except TypeError:
# moviepy 1.x uses verbose parameter; moviepy 2.x removed it
audio.write_audiofile(str(audio_path), verbose=False, logger=None)
audio.close()
2026-03-28 12:26:45 -06:00
if not audio_path.exists() or audio_path.stat().st_size == 0:
logger.error(f"[extract_audio] Output WAV is empty or missing: {audio_path}")
raise RuntimeError(f"Audio extraction produced empty file: {audio_path}")
logger.info(f"[extract_audio] Extracted to: {audio_path} ({audio_path.stat().st_size} bytes)")
_temp_audio_files.append(str(audio_path))
2025-01-28 17:00:03 -05:00
return audio_path
2026-03-28 12:26:45 -06:00
except RuntimeError:
raise
2025-01-28 17:00:03 -05:00
except Exception as e:
2026-03-28 12:26:45 -06:00
logger.error(f"[extract_audio] Failed for '{video_path}': {e}", exc_info=True)
2025-01-28 17:00:03 -05:00
raise RuntimeError(f"Audio extraction failed: {e}")
def cleanup_temp_audio():
"""Remove all temporary audio files created during processing."""
cleaned = 0
for fpath in _temp_audio_files:
try:
if os.path.exists(fpath):
os.remove(fpath)
parent = os.path.dirname(fpath)
if os.path.isdir(parent) and not os.listdir(parent):
os.rmdir(parent)
cleaned += 1
except Exception as e:
logger.warning(f"Could not remove temp file {fpath}: {e}")
_temp_audio_files.clear()
return cleaned
def get_video_duration(video_path: Path):
"""Get duration of a video/audio file in seconds."""
try:
clip = AudioFileClip(str(video_path))
duration = clip.duration
clip.close()
2026-03-28 12:26:45 -06:00
if duration is None or duration == 0:
logger.warning(f"[get_video_duration] Zero or null duration for: {video_path}")
return duration
2026-03-28 12:26:45 -06:00
except Exception as e:
logger.error(f"[get_video_duration] Failed for '{video_path}': {e}", exc_info=True)
return None