Melies/encode_video.py
2025-04-21 11:39:04 +02:00

103 lines
3.1 KiB
Python
Executable File

#!/usr/bin/env python3
import argparse
import os
import subprocess
import re
import sys
SUPPORTED_EXTENSIONS = (".mp4", ".mkv", ".mov", ".avi")
def get_duration(file_path):
result = subprocess.run([
"ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", file_path
], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
return float(result.stdout.strip())
def encode(input_file, codec):
if codec == "x265":
ffmpeg_codec = "libx265"
crf = 26
folder = "h265"
elif codec == "av1":
ffmpeg_codec = "libaom-av1"
crf = 32
folder = "av1"
else:
raise ValueError("Unsupported codec")
try:
duration = get_duration(input_file)
except Exception:
print(f"\n❌ Could not read duration for {input_file}")
return
filename = os.path.basename(input_file)
name, _ = os.path.splitext(filename)
outdir = os.path.join(os.path.dirname(input_file), folder)
os.makedirs(outdir, exist_ok=True)
output_file = os.path.join(outdir, f"{name}.mkv")
cmd = [
"ffmpeg",
"-i", input_file,
"-map", "0",
"-c:v", ffmpeg_codec,
"-crf", str(crf),
"-c:a", "copy",
"-c:s", "copy",
"-y", output_file
]
print(f"\n🎬 Encoding {filename}{folder}/{name}.mkv")
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
time_re = re.compile(r"time=(\d+):(\d+):([\d.]+)")
last_percent = -1
for line in process.stderr:
match = time_re.search(line)
if match:
h, m, s = map(float, match.groups())
current = h * 3600 + m * 60 + s
percent = int((current / duration) * 100)
if percent != last_percent:
print(f"\r⏳ Progress: {percent}%", end='', flush=True)
last_percent = percent
process.wait()
print("\n✅ Done.")
def encode_batch(directory, codec):
if not os.path.isdir(directory):
print(f"❌ Not a valid directory: {directory}")
return
for root, _, files in os.walk(directory):
for file in files:
if file.lower().endswith(SUPPORTED_EXTENSIONS):
filepath = os.path.join(root, file)
encode(filepath, codec)
def main():
parser = argparse.ArgumentParser(description="Encode video(s) to x265 or AV1.")
parser.add_argument("input", nargs="?", help="Path to input file")
parser.add_argument("-d", "--directory", help="Path to a directory for batch encoding")
parser.add_argument("--codec", choices=["x265", "av1"], default="x265", help="Codec to use (default: x265)")
args = parser.parse_args()
if args.directory:
encode_batch(args.directory, args.codec)
elif args.input:
if not os.path.isfile(args.input):
print(f"❌ File not found: {args.input}")
sys.exit(1)
encode(args.input, args.codec)
else:
print("❌ Please provide a file path or use -d for a directory.")
parser.print_help()
if __name__ == "__main__":
main()