"""
Subtitle operations for FFmpeg backend.
This module provides subtitle functionality for the FFmpeg backend.
"""
import os
import tempfile
from pathlib import Path
from typing import Union, List, Tuple, Optional, Dict, Any
import logging
from PIL import Image, ImageDraw, ImageFont, ImageColor
from ...operations.subtitle import SubtitleEntry
logger = logging.getLogger(__name__)
[docs]
class FFmpegSubtitle:
"""FFmpeg subtitle operations."""
[docs]
def add_subtitles(self, video: Dict, entries: List[SubtitleEntry],
font: str = "Arial", size: int = 24,
color: Union[str, Tuple[int, int, int]] = "white",
position: Optional[Tuple[int, int]] = None) -> Dict:
"""
Add subtitles from a list of entries.
Args:
video: Video dictionary
entries: List of SubtitleEntry objects
font: Font name or path
size: Font size in pixels
color: Text color (name or RGB tuple)
position: Default position (None for bottom center)
Returns:
Updated video dictionary
"""
if not entries:
return video
self._ensure_temp_dir()
# Convert color to hex format
if isinstance(color, tuple):
hex_color = "#{:02x}{:02x}{:02x}".format(*color)
else:
rgb = ImageColor.getrgb(color)
hex_color = "#{:02x}{:02x}{:02x}".format(*rgb)
# Create a temporary SRT file
srt_file = Path(tempfile.mktemp(suffix=".srt", dir=self._temp_dir))
with open(srt_file, 'w', encoding='utf-8') as f:
for i, entry in enumerate(entries, 1):
# Format timestamps as SRT format: HH:MM:SS,mmm
start_time_str = self._format_srt_time(entry.start_time)
end_time_str = self._format_srt_time(entry.end_time)
# Write SRT entry
f.write(f"{i}\n")
f.write(f"{start_time_str} --> {end_time_str}\n")
f.write(f"{entry.text}\n\n")
# Create subtitle filter
# Escape path for Windows (replace colons)
escaped_path = srt_file.as_posix().replace('\\', '\\\\').replace(':', '\\:')
subtitle_filter = f"subtitles={escaped_path}:force_style="
# Add font style
style_params = [
f"FontName={font}",
f"FontSize={size}",
f"PrimaryColour={hex_color}",
f"Outline=1",
f"Shadow=0",
f"MarginV=10"
]
# Add position if specified
if position:
video_info = self.get_info(video)
video_width = video_info.get("width", 1280)
video_height = video_info.get("height", 720)
x, y = position
# Convert absolute position to percentage
x_percent = int((x / video_width) * 100)
y_percent = int((y / video_height) * 100)
style_params.append(f"MarginL={x_percent}")
style_params.append(f"MarginV={y_percent}")
subtitle_filter += ','.join(style_params)
# Apply the subtitle filter using apply_filter
return self.apply_filter(video, subtitle_filter)
[docs]
def add_subtitle_text(self, video: Dict, entry: SubtitleEntry,
font: str = "Arial", size: int = 24,
color: Union[str, Tuple[int, int, int]] = "white") -> Dict:
"""
Add a single subtitle entry.
Args:
video: Video dictionary
entry: SubtitleEntry object
font: Font name or path
size: Font size in pixels
color: Text color (name or RGB tuple)
Returns:
Updated video dictionary
"""
# Reuse the add_subtitles method with a single entry
return self.add_subtitles(
video,
[entry],
font=font,
size=size,
color=color,
position=entry.position
)
def _format_srt_time(self, seconds: float) -> str:
"""
Format time in seconds to SRT format (HH:MM:SS,mmm).
Args:
seconds: Time in seconds
Returns:
Formatted time string
"""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
seconds = seconds % 60
return f"{hours:02d}:{minutes:02d}:{int(seconds):02d},{int((seconds % 1) * 1000):03d}"