#!/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))