"""
FFmpeg audio operations.
This module provides implementation for audio-related operations.
"""
import os
from typing import Dict, Any, Union
from pathlib import Path
import logging
logger = logging.getLogger(__name__)
[docs]
class FFmpegAudio:
"""FFmpeg audio operations implementation."""
[docs]
def mute(self, video: Dict[str, Any]) -> Dict[str, Any]:
"""
Remove audio track from video.
Args:
video: Video dictionary
Returns:
New video dictionary
"""
self._ensure_temp_dir()
temp_file = os.path.join(self._temp_dir, f"mute_{os.path.basename(video['path'])}")
args = ["-i", video["path"]]
if video.get("start", 0) > 0:
args = ["-ss", str(video["start"])] + args
if video.get("end") is not None:
duration = video["end"] - video.get("start", 0)
args += ["-t", str(duration)]
args += [
"-c:v", "copy",
"-an",
"-y",
temp_file
]
self._run_ffmpeg(args)
info = self._run_ffprobe(["-i", temp_file])
return {
"path": temp_file,
"info": info,
"is_temp": True,
"start": 0,
"end": None
}
[docs]
def volume(self, video: Dict[str, Any], level: float) -> Dict[str, Any]:
"""
Set audio volume level.
Args:
video: Video dictionary
level: Volume level (1.0 = original, 0.5 = 50%, 2.0 = 200%)
Returns:
New video dictionary
"""
self._ensure_temp_dir()
temp_file = os.path.join(self._temp_dir, f"volume_{level}_{os.path.basename(video['path'])}")
# Use volume filter - volume in dB = 20 * log10(level)
volume_filter = f"volume={level}"
args = ["-i", video["path"]]
if video.get("start", 0) > 0:
args = ["-ss", str(video["start"])] + args
if video.get("end") is not None:
duration = video["end"] - video.get("start", 0)
args += ["-t", str(duration)]
args += [
"-af", volume_filter,
"-c:v", "copy",
"-y",
temp_file
]
self._run_ffmpeg(args)
info = self._run_ffprobe(["-i", temp_file])
return {
"path": temp_file,
"info": info,
"is_temp": True,
"start": 0,
"end": None
}
[docs]
def add_audio(self, video: Dict[str, Any], audio_path: Union[str, Path],
start: float = 0, volume: float = 1.0) -> Dict[str, Any]:
"""
Add audio track to video.
Args:
video: Video dictionary
audio_path: Path to audio file
start: Start time for the audio (seconds)
volume: Volume level (1.0 = original)
Returns:
New video dictionary
"""
self._ensure_temp_dir()
temp_file = os.path.join(self._temp_dir, f"add_audio_{os.path.basename(video['path'])}")
# Build filter complex for mixing audio
# If video has audio, mix with new audio; otherwise just add new audio
args = ["-i", video["path"], "-i", str(audio_path)]
if video.get("start", 0) > 0:
args = ["-ss", str(video["start"])] + args
if video.get("end") is not None:
duration = video["end"] - video.get("start", 0)
args += ["-t", str(duration)]
# Check if original video has audio
has_audio = video.get("info", {}).get("has_audio", False)
if has_audio:
# Mix both audio tracks
args += [
"-filter_complex", f"[1:a]volume={volume}[a1];[0:a][a1]amix=inputs=2:duration=first",
"-c:v", "copy",
"-y",
temp_file
]
else:
# Just add the new audio
if volume != 1.0:
args += [
"-af", f"volume={volume}",
]
args += [
"-map", "0:v",
"-map", "1:a",
"-c:v", "copy",
"-y",
temp_file
]
self._run_ffmpeg(args)
info = self._run_ffprobe(["-i", temp_file])
return {
"path": temp_file,
"info": info,
"is_temp": True,
"start": 0,
"end": None
}