feat: completed with all features

This commit is contained in:
Tutorial Builder
2026-06-16 11:54:06 +03:30
parent aec8fe6a76
commit 2bd38682e4
8 changed files with 198 additions and 179 deletions
@@ -0,0 +1,59 @@
import argparse
import subprocess
from pathlib import Path
def resolve_path(value: str) -> Path:
return Path(value).expanduser().resolve()
def run(command: list[str]) -> None:
print("Running:", " ".join(command))
subprocess.run(command, check=True)
def main() -> None:
parser = argparse.ArgumentParser(
description="Replace a video's original audio with a new voiceover audio file."
)
parser.add_argument("--video", required=True, help="Input video file, for example input/tutorial.mp4")
parser.add_argument("--audio", required=True, help="Input voiceover audio file, for example output/voiceover.wav")
parser.add_argument("--output", required=True, help="Output MP4 file, for example output/final_video.mp4")
parser.add_argument(
"--reencode-video",
action="store_true",
help="Re-encode video with H.264 instead of copying. Use only if the copied output has playback issues.",
)
args = parser.parse_args()
video_file = resolve_path(args.video)
audio_file = resolve_path(args.audio)
output_file = resolve_path(args.output)
if not video_file.exists():
raise FileNotFoundError(f"Video not found: {video_file}")
if not audio_file.exists():
raise FileNotFoundError(f"Audio not found: {audio_file}")
output_file.parent.mkdir(parents=True, exist_ok=True)
video_codec_args = ["-c:v", "libx264", "-preset", "medium", "-crf", "18"] if args.reencode_video else ["-c:v", "copy"]
command = [
"ffmpeg", "-y",
"-i", str(video_file),
"-i", str(audio_file),
"-map", "0:v:0", # video from first input
"-map", "1:a:0", # audio from second input only, so original video audio is muted/removed
*video_codec_args,
"-c:a", "aac",
"-b:a", "192k",
"-movflags", "+faststart",
str(output_file),
]
run(command)
print(f"Done: {output_file}")
if __name__ == "__main__":
main()