feat: switch to GPU-accelerated AV1 encoding with logs
This commit is contained in:
parent
1e7d3d521e
commit
f10d1208f8
@ -19,10 +19,12 @@ def encode(input_file, codec):
|
|||||||
ffmpeg_codec = "libx265"
|
ffmpeg_codec = "libx265"
|
||||||
crf = 26
|
crf = 26
|
||||||
folder = "h265"
|
folder = "h265"
|
||||||
|
extra_params = ["-crf", str(crf)]
|
||||||
elif codec == "av1":
|
elif codec == "av1":
|
||||||
ffmpeg_codec = "libaom-av1"
|
ffmpeg_codec = "av1_nvenc"
|
||||||
crf = 32
|
cq = 32
|
||||||
folder = "av1"
|
folder = "av1"
|
||||||
|
extra_params = ["-pix_fmt", "yuv420p", "-preset", "p4", "-cq", str(cq), "-rc", "vbr", "-b:v", "0"]
|
||||||
else:
|
else:
|
||||||
raise ValueError("Unsupported codec")
|
raise ValueError("Unsupported codec")
|
||||||
|
|
||||||
@ -37,36 +39,49 @@ def encode(input_file, codec):
|
|||||||
outdir = os.path.join(os.path.dirname(input_file), folder)
|
outdir = os.path.join(os.path.dirname(input_file), folder)
|
||||||
os.makedirs(outdir, exist_ok=True)
|
os.makedirs(outdir, exist_ok=True)
|
||||||
output_file = os.path.join(outdir, f"{name}.mkv")
|
output_file = os.path.join(outdir, f"{name}.mkv")
|
||||||
|
log_file = os.path.join(outdir, f"{name}.log")
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
"ffmpeg",
|
"ffmpeg",
|
||||||
"-i", input_file,
|
"-i", input_file,
|
||||||
"-map", "0",
|
"-map", "0",
|
||||||
|
] + extra_params + [
|
||||||
"-c:v", ffmpeg_codec,
|
"-c:v", ffmpeg_codec,
|
||||||
"-crf", str(crf),
|
|
||||||
"-c:a", "copy",
|
"-c:a", "copy",
|
||||||
"-c:s", "copy",
|
"-c:s", "copy",
|
||||||
"-y", output_file
|
"-y", output_file
|
||||||
]
|
]
|
||||||
|
|
||||||
print(f"\n🎬 Encoding {filename} → {folder}/{name}.mkv")
|
print(f"\n🎬 Encoding {filename} → {folder}/{name}.mkv")
|
||||||
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
|
with open(log_file, "w") as log:
|
||||||
|
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
time_re = re.compile(r"time=(\d+):(\d+):([\d.]+)")
|
time_re = re.compile(r"time=(\d+):(\d+):([\d.]+)")
|
||||||
last_percent = -1
|
last_percent = -1
|
||||||
|
|
||||||
for line in process.stderr:
|
while True:
|
||||||
match = time_re.search(line)
|
line = process.stderr.readline()
|
||||||
if match:
|
if not line:
|
||||||
h, m, s = map(float, match.groups())
|
break
|
||||||
current = h * 3600 + m * 60 + s
|
log.write(line)
|
||||||
percent = int((current / duration) * 100)
|
match = time_re.search(line)
|
||||||
if percent != last_percent:
|
if match:
|
||||||
print(f"\r⏳ Progress: {percent}%", end='', flush=True)
|
h, m, s = map(float, match.groups())
|
||||||
last_percent = percent
|
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()
|
process.wait()
|
||||||
print("\n✅ Done.")
|
|
||||||
|
if process.returncode != 0:
|
||||||
|
print(f"\n❌ [Error] FFmpeg failed for {filename}. Check the log: {log_file}")
|
||||||
|
# Clean broken output
|
||||||
|
if os.path.exists(output_file):
|
||||||
|
os.remove(output_file)
|
||||||
|
else:
|
||||||
|
print("\n✅ Done.")
|
||||||
|
|
||||||
def encode_batch(directory, codec):
|
def encode_batch(directory, codec):
|
||||||
if not os.path.isdir(directory):
|
if not os.path.isdir(directory):
|
||||||
@ -77,10 +92,14 @@ def encode_batch(directory, codec):
|
|||||||
for file in files:
|
for file in files:
|
||||||
if file.lower().endswith(SUPPORTED_EXTENSIONS):
|
if file.lower().endswith(SUPPORTED_EXTENSIONS):
|
||||||
filepath = os.path.join(root, file)
|
filepath = os.path.join(root, file)
|
||||||
encode(filepath, codec)
|
try:
|
||||||
|
encode(filepath, codec)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ Error with file {file}: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Encode video(s) to x265 or AV1.")
|
parser = argparse.ArgumentParser(description="Encode video(s) to x265 or AV1 with GPU.")
|
||||||
parser.add_argument("input", nargs="?", help="Path to input file")
|
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("-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)")
|
parser.add_argument("--codec", choices=["x265", "av1"], default="x265", help="Codec to use (default: x265)")
|
||||||
@ -99,4 +118,3 @@ def main():
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user