REFACTOR (CORE): splits build_ffmpeg_command method into one orchestrator and helper methods

This commit is contained in:
2026-08-30 14:47:13 +05:00
parent 8e0d345ba7
commit e935241fd8
+116 -85
View File
@@ -269,6 +269,109 @@ def extract_cover_image(input_file: str, output_path: str) -> bool:
return True return True
def _build_inputs(cmd: List[str], input_file: str, cover_image_path: Optional[str]) -> None:
"""Add input files to the command."""
if cover_image_path:
cmd.extend(['-i', cover_image_path])
cmd.extend(['-i', input_file])
def _build_time_options(cmd: List[str], start_seconds: int, duration_seconds: int) -> None:
"""Add time-based options to the command."""
cmd.extend(['-ss', format_time(start_seconds), '-t', format_time(duration_seconds)])
def _build_metadata(cmd: List[str], metadata: Optional[Dict]) -> None:
"""Add metadata options to the command."""
cmd.append('-map_metadata')
cmd.append('-1')
if metadata:
for key, value in metadata.items():
if value is not None and value != '':
cmd.extend(['-metadata', f"{key}={value}"])
def _build_cover_image_mapping(
cmd: List[str],
audio_codec: Optional[str],
video_codec: Optional[str],
video_quality: Optional[int],
) -> None:
"""Build stream mapping for cover image extraction (audio + cover video)."""
cmd.extend(['-map', '1:a:0', '-map', '0:v:0'])
if video_codec and video_codec != 'copy':
cmd.extend(['-c:v', video_codec])
else:
cmd.extend(['-c:v', 'png'])
if audio_codec and audio_codec != 'copy':
cmd.extend(['-c:a', audio_codec])
else:
cmd.extend(['-c:a', 'copy'])
cmd.append('-sn')
if video_quality is not None:
cmd.extend(['-q:v', str(video_quality)])
def _build_standard_mapping(
cmd: List[str],
stream_info: Dict,
audio_codec: Optional[str],
video_codec: Optional[str],
subtitle_codec: Optional[str],
video_quality: Optional[int],
drop_video: bool,
drop_subs: bool,
) -> None:
"""Build stream mapping for standard extraction (no cover image)."""
if drop_video and drop_subs:
cmd.extend(['-map', '0:a:0'])
elif drop_video:
cmd.extend(['-map', '0:a:0', '-map', '0:s?'])
elif drop_subs:
cmd.extend(['-map', '0:a:0', '-map', '0:v:0'])
else:
cmd.extend(['-map', '0'])
if audio_codec and audio_codec != 'copy':
cmd.extend(['-c:a', audio_codec])
else:
cmd.extend(['-c:a', 'copy'])
if not drop_video and stream_info.get('has_video'):
if video_codec and video_codec != 'copy':
cmd.extend(['-c:v', video_codec])
if video_quality is not None:
cmd.extend(['-q:v', str(video_quality)])
else:
cmd.extend(['-c:v', 'copy'])
else:
cmd.append('-vn')
if not drop_subs and stream_info.get('has_subtitle'):
if subtitle_codec and subtitle_codec != 'copy':
cmd.extend(['-c:s', subtitle_codec])
else:
cmd.extend(['-c:s', 'copy'])
else:
cmd.append('-sn')
def _build_format(cmd: List[str], format_opt: Optional[str]) -> None:
"""Add output format option if specified."""
if format_opt:
ffmpeg_format = FORMAT_INFO.get(format_opt, {}).get('ffmpeg', format_opt)
cmd.extend(['-f', ffmpeg_format])
def _build_output(cmd: List[str], output_path: str) -> None:
"""Add output file to the command."""
cmd.extend(['-y', output_path])
def build_ffmpeg_command( def build_ffmpeg_command(
input_file: str, input_file: str,
start_seconds: int, start_seconds: int,
@@ -309,93 +412,21 @@ def build_ffmpeg_command(
""" """
cmd = ['ffmpeg'] cmd = ['ffmpeg']
# Add cover image as first input if provided _build_inputs(cmd, input_file, cover_image_path)
_build_time_options(cmd, start_seconds, duration_seconds)
_build_metadata(cmd, metadata)
if cover_image_path: if cover_image_path:
cmd.extend(['-i', cover_image_path]) _build_cover_image_mapping(cmd, audio_codec, video_codec, video_quality)
# Add main input file
cmd.extend(['-i', input_file])
# Time options
cmd.extend(['-ss', format_time(start_seconds), '-t', format_time(duration_seconds)])
# Clear all original metadata.
cmd.append('-map_metadata')
cmd.append('-1')
# Apply custom metadata.
if metadata:
for key, value in metadata.items():
if value is not None and value != '':
cmd.extend(['-metadata', f"{key}={value}"])
# ---------- Stream mapping and codecs ----------
if cover_image_path:
# We have two inputs: index 0 = cover image, index 1 = main input
# Map audio from main input (index 1) and video from cover image (index 0)
cmd.extend(['-map', '1:a:0', '-map', '0:v:0'])
# Video codec: use user-specified codec if provided, otherwise fallback to png
if video_codec and video_codec != 'copy':
cmd.extend(['-c:v', video_codec])
else: else:
cmd.extend(['-c:v', 'png']) _build_standard_mapping(
cmd, stream_info, audio_codec, video_codec, subtitle_codec,
video_quality, drop_video, drop_subs
)
# Audio codec _build_format(cmd, format_opt)
if audio_codec and audio_codec != 'copy': _build_output(cmd, output_path)
cmd.extend(['-c:a', audio_codec])
else:
cmd.extend(['-c:a', 'copy'])
# Subtitle: we don't copy subtitles when using cover image (they would be from main input)
cmd.append('-sn')
# Video quality if specified
if video_quality is not None:
cmd.extend(['-q:v', str(video_quality)])
else:
# Standard mapping (no cover image)
if drop_video and drop_subs:
cmd.extend(['-map', '0:a:0'])
elif drop_video:
cmd.extend(['-map', '0:a:0', '-map', '0:s?'])
elif drop_subs:
cmd.extend(['-map', '0:a:0', '-map', '0:v:0'])
else:
cmd.extend(['-map', '0'])
# Audio codec
if audio_codec and audio_codec != 'copy':
cmd.extend(['-c:a', audio_codec])
else:
cmd.extend(['-c:a', 'copy'])
# Video codec
if not drop_video and stream_info.get('has_video'):
if video_codec and video_codec != 'copy':
cmd.extend(['-c:v', video_codec])
# Add video quality if specified (only when re-encoding)
if video_quality is not None:
cmd.extend(['-q:v', str(video_quality)])
else:
cmd.extend(['-c:v', 'copy'])
else:
cmd.append('-vn')
# Subtitle codec
if not drop_subs and stream_info.get('has_subtitle'):
if subtitle_codec and subtitle_codec != 'copy':
cmd.extend(['-c:s', subtitle_codec])
else:
cmd.extend(['-c:s', 'copy'])
else:
cmd.append('-sn')
# Output format
if format_opt:
ffmpeg_format = FORMAT_INFO.get(format_opt, {}).get('ffmpeg', format_opt)
cmd.extend(['-f', ffmpeg_format])
cmd.extend(['-y', output_path])
return cmd return cmd