|
import os |
|
import subprocess as sp |
|
import warnings |
|
from typing import List |
|
|
|
__all__ = ['encode_video_comparison'] |
|
|
|
|
|
def encode_video_comparison( |
|
audiofile: str, |
|
subtitle_files: List[str], |
|
output_videopath: str = None, |
|
*, |
|
labels: List[str] = None, |
|
height: int = 90, |
|
width: int = 720, |
|
color: str = 'black', |
|
fontsize: int = 70, |
|
border_color: str = 'white', |
|
label_color: str = 'white', |
|
label_size: int = 14, |
|
fps: int = 25, |
|
video_codec: str = None, |
|
audio_codec: str = None, |
|
overwrite=False, |
|
only_cmd: bool = False, |
|
verbose=True |
|
) -> (str, None): |
|
""" |
|
Encode multiple subtitle files into one video with the subtitles vertically stacked. |
|
|
|
Parameters |
|
---------- |
|
audiofile : str |
|
Path of audio file. |
|
subtitle_files : list of str |
|
List of paths for subtitle file. |
|
output_videopath : str, optional |
|
Output video path. |
|
labels : list of str, default, None, meaning use ``subtitle_files`` as labels |
|
List of labels for ``subtitle_files``. |
|
height : int, default 90 |
|
Height for each subtitle section. |
|
width : int, default 720 |
|
Width for each subtitle section. |
|
color : str, default 'black' |
|
Background color of the video. |
|
fontsize: int, default 70 |
|
Font size for subtitles. |
|
border_color : str, default 'white' |
|
Border color for separating the sections of subtitle. |
|
label_color : str, default 'white' |
|
Color of labels. |
|
label_size : int, default 14 |
|
Font size of labels. |
|
fps : int, default 25 |
|
Frame-rate of the video. |
|
video_codec : str, optional |
|
Video codec opf the video. |
|
audio_codec : str, optional |
|
Audio codec opf the video. |
|
overwrite : bool, default False |
|
Whether to overwrite existing video files with the same path as the output video. |
|
only_cmd : bool, default False |
|
Whether to skip encoding and only return the full command generate from the specified options. |
|
verbose : bool, default True |
|
Whether to display ffmpeg processing info. |
|
|
|
Returns |
|
------- |
|
str or None |
|
Encoding command as a string if ``only_cmd = True``. |
|
""" |
|
vc = '' if video_codec is None else f' -c:v {video_codec}' |
|
ac = '' if audio_codec is None else f' -c:a {audio_codec}' |
|
background = f'-f lavfi -i color=size={width}x{height}:rate={fps}:color={color}' |
|
border = f'-f lavfi -i color=size={width}x3:rate={fps}:color={border_color}' |
|
audio = f'-i "{audiofile}"' |
|
cfilters0 = [] |
|
assert labels is None or len(labels) == len(subtitle_files) |
|
for i, sub in enumerate(subtitle_files): |
|
label = sub if labels is None else labels[i] |
|
label = label.replace("'", '"') |
|
fil = f"[0]drawtext=text='{label}':fontcolor={label_color}:fontsize={label_size}:x=10:y=10[a{i}]," \ |
|
f"[a{i}]subtitles='{sub}':force_style='Fontsize={fontsize}'[b{i}]" |
|
cfilters0.append(fil) |
|
cfilters1 = ( |
|
'[1]'.join( |
|
f'[b{i}]' for i in range(len(cfilters0)) |
|
) |
|
+ |
|
f'vstack=inputs={len(cfilters0) * 2 - 1}' |
|
) |
|
final_fil = ','.join(cfilters0) + f';{cfilters1}' |
|
ow = '-y' if overwrite else '-n' |
|
if output_videopath is None: |
|
name = os.path.split(os.path.splitext(audiofile)[0])[1] |
|
output_videopath = f'{name}_sub_comparison.mp4' |
|
cmd = (f'ffmpeg {ow} {background} {border} {audio} ' |
|
f'-filter_complex "{final_fil}"{vc}{ac} -shortest "{output_videopath}"') |
|
if only_cmd: |
|
return cmd |
|
if verbose: |
|
print(cmd) |
|
rc = sp.run(cmd, capture_output=not verbose).returncode |
|
if rc == 0: |
|
if verbose: |
|
print(f'Encoded: {output_videopath}') |
|
else: |
|
warnings.warn(f'Failed to encode {output_videopath}') |
|
|
|
|