#!/usr/bin/env python3 import subprocess, sys, multiprocessing, json, os, shlex, re from datetime import timedelta error_messages = { "param_len": """Not enough parameters Format: %s [input_file] (optional)[parameters] [movie_title] """ } output_display = "-hide_banner -loglevel error" default_params = " ".join([ "-c:v libvpx -vf %sscale=w=1280:h=720:force_original_aspect_ratio=decrease -b:v %s -crf 10", '-c:a libopus -ac 2',#-af "pan=stereo|FL=0.5*FC+0.707*FL+0.707*BL+0.5*LFE|FR=0.5*FC+0.707*FR+0.707*BR+0.5*LFE"', "-map_metadata -1 -metadata title=\"%s\" -threads %d -y -progress -" ]) def parseParams(params): parsed = shlex.split(params) vf_param = "" bitrate = "1M" try: param_contents = parsed.index("-vf")+1 vf_param = parsed[param_contents] + "," except ValueError: pass try: param_contents = parsed.index("-b:v")+1 bitrate = parsed[param_contents] except ValueError: pass return vf_param, bitrate def videoFilesize(filename): if not os.path.isfile(filename): return 0 return os.path.getsize(filename)/1024 #to kb def videoLength(filename): if not os.path.isfile(filename): return 0.0 result = subprocess.run( shlex.split('ffprobe %s -show_entries format=duration -print_format json "%s"' % ( output_display, filename )), stdout = subprocess.PIPE ) return int(json.loads(result.stdout.decode("utf-8"))["format"]["duration"].replace(".", "")) def encode(file_in, movie_title, params = ""): vf_filters, v_bitrate = parseParams(params) p = subprocess.Popen( shlex.split('ffmpeg %s -i "%s" %s %s "%s.webm"' % ( output_display, file_in, params, #get overwritten by defaults default_params % (vf_filters, v_bitrate, movie_title, multiprocessing.cpu_count()), movie_title.lower().replace(" ", "_") )), stdout = subprocess.PIPE ) ffmpeg_output = {} video_length = videoLength(file_in) video_filesize = videoFilesize(file_in) if video_length <= 0 or video_filesize <= 0: print("Input file not found!") return print("Encoding...\nPress [q] to exit") for line in iter(p.stdout.readline, b""): l = line.decode("utf-8").rstrip().split("=") if l[0] != "progress": if l[0] == "out_time_ms" or l[0] == "total_size": #if not structured like this, the next if will fail ffmpeg_output[l[0]] = int(l[1]) elif l[0] == "speed" and l[1] != "N/A": ffmpeg_output[l[0]] = float(l[1][:-1]) elif ffmpeg_output["out_time_ms"] > 0: percent = ffmpeg_output["out_time_ms"] / float(video_length) filesize = (ffmpeg_output["total_size"] / percent) / 1024 #to kb print("\rEst. Remaining Time: %s | Est. Filesize: %s | %6.2f%% " % ( #format example: " 2.44%" ':'.join(str(timedelta(microseconds=( #only want the (days), hours, mins int((video_length - ffmpeg_output["out_time_ms"]) / ffmpeg_output["speed"]) ))).split(":")[:2]), ("%7dK" if filesize < video_filesize else "!!%7dK!!") % (filesize), #alert if larger than original video percent * 100 ), end = "") p.wait() #ensure stdout isn't stolen print() #just have a newline if __name__ == "__main__": if len(sys.argv) < 3: sys.stderr.write( error_messages["param_len"] % (sys.argv[0]) ) sys.exit(1) sys.argv.pop(0) i = sys.argv.pop(0) o = sys.argv.pop(-1) encode(i, o, " ".join(sys.argv))