Source code for fmusvid.backends.ffmpeg.audio

"""
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 }