Spaces:
Configuration error
Configuration error
import os | |
import sys | |
import re | |
import argparse | |
from easy_functions import (format_time, | |
get_input_length, | |
get_video_details, | |
show_video, | |
g_colab) | |
import contextlib | |
import shutil | |
import subprocess | |
import time | |
from IPython.display import Audio, Image, clear_output, display | |
from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip | |
import configparser | |
parser = argparse.ArgumentParser(description='SyncKing-Kong main run file') | |
parser.add_argument('-video_file', type=str, | |
help='Input video file path', required=False, default=False) | |
parser.add_argument('-vocal_file', type=str, | |
help='Input audio file path', required=False, default=False) | |
parser.add_argument('-output_file', type=str, | |
help='Output video file path', required=False, default=False) | |
args = parser.parse_args() | |
# retrieve variables from config.ini | |
config = configparser.ConfigParser() | |
config.read('config.ini') | |
if args.video_file: | |
video_file = args.video_file | |
else: | |
video_file = config['OPTIONS']['video_file'] | |
if args.vocal_file: | |
vocal_file = args.vocal_file | |
else: | |
vocal_file = config['OPTIONS']['vocal_file'] | |
quality = config['OPTIONS']['quality'] | |
output_height = config['OPTIONS']['output_height'] | |
wav2lip_version = config['OPTIONS']['wav2lip_version'] | |
use_previous_tracking_data = config['OPTIONS']['use_previous_tracking_data'] | |
nosmooth = config.getboolean('OPTIONS', 'nosmooth') | |
U = config.getint('PADDING', 'U') | |
D = config.getint('PADDING', 'D') | |
L = config.getint('PADDING', 'L') | |
R = config.getint('PADDING', 'R') | |
size = config.getfloat('MASK', 'size') | |
feathering = config.getint('MASK', 'feathering') | |
mouth_tracking = config.getboolean('MASK', 'mouth_tracking') | |
debug_mask = config.getboolean('MASK', 'debug_mask') | |
batch_process = config.getboolean('OTHER', 'batch_process') | |
output_suffix = config['OTHER']['output_suffix'] | |
include_settings_in_suffix = config.getboolean('OTHER', 'include_settings_in_suffix') | |
if g_colab(): | |
preview_input = config.getboolean("OTHER", "preview_input") | |
else: | |
preview_input = False | |
preview_settings = config.getboolean("OTHER", "preview_settings") | |
frame_to_preview = config.getint("OTHER", "frame_to_preview") | |
working_directory = os.getcwd() | |
start_time = time.time() | |
video_file = video_file.strip('"') | |
vocal_file = vocal_file.strip('"') | |
# check video_file exists | |
if video_file == "": | |
sys.exit(f"video_file cannot be blank") | |
if os.path.isdir(video_file): | |
sys.exit(f"{video_file} is a directory, you need to point to a file") | |
if not os.path.exists(video_file): | |
sys.exit(f"Could not find file: {video_file}") | |
if wav2lip_version == "Wav2Lip_GAN": | |
checkpoint_path = os.path.join(working_directory, "checkpoints", "Wav2Lip_GAN.pth") | |
else: | |
checkpoint_path = os.path.join(working_directory, "checkpoints", "Wav2Lip.pth") | |
if feathering == 3: | |
feathering = 5 | |
if feathering == 2: | |
feathering = 3 | |
resolution_scale = 1 | |
res_custom = False | |
if output_height == "half resolution": | |
resolution_scale = 2 | |
elif output_height == "full resolution": | |
resolution_scale = 1 | |
else: | |
res_custom = True | |
resolution_scale = 3 | |
in_width, in_height, in_fps, in_length = get_video_details(video_file) | |
out_height = round(in_height / resolution_scale) | |
if res_custom: | |
out_height = int(output_height) | |
fps_for_static_image = 30 | |
if output_suffix == "" and not include_settings_in_suffix: | |
sys.exit( | |
"Current suffix settings will overwrite your input video! Please add a suffix or tick include_settings_in_suffix" | |
) | |
frame_to_preview = max(frame_to_preview - 1, 0) | |
if include_settings_in_suffix: | |
if wav2lip_version == "Wav2Lip_GAN": | |
output_suffix = f"{output_suffix}_GAN" | |
output_suffix = f"{output_suffix}_{quality}" | |
if output_height != "full resolution": | |
output_suffix = f"{output_suffix}_{out_height}" | |
if nosmooth: | |
output_suffix = f"{output_suffix}_nosmooth1" | |
else: | |
output_suffix = f"{output_suffix}_nosmooth0" | |
if U != 0 or D != 0 or L != 0 or R != 0: | |
output_suffix = f"{output_suffix}_pads-" | |
if U != 0: | |
output_suffix = f"{output_suffix}U{U}" | |
if D != 0: | |
output_suffix = f"{output_suffix}D{D}" | |
if L != 0: | |
output_suffix = f"{output_suffix}L{L}" | |
if R != 0: | |
output_suffix = f"{output_suffix}R{R}" | |
if quality != "fast": | |
output_suffix = f"{output_suffix}_mask-S{size}F{feathering}" | |
if mouth_tracking: | |
output_suffix = f"{output_suffix}_mt" | |
if debug_mask: | |
output_suffix = f"{output_suffix}_debug" | |
if preview_settings: | |
output_suffix = f"{output_suffix}_preview" | |
rescaleFactor = str(round(1 // resolution_scale)) | |
pad_up = str(round(U * resolution_scale)) | |
pad_down = str(round(D * resolution_scale)) | |
pad_left = str(round(L * resolution_scale)) | |
pad_right = str(round(R * resolution_scale)) | |
################################################################################ | |
######################### reconstruct input paths ############################## | |
# Extract each part of the path | |
folder, filename_with_extension = os.path.split(video_file) | |
filename, file_type = os.path.splitext(filename_with_extension) | |
# Extract filenumber if it exists | |
filenumber_match = re.search(r"\d+$", filename) | |
if filenumber_match: # if there is a filenumber - extract it | |
filenumber = str(filenumber_match.group()) | |
filenamenonumber = re.sub(r"\d+$", "", filename) | |
else: # if there is no filenumber - make it blank | |
filenumber = "" | |
filenamenonumber = filename | |
# if vocal_file is blank - use the video as audio | |
if vocal_file == "": | |
vocal_file = video_file | |
# if not, check that the vocal_file file exists | |
else: | |
if not os.path.exists(vocal_file): | |
sys.exit(f"Could not find file: {vocal_file}") | |
if os.path.isdir(vocal_file): | |
sys.exit(f"{vocal_file} is a directory, you need to point to a file") | |
# Extract each part of the path | |
audio_folder, audio_filename_with_extension = os.path.split(vocal_file) | |
audio_filename, audio_file_type = os.path.splitext(audio_filename_with_extension) | |
# Extract filenumber if it exists | |
audio_filenumber_match = re.search(r"\d+$", audio_filename) | |
if audio_filenumber_match: # if there is a filenumber - extract it | |
audio_filenumber = str(audio_filenumber_match.group()) | |
audio_filenamenonumber = re.sub(r"\d+$", "", audio_filename) | |
else: # if there is no filenumber - make it blank | |
audio_filenumber = "" | |
audio_filenamenonumber = audio_filename | |
################################################################################ | |
# set process_failed to False so that it may be set to True if one or more processings fail | |
process_failed = False | |
temp_output = os.path.join(working_directory, "temp", "output.mp4") | |
temp_folder = os.path.join(working_directory, "temp") | |
last_input_video = None | |
last_input_audio = None | |
# --------------------------Batch processing loop-------------------------------! | |
while True: | |
# construct input_video | |
input_video = os.path.join(folder, filenamenonumber + str(filenumber) + file_type) | |
input_videofile = os.path.basename(input_video) | |
# construct input_audio | |
input_audio = os.path.join( | |
audio_folder, audio_filenamenonumber + str(audio_filenumber) + audio_file_type | |
) | |
input_audiofile = os.path.basename(input_audio) | |
# see if filenames are different: | |
if filenamenonumber + str(filenumber) != audio_filenamenonumber + str( | |
audio_filenumber | |
): | |
output_filename = ( | |
filenamenonumber | |
+ str(filenumber) | |
+ "_" | |
+ audio_filenamenonumber | |
+ str(audio_filenumber) | |
) | |
else: | |
output_filename = filenamenonumber + str(filenumber) | |
# construct output_video | |
output_video = os.path.join(folder, output_filename + output_suffix + ".mp4") | |
output_video = os.path.normpath(output_video) | |
output_videofile = os.path.basename(output_video) | |
# remove last outputs | |
if os.path.exists("temp"): | |
shutil.rmtree("temp") | |
os.makedirs("temp", exist_ok=True) | |
# preview inputs (if enabled) | |
if preview_input: | |
print("input video:") | |
show_video(input_video) | |
if vocal_file != "": | |
print("input audio:") | |
display(Audio(input_audio)) | |
else: | |
print("using", input_videofile, "for audio") | |
print("You may want to check now that they're the correct files!") | |
last_input_video = input_video | |
last_input_audio = input_audio | |
shutil.copy(input_video, temp_folder) | |
shutil.copy(input_audio, temp_folder) | |
# rename temp file to include padding or else changing padding does nothing | |
temp_input_video = os.path.join(temp_folder, input_videofile) | |
renamed_temp_input_video = os.path.join( | |
temp_folder, str(U) + str(D) + str(L) + str(R) + input_videofile | |
) | |
shutil.copy(temp_input_video, renamed_temp_input_video) | |
temp_input_video = renamed_temp_input_video | |
temp_input_videofile = os.path.basename(renamed_temp_input_video) | |
temp_input_audio = os.path.join(temp_folder, input_audiofile) | |
# trim video if it's longer than the audio | |
video_length = get_input_length(temp_input_video) | |
audio_length = get_input_length(temp_input_audio) | |
if preview_settings: | |
batch_process = False | |
preview_length_seconds = 1 | |
converted_preview_frame = frame_to_preview / in_fps | |
preview_start_time = min( | |
converted_preview_frame, video_length - preview_length_seconds | |
) | |
preview_video_path = os.path.join( | |
temp_folder, | |
"preview_" | |
+ str(preview_start_time) | |
+ "_" | |
+ str(U) | |
+ str(D) | |
+ str(L) | |
+ str(R) | |
+ input_videofile, | |
) | |
preview_audio_path = os.path.join(temp_folder, "preview_" + input_audiofile) | |
subprocess.call( | |
[ | |
"ffmpeg", | |
"-loglevel", | |
"error", | |
"-i", | |
temp_input_video, | |
"-ss", | |
str(preview_start_time), | |
"-to", | |
str(preview_start_time + preview_length_seconds), | |
"-c", | |
"copy", | |
preview_video_path, | |
] | |
) | |
subprocess.call( | |
[ | |
"ffmpeg", | |
"-loglevel", | |
"error", | |
"-i", | |
temp_input_audio, | |
"-ss", | |
str(preview_start_time), | |
"-to", | |
str(preview_start_time + 1), | |
"-c", | |
"copy", | |
preview_audio_path, | |
] | |
) | |
temp_input_video = preview_video_path | |
temp_input_audio = preview_audio_path | |
if video_length > audio_length: | |
trimmed_video_path = os.path.join( | |
temp_folder, "trimmed_" + temp_input_videofile | |
) | |
with open(os.devnull, "w") as devnull: | |
with contextlib.redirect_stdout(devnull), contextlib.redirect_stderr( | |
devnull | |
): | |
ffmpeg_extract_subclip( | |
temp_input_video, 0, audio_length, targetname=trimmed_video_path | |
) | |
temp_input_video = trimmed_video_path | |
# check if face detection has already happened on this clip | |
last_detected_face = os.path.join(working_directory, "last_detected_face.pkl") | |
if os.path.isfile("last_file.txt"): | |
with open("last_file.txt", "r") as file: | |
last_file = file.readline() | |
if last_file != temp_input_video or use_previous_tracking_data == "False": | |
if os.path.isfile(last_detected_face): | |
os.remove(last_detected_face) | |
# ----------------------------Process the inputs!-----------------------------! | |
print( | |
f"Processing{' preview of' if preview_settings else ''} " | |
f"{input_videofile} using {input_audiofile} for audio" | |
) | |
# execute Wav2Lip & upscaler | |
cmd = [ | |
sys.executable, | |
"inference.py", | |
"--face", | |
temp_input_video, | |
"--audio", | |
temp_input_audio, | |
"--outfile", | |
temp_output, | |
"--pads", | |
str(pad_up), | |
str(pad_down), | |
str(pad_left), | |
str(pad_right), | |
"--checkpoint_path", | |
checkpoint_path, | |
"--out_height", | |
str(out_height), | |
"--fullres", | |
str(resolution_scale), | |
"--quality", | |
quality, | |
"--mask_dilation", | |
str(size), | |
"--mask_feathering", | |
str(feathering), | |
"--nosmooth", | |
str(nosmooth), | |
"--debug_mask", | |
str(debug_mask), | |
"--preview_settings", | |
str(preview_settings), | |
"--mouth_tracking", | |
str(mouth_tracking), | |
] | |
# Run the command | |
subprocess.run(cmd) | |
if preview_settings: | |
if os.path.isfile(os.path.join(temp_folder, "preview.jpg")): | |
print(f"preview successful! Check out temp/preview.jpg") | |
with open("last_file.txt", "w") as f: | |
f.write(temp_input_video) | |
# end processing timer and format the time it took | |
end_time = time.time() | |
elapsed_time = end_time - start_time | |
formatted_setup_time = format_time(elapsed_time) | |
print(f"Execution time: {formatted_setup_time}") | |
break | |
else: | |
print(f"Processing failed! :( see line above π") | |
print("Consider searching the issues tab on the github:") | |
print("https://github.com/anothermartz/Easy-Wav2Lip/issues") | |
exit() | |
# rename temp file and move to correct directory | |
if os.path.isfile(temp_output): | |
if os.path.isfile(output_video): | |
os.remove(output_video) | |
shutil.copy(temp_output, output_video) | |
# show output video | |
with open("last_file.txt", "w") as f: | |
f.write(temp_input_video) | |
print(f"{output_filename} successfully lip synced! It will be found here:") | |
print(output_video) | |
# end processing timer and format the time it took | |
end_time = time.time() | |
elapsed_time = end_time - start_time | |
formatted_setup_time = format_time(elapsed_time) | |
print(f"Execution time: {formatted_setup_time}") | |
else: | |
print(f"Processing failed! :( see line above π") | |
print("Consider searching the issues tab on the github:") | |
print("https://github.com/anothermartz/Easy-Wav2Lip/issues") | |
process_failed = True | |
if batch_process == False: | |
if process_failed: | |
exit() | |
else: | |
break | |
elif filenumber == "" and audio_filenumber == "": | |
print("Files not set for batch processing") | |
break | |
# -----------------------------Batch Processing!------------------------------! | |
if filenumber != "": # if video has a filenumber | |
match = re.search(r"\d+", filenumber) | |
# add 1 to video filenumber | |
filenumber = ( | |
f"{filenumber[:match.start()]}{int(match.group())+1:0{len(match.group())}d}" | |
) | |
if audio_filenumber != "": # if audio has a filenumber | |
match = re.search(r"\d+", audio_filenumber) | |
# add 1 to audio filenumber | |
audio_filenumber = f"{audio_filenumber[:match.start()]}{int(match.group())+1:0{len(match.group())}d}" | |
# construct input_video | |
input_video = os.path.join(folder, filenamenonumber + str(filenumber) + file_type) | |
input_videofile = os.path.basename(input_video) | |
# construct input_audio | |
input_audio = os.path.join( | |
audio_folder, audio_filenamenonumber + str(audio_filenumber) + audio_file_type | |
) | |
input_audiofile = os.path.basename(input_audio) | |
# now check which input files exist and what to do for each scenario | |
# both +1 files exist - continue processing | |
if os.path.exists(input_video) and os.path.exists(input_audio): | |
continue | |
# video +1 only - continue with last audio file | |
if os.path.exists(input_video) and input_video != last_input_video: | |
if audio_filenumber != "": # if audio has a filenumber | |
match = re.search(r"\d+", audio_filenumber) | |
# take 1 from audio filenumber | |
audio_filenumber = f"{audio_filenumber[:match.start()]}{int(match.group())-1:0{len(match.group())}d}" | |
continue | |
# audio +1 only - continue with last video file | |
if os.path.exists(input_audio) and input_audio != last_input_audio: | |
if filenumber != "": # if video has a filenumber | |
match = re.search(r"\d+", filenumber) | |
# take 1 from video filenumber | |
filenumber = f"{filenumber[:match.start()]}{int(match.group())-1:0{len(match.group())}d}" | |
continue | |
# neither +1 files exist or current files already processed - finish processing | |
print("Finished all sequentially numbered files") | |
if process_failed: | |
sys.exit("Processing failed on at least one video") | |
else: | |
break | |