143 lines
4.2 KiB
Python
143 lines
4.2 KiB
Python
import subprocess as SP
|
|
import json
|
|
import shlex
|
|
import time
|
|
import os
|
|
import io
|
|
import sys
|
|
import uuid
|
|
from tqdm import tqdm
|
|
from utils import handle_config
|
|
|
|
profiles = handle_config().get("transcode_profiles", {})
|
|
|
|
profiles[None] = {
|
|
"command": "-vcodec copy -acodec copy -scodec copy -f null",
|
|
"doc": "null output for counting frames",
|
|
}
|
|
|
|
|
|
def ffprobe(file):
|
|
cmd = [
|
|
"ffprobe",
|
|
"-v",
|
|
"error",
|
|
"-print_format",
|
|
"json",
|
|
"-show_format",
|
|
"-show_streams",
|
|
file,
|
|
]
|
|
try:
|
|
out = SP.check_output(cmd)
|
|
except KeyboardInterrupt:
|
|
raise
|
|
except:
|
|
return file, None
|
|
return file, json.loads(out)
|
|
|
|
|
|
def make_ffmpeg_command_line(infile, outfile, profile=None, **kwargs):
|
|
default_opts = ["-v", "error", "-y", "-nostdin"]
|
|
ffmpeg = (
|
|
"C:\\Users\\Earthnuker\\scoop\\apps\\ffmpeg-nightly\\current\\bin\\ffmpeg.exe"
|
|
)
|
|
cmdline = profile["command"]
|
|
opts = profile.get("defaults", {}).copy()
|
|
opts.update(kwargs)
|
|
|
|
if isinstance(cmdline, str):
|
|
cmdline = shlex.split(cmdline)
|
|
cmdline = list(cmdline or [])
|
|
cmdline += ["-progress", "-", "-nostats"]
|
|
ret = [ffmpeg, *default_opts, "-i", infile, *cmdline, outfile]
|
|
ret = [v.format(**opts) for v in ret]
|
|
return ret
|
|
|
|
|
|
def count_frames(file, **kwargs):
|
|
total_frames = None
|
|
for state in run_transcode(file, os.devnull, None):
|
|
if state.get("progress") == "end":
|
|
total_frames = int(state.get("frame", -1))
|
|
if total_frames is None:
|
|
return total_frames
|
|
if total_frames <= 0:
|
|
total_frames = None
|
|
return total_frames
|
|
|
|
|
|
def run_transcode(file, outfile, profile, job_id=None, **kwargs):
|
|
job_id = job_id or str(uuid.uuid4())
|
|
stderr_fh = None
|
|
if outfile != os.devnull:
|
|
stderr_fh = open("{}.log".format(job_id), "w")
|
|
proc = SP.Popen(
|
|
make_ffmpeg_command_line(file, outfile, profiles[profile], **kwargs),
|
|
stdout=SP.PIPE,
|
|
stderr=stderr_fh,
|
|
encoding="utf8",
|
|
)
|
|
state = {}
|
|
poll = None
|
|
while poll is None:
|
|
poll = proc.poll()
|
|
state["ret"] = poll
|
|
if outfile != os.devnull:
|
|
with open("{}.log".format(job_id), "r") as tl:
|
|
state["stderr"] = tl.read()
|
|
line = proc.stdout.readline().strip()
|
|
if not line:
|
|
continue
|
|
try:
|
|
key, val = line.split("=", 1)
|
|
except ValueError:
|
|
print(line)
|
|
continue
|
|
key = key.strip()
|
|
val = val.strip()
|
|
state[key] = val
|
|
if key == "progress":
|
|
yield state
|
|
if stderr_fh:
|
|
stderr_fh.close()
|
|
os.unlink(stderr_fh.name)
|
|
yield state
|
|
|
|
|
|
def transcode(file, outfile, profile, job_id=None, **kwargs):
|
|
from pprint import pprint
|
|
|
|
info = ffprobe(file)
|
|
frames = count_frames(file)
|
|
progbar = tqdm(desc="Processing {}".format(outfile), total=frames, unit=" frames", disable=False,leave=False)
|
|
for state in run_transcode(file, outfile, profile, job_id, **kwargs):
|
|
if "frame" in state:
|
|
progbar.n = int(state["frame"])
|
|
progbar.update(0)
|
|
state["total_frames"] = frames
|
|
state["file"] = file
|
|
state["outfile"] = outfile
|
|
# progbar.write(state["stderr"])
|
|
yield state
|
|
progbar.close()
|
|
|
|
|
|
def preview_command(file, outfile, profile, **kwargs):
|
|
return make_ffmpeg_command_line(file, outfile, profiles[profile], **kwargs)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
file = sys.argv[1]
|
|
for profile in ["H.265 transcode", "H.264 transcode"]:
|
|
for preset in ["ultrafast", "fast", "medium", "slow", "veryslow"]:
|
|
for crf in list(range(10, 54, 4))[::-1]:
|
|
outfile = os.path.join("E:\\","transcode",profile,"{}_{}.mkv".format(crf, preset))
|
|
os.makedirs(os.path.dirname(outfile), exist_ok=True)
|
|
if os.path.isfile(outfile):
|
|
print("Skipping",outfile)
|
|
continue
|
|
for _ in transcode(
|
|
file, outfile, profile, "transcode", preset=preset, crf=crf
|
|
):
|
|
pass
|