Created
June 10, 2024 05:46
-
-
Save LuisCusihuaman/0084489b1633de74d33ef6e953c31887 to your computer and use it in GitHub Desktop.
Merge subtitles with video
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import sys | |
import logging | |
import subprocess | |
import codecs | |
# Configure logging | |
logging.basicConfig(level=logging.INFO, format='%(message)s') | |
logger = logging.getLogger() | |
def check_ffmpeg(): | |
ffmpeg = os.environ.get('FFMPEG_DIRECTORY') | |
if not ffmpeg: | |
logger.error('❌ Point FFMPEG_DIRECTORY environment variable to the location of FFMPEG') | |
return None | |
if os.path.isfile(ffmpeg) and os.access(ffmpeg, os.X_OK): | |
binaries = os.path.dirname(ffmpeg) | |
else: | |
binaries = ffmpeg | |
ffmpeg_path = os.path.join(binaries, 'ffmpeg') | |
if not os.path.exists(ffmpeg_path): | |
logger.error('❌ Please download the ffmpeg binaries from www.ffmpeg.org') | |
return None | |
logger.info('✅ FFMPEG binaries found') | |
return binaries | |
def get_target_directory(): | |
if len(sys.argv) > 1: | |
return sys.argv[1] | |
return os.getcwd() | |
def is_video_file(filename): | |
video_extensions = ['.avi', '.mp4', '.mkv'] | |
return any(filename.endswith(ext) for ext in video_extensions) | |
def sanitize_filename(path): | |
return path.replace('"', '').replace('\'', '').replace(',', '') | |
def clean_subtitle_file(subtitle_file): | |
try: | |
with codecs.open(subtitle_file, 'r', encoding='utf-8', errors='ignore') as file: | |
content = file.read() | |
with codecs.open(subtitle_file, 'w', encoding='utf-8') as file: | |
file.write(content) | |
logger.info(f'✅ Cleaned and re-encoded subtitle file: {subtitle_file}') | |
except Exception as e: | |
logger.error(f'❌ Failed to clean subtitle file: {subtitle_file}. Error: {e}') | |
def merge_subtitles(binaries): | |
for root, _, files in os.walk(get_target_directory()): | |
for name in files: | |
if is_video_file(name): | |
original = name | |
os.chdir(root) | |
name_without_ext = os.path.splitext(name)[0] | |
subtitles = f'{name_without_ext}.srt' | |
sanitized_original = sanitize_filename(original) | |
os.rename(original, sanitized_original) | |
original = sanitized_original | |
output_file = sanitize_filename(f'{name_without_ext}.srt.mp4') | |
if os.path.exists(subtitles): | |
sanitized_subtitles = sanitize_filename(subtitles) | |
os.rename(subtitles, sanitized_subtitles) | |
clean_subtitle_file(sanitized_subtitles) | |
subtitles_option = f'subtitles={sanitized_subtitles}:charenc=UTF-8' | |
logger.info(f'🎬 Merging subtitles for {original}') | |
full_command = [ | |
os.path.join(binaries, "ffmpeg"), "-i", original, | |
"-vf", subtitles_option, "-c:a", "aac", "-b:a", "128k", | |
"-ac", "2", "-c:v", "libx264", output_file | |
] | |
else: | |
subtitles_option = '' | |
logger.info(f'🎬 No subtitles found for {original}') | |
full_command = [ | |
os.path.join(binaries, "ffmpeg"), "-i", original, | |
"-c:a", "aac", "-b:a", "128k", "-ac", "2", "-c:v", "libx264", output_file | |
] | |
logger.info(f'Executing command: {" ".join(full_command)}') | |
subprocess.run(full_command) | |
logger.info(f'✅ Created {output_file}') | |
def merge_video_audio_subtitles(binaries, video, audio, subs, output): | |
clean_subtitle_file(subs) | |
full_command = [ | |
os.path.join(binaries, "ffmpeg"), "-i", video, "-i", audio, "-i", subs, | |
"-c:v", "copy", "-c:a", "copy", "-c:s", "srt", | |
"-metadata:s:s:0", "language=nld", output | |
] | |
logger.info(f'Executing command: {" ".join(full_command)}') | |
subprocess.run(full_command) | |
logger.info(f'✅ Created {output}') | |
def apply_crossfade_transition(binaries, video1, video2, output, duration=0.5, offset=0): | |
full_command = [ | |
os.path.join(binaries, "ffmpeg"), "-i", video1, "-i", video2, | |
"-filter_complex", f"[0][1]xfade=transition=fade:duration={duration}:offset={offset}," | |
f"[0:a][1:a]acrossfade=d={duration}:c1=tri:c2=tri", | |
"-movflags", "+faststart", output | |
] | |
logger.info(f'Executing command: {" ".join(full_command)}') | |
subprocess.run(full_command) | |
logger.info(f'✅ Created {output}') | |
if __name__ == "__main__": | |
binaries = check_ffmpeg() | |
if binaries: | |
if len(sys.argv) == 5: | |
video = sys.argv[1] | |
audio = sys.argv[2] | |
subs = sys.argv[3] | |
output = sys.argv[4] | |
merge_video_audio_subtitles(binaries, video, audio, subs, output) | |
elif len(sys.argv) == 4: | |
video1 = sys.argv[1] | |
video2 = sys.argv[2] | |
output = sys.argv[3] | |
apply_crossfade_transition(binaries, video1, video2, output) | |
else: | |
merge_subtitles(binaries) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment