local mp = require("mp")
local assdraw = require("mp.assdraw")
local msg = require("mp.msg")
local utils = require("mp.utils")
local mpopts = require("mp.options")
local options = {
       -- Defaults to shift+w
       keybind = "W",
       -- If empty, saves on the same directory of the playing video.
       -- A starting "~" will be replaced by the home dir.
       -- This field is delimited by double-square-brackets - [[ and ]] - instead of
       -- quotes, because Windows users might run into a issue when using
       -- backslashes as a path separator. Examples of valid inputs for this field
       -- would be: [[]] (the default, empty value), [[C:\Users\John]] (on Windows),
       -- and [[/home/john]] (on Unix-like systems eg. Linux).
       -- The [[]] delimiter is not needed when using from a configuration file
       -- in the script-opts folder.
       output_directory = [[]],
       run_detached = false,
       -- Template string for the output file
       -- %f - Filename, with extension
       -- %F - Filename, without extension
       -- %T - Media title, if it exists, or filename, with extension (useful for some streams, such as YouTube).
       -- %s, %e - Start and end time, with milliseconds
       -- %S, %E - Start and end time, without milliseconds
       -- %M - "-audio", if audio is enabled, empty otherwise
       -- %R - "-(height)p", where height is the video's height, or scale_height, if it's enabled.
       -- More specifiers are supported, see https://mpv.io/manual/master/#options-screenshot-template
       -- Property expansion is supported (with %{} at top level, ${} when nested), see https://mpv.io/manual/master/#property-expansion
       output_template = "%F-[%s-%e]%M",
       -- Scale video to a certain height, keeping the aspect ratio. -1 disables it.
       scale_height = -1,
       -- Change the FPS of the output video, dropping or duplicating frames as needed.
       -- -1 means the FPS will be unchanged from the source.
       fps = -1,
       -- Target filesize, in kB. This will be used to calculate the bitrate
       -- used on the encode. If this is set to <= 0, the video bitrate will be set
       -- to 0, which might enable constant quality modes, depending on the
       -- video codec that's used (VP8 and VP9, for example).
       target_filesize = 2500,
       -- If true, will use stricter flags to ensure the resulting file doesn't
       -- overshoot the target filesize. Not recommended, as constrained quality
       -- mode should work well, unless you're really having trouble hitting
       -- the target size.
       strict_filesize_constraint = false,
       strict_bitrate_multiplier = 0.95,
       -- In kilobits.
       strict_audio_bitrate = 64,
       -- Sets the output format, from a few predefined ones.
       -- Currently we have:
       -- webm-vp8 (libvpx/libvorbis)
       -- webm-vp9 (libvpx-vp9/libopus)
       -- mp4 (h264/AAC)
       -- mp4-nvenc (h264-NVENC/AAC)
       -- raw (rawvideo/pcm_s16le).
       -- mp3 (libmp3lame)
       -- and gif
       output_format = "webm-vp8",
       twopass = true,
       -- If set, applies the video filters currently used on the playback to the encode.
       apply_current_filters = true,
       -- If set, writes the video's filename to the "Title" field on the metadata.
       write_filename_on_metadata = false,
       -- Set the number of encoding threads, for codecs libvpx and libvpx-vp9
       libvpx_threads = 4,
       additional_flags = "",
       -- Constant Rate Factor (CRF). The value meaning and limits may change,
       -- from codec to codec. Set to -1 to disable.
       crf = 10,
       -- Useful for flags that may impact output filesize, such as qmin, qmax etc
       -- Won't be applied when strict_filesize_constraint is on.
       non_strict_additional_flags = "",
       -- Display the encode progress, in %. Requires run_detached to be disabled.
       -- On Windows, it shows a cmd popup. "auto" will display progress on non-Windows platforms.
       display_progress = "auto",
       -- The font size used in the menu. Isn't used for the notifications (started encode, finished encode etc)
       font_size = 28,
       margin = 10,
       message_duration = 5,
       -- gif dither mode, 0-5 for bayer w/ bayer_scale 0-5, 6 for paletteuse default (sierra2_4a)
       gif_dither = 2,
       -- Force square pixels on output video
       -- Some players like recent Firefox versions display videos with non-square pixels with wrong aspect ratio
       force_square_pixels = false,
}

mpopts.read_options(options)
local base64_chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

-- encoding
function base64_encode(data)
   return ((data:gsub('.', function(x)
       local r,b='',x:byte()
       for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
       return r;
   end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
       if (#x < 6) then return '' end
       local c=0
       for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
       return base64_chars:sub(c+1,c+1)
   end)..({ '', '==', '=' })[#data%3+1])
end

-- decoding
function base64_decode(data)
   data = string.gsub(data, '[^'..base64_chars..'=]', '')
   return (data:gsub('.', function(x)
       if (x == '=') then return '' end
       local r,f='',(base64_chars:find(x)-1)
       for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
       return r;
   end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
       if (#x ~= 8) then return '' end
       local c=0
       for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
       return string.char(c)
   end))
end
local emit_event
emit_event = function(event_name, ...)
 return mp.commandv("script-message", "webm-" .. tostring(event_name), ...)
end
local test_set_options
test_set_options = function(new_options_json)
 local new_options = utils.parse_json(new_options_json)
 for k, v in pairs(new_options) do
   options[k] = v
 end
end
mp.register_script_message("mpv-webm-set-options", test_set_options)
local bold
bold = function(text)
 return "{\\b1}" .. tostring(text) .. "{\\b0}"
end
local message
message = function(text, duration)
 local ass = mp.get_property_osd("osd-ass-cc/0")
 ass = ass .. text
 return mp.osd_message(ass, duration or options.message_duration)
end
local append
append = function(a, b)
 for _, val in ipairs(b) do
   a[#a + 1] = val
 end
 return a
end
local seconds_to_time_string
seconds_to_time_string = function(seconds, no_ms, full)
 if seconds < 0 then
   return "unknown"
 end
 local ret = ""
 if not (no_ms) then
   ret = string.format(".%03d", seconds * 1000 % 1000)
 end
 ret = string.format("%02d:%02d%s", math.floor(seconds / 60) % 60, math.floor(seconds) % 60, ret)
 if full or seconds > 3600 then
   ret = string.format("%d:%s", math.floor(seconds / 3600), ret)
 end
 return ret
end
local seconds_to_path_element
seconds_to_path_element = function(seconds, no_ms, full)
 local time_string = seconds_to_time_string(seconds, no_ms, full)
 local _
 time_string, _ = time_string:gsub(":", ".")
 return time_string
end
local file_exists
file_exists = function(name)
 local info, err = utils.file_info(name)
 if info ~= nil then
   return true
 end
 return false
end
local expand_properties
expand_properties = function(text, magic)
 if magic == nil then
   magic = "$"
 end
 for prefix, raw, prop, colon, fallback, closing in text:gmatch("%" .. magic .. "{([?!]?)(=?)([^}:]*)(:?)([^}]*)(}*)}") do
   local err
   local prop_value
   local compare_value
   local original_prop = prop
   local get_property = mp.get_property_osd
   if raw == "=" then
     get_property = mp.get_property
   end
   if prefix ~= "" then
     for actual_prop, compare in prop:gmatch("(.-)==(.*)") do
       prop = actual_prop
       compare_value = compare
     end
   end
   if colon == ":" then
     prop_value, err = get_property(prop, fallback)
   else
     prop_value, err = get_property(prop, "(error)")
   end
   prop_value = tostring(prop_value)
   if prefix == "?" then
     if compare_value == nil then
       prop_value = err == nil and fallback .. closing or ""
     else
       prop_value = prop_value == compare_value and fallback .. closing or ""
     end
     prefix = "%" .. prefix
   elseif prefix == "!" then
     if compare_value == nil then
       prop_value = err ~= nil and fallback .. closing or ""
     else
       prop_value = prop_value ~= compare_value and fallback .. closing or ""
     end
   else
     prop_value = prop_value .. closing
   end
   if colon == ":" then
     local _
     text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. ":" .. fallback:gsub("%W", "%%%1") .. closing .. "}", expand_properties(prop_value))
   else
     local _
     text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. closing .. "}", prop_value)
   end
 end
 return text
end
local format_filename
format_filename = function(startTime, endTime, videoFormat)
 local hasAudioCodec = videoFormat.audioCodec ~= ""
 local replaceFirst = {
   ["%%mp"] = "%%mH.%%mM.%%mS",
   ["%%mP"] = "%%mH.%%mM.%%mS.%%mT",
   ["%%p"] = "%%wH.%%wM.%%wS",
   ["%%P"] = "%%wH.%%wM.%%wS.%%wT"
 }
 local replaceTable = {
   ["%%wH"] = string.format("%02d", math.floor(startTime / (60 * 60))),
   ["%%wh"] = string.format("%d", math.floor(startTime / (60 * 60))),
   ["%%wM"] = string.format("%02d", math.floor(startTime / 60 % 60)),
   ["%%wm"] = string.format("%d", math.floor(startTime / 60)),
   ["%%wS"] = string.format("%02d", math.floor(startTime % 60)),
   ["%%ws"] = string.format("%d", math.floor(startTime)),
   ["%%wf"] = string.format("%s", startTime),
   ["%%wT"] = string.sub(string.format("%.3f", startTime % 1), 3),
   ["%%mH"] = string.format("%02d", math.floor(endTime / (60 * 60))),
   ["%%mh"] = string.format("%d", math.floor(endTime / (60 * 60))),
   ["%%mM"] = string.format("%02d", math.floor(endTime / 60 % 60)),
   ["%%mm"] = string.format("%d", math.floor(endTime / 60)),
   ["%%mS"] = string.format("%02d", math.floor(endTime % 60)),
   ["%%ms"] = string.format("%d", math.floor(endTime)),
   ["%%mf"] = string.format("%s", endTime),
   ["%%mT"] = string.sub(string.format("%.3f", endTime % 1), 3),
   ["%%f"] = mp.get_property("filename"),
   ["%%F"] = mp.get_property("filename/no-ext"),
   ["%%s"] = seconds_to_path_element(startTime),
   ["%%S"] = seconds_to_path_element(startTime, true),
   ["%%e"] = seconds_to_path_element(endTime),
   ["%%E"] = seconds_to_path_element(endTime, true),
   ["%%T"] = mp.get_property("media-title"),
   ["%%M"] = (mp.get_property_native('aid') and not mp.get_property_native('mute') and hasAudioCodec) and '-audio' or '',
   ["%%R"] = (options.scale_height ~= -1) and "-" .. tostring(options.scale_height) .. "p" or "-" .. tostring(mp.get_property_native('height')) .. "p",
   ["%%t%%"] = "%%"
 }
 local filename = options.output_template
 for format, value in pairs(replaceFirst) do
   local _
   filename, _ = filename:gsub(format, value)
 end
 for format, value in pairs(replaceTable) do
   local _
   filename, _ = filename:gsub(format, value)
 end
 if mp.get_property_bool("demuxer-via-network", false) then
   local _
   filename, _ = filename:gsub("%%X{([^}]*)}", "%1")
   filename, _ = filename:gsub("%%x", "")
 else
   local x = string.gsub(mp.get_property("stream-open-filename", ""), string.gsub(mp.get_property("filename", ""), "%W", "%%%1") .. "$", "")
   local _
   filename, _ = filename:gsub("%%X{[^}]*}", x)
   filename, _ = filename:gsub("%%x", x)
 end
 filename = expand_properties(filename, "%")
 for format in filename:gmatch("%%t([aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ])") do
   local _
   filename, _ = filename:gsub("%%t" .. format, os.date("%" .. format))
 end
 local _
 filename, _ = filename:gsub("[<>:\"/\\|?*]", "")
 return tostring(filename) .. "." .. tostring(videoFormat.outputExtension)
end
local parse_directory
parse_directory = function(dir)
 local home_dir = os.getenv("HOME")
 if not home_dir then
   home_dir = os.getenv("USERPROFILE")
 end
 if not home_dir then
   local drive = os.getenv("HOMEDRIVE")
   local path = os.getenv("HOMEPATH")
   if drive and path then
     home_dir = utils.join_path(drive, path)
   else
     msg.warn("Couldn't find home dir.")
     home_dir = ""
   end
 end
 local _
 dir, _ = dir:gsub("^~", home_dir)
 return dir
end
local is_windows = type(package) == "table" and type(package.config) == "string" and package.config:sub(1, 1) == "\\"
local trim
trim = function(s)
 return s:match("^%s*(.-)%s*$")
end
local get_null_path
get_null_path = function()
 if file_exists("/dev/null") then
   return "/dev/null"
 end
 return "NUL"
end
local run_subprocess
run_subprocess = function(params)
 local res = utils.subprocess(params)
 msg.verbose("Command stdout: ")
 msg.verbose(res.stdout)
 if res.status ~= 0 then
   msg.verbose("Command failed! Reason: ", res.error, " Killed by us? ", res.killed_by_us and "yes" or "no")
   return false
 end
 return true
end
local shell_escape
shell_escape = function(args)
 local ret = { }
 for i, a in ipairs(args) do
   local s = tostring(a)
   if string.match(s, "[^A-Za-z0-9_/:=-]") then
     if is_windows then
       s = '"' .. string.gsub(s, '"', '"\\""') .. '"'
     else
       s = "'" .. string.gsub(s, "'", "'\\''") .. "'"
     end
   end
   table.insert(ret, s)
 end
 local concat = table.concat(ret, " ")
 if is_windows then
   concat = '"' .. concat .. '"'
 end
 return concat
end
local run_subprocess_popen
run_subprocess_popen = function(command_line)
 local command_line_string = shell_escape(command_line)
 command_line_string = command_line_string .. " 2>&1"
 msg.verbose("run_subprocess_popen: running " .. tostring(command_line_string))
 return io.popen(command_line_string)
end
local calculate_scale_factor
calculate_scale_factor = function()
 local baseResY = 720
 local osd_w, osd_h = mp.get_osd_size()
 return osd_h / baseResY
end
local should_display_progress
should_display_progress = function()
 if options.display_progress == "auto" then
   return not is_windows
 end
 return options.display_progress
end
local reverse
reverse = function(list)
 local _accum_0 = { }
 local _len_0 = 1
 local _max_0 = 1
 for _index_0 = #list, _max_0 < 0 and #list + _max_0 or _max_0, -1 do
   local element = list[_index_0]
   _accum_0[_len_0] = element
   _len_0 = _len_0 + 1
 end
 return _accum_0
end
local get_pass_logfile_path
get_pass_logfile_path = function(encode_out_path)
 return tostring(encode_out_path) .. "-video-pass1.log"
end
local dimensions_changed = true
local _video_dimensions = { }
local get_video_dimensions
get_video_dimensions = function()
 if not (dimensions_changed) then
   return _video_dimensions
 end
 local video_params = mp.get_property_native("video-out-params")
 if not video_params then
   return nil
 end
 dimensions_changed = false
 local keep_aspect = mp.get_property_bool("keepaspect")
 local w = video_params["w"]
 local h = video_params["h"]
 local dw = video_params["dw"]
 local dh = video_params["dh"]
 if mp.get_property_number("video-rotate") % 180 == 90 then
   w, h = h, w
   dw, dh = dh, dw
 end
 _video_dimensions = {
   top_left = { },
   bottom_right = { },
   ratios = { }
 }
 local window_w, window_h = mp.get_osd_size()
 if keep_aspect then
   local unscaled = mp.get_property_native("video-unscaled")
   local panscan = mp.get_property_number("panscan")
   local fwidth = window_w
   local fheight = math.floor(window_w / dw * dh)
   if fheight > window_h or fheight < h then
     local tmpw = math.floor(window_h / dh * dw)
     if tmpw <= window_w then
       fheight = window_h
       fwidth = tmpw
     end
   end
   local vo_panscan_area = window_h - fheight
   local f_w = fwidth / fheight
   local f_h = 1
   if vo_panscan_area == 0 then
     vo_panscan_area = window_h - fwidth
     f_w = 1
     f_h = fheight / fwidth
   end
   if unscaled or unscaled == "downscale-big" then
     vo_panscan_area = 0
     if unscaled or (dw <= window_w and dh <= window_h) then
       fwidth = dw
       fheight = dh
     end
   end
   local scaled_width = fwidth + math.floor(vo_panscan_area * panscan * f_w)
   local scaled_height = fheight + math.floor(vo_panscan_area * panscan * f_h)
   local split_scaling
   split_scaling = function(dst_size, scaled_src_size, zoom, align, pan)
     scaled_src_size = math.floor(scaled_src_size * 2 ^ zoom)
     align = (align + 1) / 2
     local dst_start = math.floor((dst_size - scaled_src_size) * align + pan * scaled_src_size)
     if dst_start < 0 then
       dst_start = dst_start + 1
     end
     local dst_end = dst_start + scaled_src_size
     if dst_start >= dst_end then
       dst_start = 0
       dst_end = 1
     end
     return dst_start, dst_end
   end
   local zoom = mp.get_property_number("video-zoom")
   local align_x = mp.get_property_number("video-align-x")
   local pan_x = mp.get_property_number("video-pan-x")
   _video_dimensions.top_left.x, _video_dimensions.bottom_right.x = split_scaling(window_w, scaled_width, zoom, align_x, pan_x)
   local align_y = mp.get_property_number("video-align-y")
   local pan_y = mp.get_property_number("video-pan-y")
   _video_dimensions.top_left.y, _video_dimensions.bottom_right.y = split_scaling(window_h, scaled_height, zoom, align_y, pan_y)
 else
   _video_dimensions.top_left.x = 0
   _video_dimensions.bottom_right.x = window_w
   _video_dimensions.top_left.y = 0
   _video_dimensions.bottom_right.y = window_h
 end
 _video_dimensions.ratios.w = w / (_video_dimensions.bottom_right.x - _video_dimensions.top_left.x)
 _video_dimensions.ratios.h = h / (_video_dimensions.bottom_right.y - _video_dimensions.top_left.y)
 return _video_dimensions
end
local set_dimensions_changed
set_dimensions_changed = function()
 dimensions_changed = true
end
local monitor_dimensions
monitor_dimensions = function()
 local properties = {
   "keepaspect",
   "video-out-params",
   "video-unscaled",
   "panscan",
   "video-zoom",
   "video-align-x",
   "video-pan-x",
   "video-align-y",
   "video-pan-y",
   "osd-width",
   "osd-height"
 }
 for _, p in ipairs(properties) do
   mp.observe_property(p, "native", set_dimensions_changed)
 end
end
local clamp
clamp = function(min, val, max)
 if val <= min then
   return min
 end
 if val >= max then
   return max
 end
 return val
end
local clamp_point
clamp_point = function(top_left, point, bottom_right)
 return {
   x = clamp(top_left.x, point.x, bottom_right.x),
   y = clamp(top_left.y, point.y, bottom_right.y)
 }
end
local VideoPoint
do
 local _class_0
 local _base_0 = {
   set_from_screen = function(self, sx, sy)
     local d = get_video_dimensions()
     local point = clamp_point(d.top_left, {
       x = sx,
       y = sy
     }, d.bottom_right)
     self.x = math.floor(d.ratios.w * (point.x - d.top_left.x) + 0.5)
     self.y = math.floor(d.ratios.h * (point.y - d.top_left.y) + 0.5)
   end,
   to_screen = function(self)
     local d = get_video_dimensions()
     return {
       x = math.floor(self.x / d.ratios.w + d.top_left.x + 0.5),
       y = math.floor(self.y / d.ratios.h + d.top_left.y + 0.5)
     }
   end
 }
 _base_0.__index = _base_0
 _class_0 = setmetatable({
   __init = function(self)
     self.x = -1
     self.y = -1
   end,
   __base = _base_0,
   __name = "VideoPoint"
 }, {
   __index = _base_0,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 VideoPoint = _class_0
end
local Region
do
 local _class_0
 local _base_0 = {
   is_valid = function(self)
     return self.x > -1 and self.y > -1 and self.w > -1 and self.h > -1
   end,
   set_from_points = function(self, p1, p2)
     self.x = math.min(p1.x, p2.x)
     self.y = math.min(p1.y, p2.y)
     self.w = math.abs(p1.x - p2.x)
     self.h = math.abs(p1.y - p2.y)
   end
 }
 _base_0.__index = _base_0
 _class_0 = setmetatable({
   __init = function(self)
     self.x = -1
     self.y = -1
     self.w = -1
     self.h = -1
   end,
   __base = _base_0,
   __name = "Region"
 }, {
   __index = _base_0,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 Region = _class_0
end
local make_fullscreen_region
make_fullscreen_region = function()
 local r = Region()
 local d = get_video_dimensions()
 local a = VideoPoint()
 local b = VideoPoint()
 local xa, ya
 do
   local _obj_0 = d.top_left
   xa, ya = _obj_0.x, _obj_0.y
 end
 a:set_from_screen(xa, ya)
 local xb, yb
 do
   local _obj_0 = d.bottom_right
   xb, yb = _obj_0.x, _obj_0.y
 end
 b:set_from_screen(xb, yb)
 r:set_from_points(a, b)
 return r
end
local read_double
read_double = function(bytes)
 local sign = 1
 local mantissa = bytes[2] % 2 ^ 4
 for i = 3, 8 do
   mantissa = mantissa * 256 + bytes[i]
 end
 if bytes[1] > 127 then
   sign = -1
 end
 local exponent = (bytes[1] % 128) * 2 ^ 4 + math.floor(bytes[2] / 2 ^ 4)
 if exponent == 0 then
   return 0
 end
 mantissa = (math.ldexp(mantissa, -52) + 1) * sign
 return math.ldexp(mantissa, exponent - 1023)
end
local write_double
write_double = function(num)
 local bytes = {
   0,
   0,
   0,
   0,
   0,
   0,
   0,
   0
 }
 if num == 0 then
   return bytes
 end
 local anum = math.abs(num)
 local mantissa, exponent = math.frexp(anum)
 exponent = exponent - 1
 mantissa = mantissa * 2 - 1
 local sign = num ~= anum and 128 or 0
 exponent = exponent + 1023
 bytes[1] = sign + math.floor(exponent / 2 ^ 4)
 mantissa = mantissa * 2 ^ 4
 local currentmantissa = math.floor(mantissa)
 mantissa = mantissa - currentmantissa
 bytes[2] = (exponent % 2 ^ 4) * 2 ^ 4 + currentmantissa
 for i = 3, 8 do
   mantissa = mantissa * 2 ^ 8
   currentmantissa = math.floor(mantissa)
   mantissa = mantissa - currentmantissa
   bytes[i] = currentmantissa
 end
 return bytes
end
local FirstpassStats
do
 local _class_0
 local duration_multiplier, fields_before_duration, fields_after_duration
 local _base_0 = {
   get_duration = function(self)
     local big_endian_binary_duration = reverse(self.binary_duration)
     return read_double(reversed_binary_duration) / duration_multiplier
   end,
   set_duration = function(self, duration)
     local big_endian_binary_duration = write_double(duration * duration_multiplier)
     self.binary_duration = reverse(big_endian_binary_duration)
   end,
   _bytes_to_string = function(self, bytes)
     return string.char(unpack(bytes))
   end,
   as_binary_string = function(self)
     local before_duration_string = self:_bytes_to_string(self.binary_data_before_duration)
     local duration_string = self:_bytes_to_string(self.binary_duration)
     local after_duration_string = self:_bytes_to_string(self.binary_data_after_duration)
     return before_duration_string .. duration_string .. after_duration_string
   end
 }
 _base_0.__index = _base_0
 _class_0 = setmetatable({
   __init = function(self, before_duration, duration, after_duration)
     self.binary_data_before_duration = before_duration
     self.binary_duration = duration
     self.binary_data_after_duration = after_duration
   end,
   __base = _base_0,
   __name = "FirstpassStats"
 }, {
   __index = _base_0,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 local self = _class_0
 duration_multiplier = 10000000.0
 fields_before_duration = 16
 fields_after_duration = 1
 self.data_before_duration_size = function(self)
   return fields_before_duration * 8
 end
 self.data_after_duration_size = function(self)
   return fields_after_duration * 8
 end
 self.size = function(self)
   return (fields_before_duration + 1 + fields_after_duration) * 8
 end
 self.from_bytes = function(self, bytes)
   local before_duration
   do
     local _accum_0 = { }
     local _len_0 = 1
     local _max_0 = self:data_before_duration_size()
     for _index_0 = 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do
       local b = bytes[_index_0]
       _accum_0[_len_0] = b
       _len_0 = _len_0 + 1
     end
     before_duration = _accum_0
   end
   local duration
   do
     local _accum_0 = { }
     local _len_0 = 1
     local _max_0 = self:data_before_duration_size() + 8
     for _index_0 = self:data_before_duration_size() + 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do
       local b = bytes[_index_0]
       _accum_0[_len_0] = b
       _len_0 = _len_0 + 1
     end
     duration = _accum_0
   end
   local after_duration
   do
     local _accum_0 = { }
     local _len_0 = 1
     for _index_0 = self:data_before_duration_size() + 8 + 1, #bytes do
       local b = bytes[_index_0]
       _accum_0[_len_0] = b
       _len_0 = _len_0 + 1
     end
     after_duration = _accum_0
   end
   return self(before_duration, duration, after_duration)
 end
 FirstpassStats = _class_0
end
local read_logfile_into_stats_array
read_logfile_into_stats_array = function(logfile_path)
 local file = assert(io.open(logfile_path, "rb"))
 local logfile_string = base64_decode(file:read())
 file:close()
 local stats_size = FirstpassStats:size()
 assert(logfile_string:len() % stats_size == 0)
 local stats = { }
 for offset = 1, #logfile_string, stats_size do
   local bytes = {
     logfile_string:byte(offset, offset + stats_size - 1)
   }
   assert(#bytes == stats_size)
   stats[#stats + 1] = FirstpassStats:from_bytes(bytes)
 end
 return stats
end
local write_stats_array_to_logfile
write_stats_array_to_logfile = function(stats_array, logfile_path)
 local file = assert(io.open(logfile_path, "wb"))
 local logfile_string = ""
 for _index_0 = 1, #stats_array do
   local stat = stats_array[_index_0]
   logfile_string = logfile_string .. stat:as_binary_string()
 end
 file:write(base64_encode(logfile_string))
 return file:close()
end
local vp8_patch_logfile
vp8_patch_logfile = function(logfile_path, encode_total_duration)
 local stats_array = read_logfile_into_stats_array(logfile_path)
 local average_duration = encode_total_duration / (#stats_array - 1)
 for i = 1, #stats_array - 1 do
   stats_array[i]:set_duration(average_duration)
 end
 stats_array[#stats_array]:set_duration(encode_total_duration)
 return write_stats_array_to_logfile(stats_array, logfile_path)
end
local formats = { }
local Format
do
 local _class_0
 local _base_0 = {
   getPreFilters = function(self)
     return { }
   end,
   getPostFilters = function(self)
     return { }
   end,
   getFlags = function(self)
     return { }
   end,
   getCodecFlags = function(self)
     local codecs = { }
     if self.videoCodec ~= "" then
       codecs[#codecs + 1] = "--ovc=" .. tostring(self.videoCodec)
     end
     if self.audioCodec ~= "" then
       codecs[#codecs + 1] = "--oac=" .. tostring(self.audioCodec)
     end
     return codecs
   end,
   postCommandModifier = function(self, command, region, startTime, endTime)
     return command
   end
 }
 _base_0.__index = _base_0
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "Basic"
     self.supportsTwopass = true
     self.videoCodec = ""
     self.audioCodec = ""
     self.outputExtension = ""
     self.acceptsBitrate = true
   end,
   __base = _base_0,
   __name = "Format"
 }, {
   __index = _base_0,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 Format = _class_0
end
local RawVideo
do
 local _class_0
 local _parent_0 = Format
 local _base_0 = {
   getColorspace = function(self)
     local csp = mp.get_property("colormatrix")
     local _exp_0 = csp
     if "bt.601" == _exp_0 then
       return "bt601"
     elseif "bt.709" == _exp_0 then
       return "bt709"
     elseif "bt.2020" == _exp_0 then
       return "bt2020"
     elseif "smpte-240m" == _exp_0 then
       return "smpte240m"
     else
       msg.info("Warning, unknown colorspace " .. tostring(csp) .. " detected, using bt.601.")
       return "bt601"
     end
   end,
   getPostFilters = function(self)
     return {
       "format=yuv444p16",
       "lavfi-scale=in_color_matrix=" .. self:getColorspace(),
       "format=bgr24"
     }
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "Raw"
     self.supportsTwopass = false
     self.videoCodec = "rawvideo"
     self.audioCodec = "pcm_s16le"
     self.outputExtension = "avi"
     self.acceptsBitrate = false
   end,
   __base = _base_0,
   __name = "RawVideo",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 RawVideo = _class_0
end
formats["raw"] = RawVideo()
local WebmVP8
do
 local _class_0
 local _parent_0 = Format
 local _base_0 = {
   getPreFilters = function(self)
     local colormatrixFilter = {
       ["bt.709"] = "bt709",
       ["bt.2020"] = "bt2020",
       ["smpte-240m"] = "smpte240m"
     }
     local ret = { }
     local colormatrix = mp.get_property_native("video-params/colormatrix")
     if colormatrixFilter[colormatrix] then
       append(ret, {
         "lavfi-colormatrix=" .. tostring(colormatrixFilter[colormatrix]) .. ":bt601"
       })
     end
     return ret
   end,
   getFlags = function(self)
     return {
       "--ovcopts-add=threads=" .. tostring(options.libvpx_threads),
       "--ovcopts-add=auto-alt-ref=1",
       "--ovcopts-add=lag-in-frames=25",
       "--ovcopts-add=quality=good",
       "--ovcopts-add=cpu-used=0"
     }
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "WebM"
     self.supportsTwopass = true
     self.videoCodec = "libvpx"
     self.audioCodec = "libvorbis"
     self.outputExtension = "webm"
     self.acceptsBitrate = true
   end,
   __base = _base_0,
   __name = "WebmVP8",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 WebmVP8 = _class_0
end
formats["webm-vp8"] = WebmVP8()
local WebmVP9
do
 local _class_0
 local _parent_0 = Format
 local _base_0 = {
   getFlags = function(self)
     return {
       "--ovcopts-add=threads=" .. tostring(options.libvpx_threads)
     }
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "WebM (VP9)"
     self.supportsTwopass = false
     self.videoCodec = "libvpx-vp9"
     self.audioCodec = "libopus"
     self.outputExtension = "webm"
     self.acceptsBitrate = true
   end,
   __base = _base_0,
   __name = "WebmVP9",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 WebmVP9 = _class_0
end
formats["webm-vp9"] = WebmVP9()
local MP4
do
 local _class_0
 local _parent_0 = Format
 local _base_0 = { }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "MP4 (h264/AAC)"
     self.supportsTwopass = true
     self.videoCodec = "libx264"
     self.audioCodec = "aac"
     self.outputExtension = "mp4"
     self.acceptsBitrate = true
   end,
   __base = _base_0,
   __name = "MP4",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 MP4 = _class_0
end
formats["mp4"] = MP4()
local MP4NVENC
do
 local _class_0
 local _parent_0 = Format
 local _base_0 = { }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "MP4 (h264-NVENC/AAC)"
     self.supportsTwopass = true
     self.videoCodec = "h264_nvenc"
     self.audioCodec = "aac"
     self.outputExtension = "mp4"
     self.acceptsBitrate = true
   end,
   __base = _base_0,
   __name = "MP4NVENC",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 MP4NVENC = _class_0
end
formats["mp4-nvenc"] = MP4NVENC()
local MP3
do
 local _class_0
 local _parent_0 = Format
 local _base_0 = { }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "MP3 (libmp3lame)"
     self.supportsTwopass = false
     self.videoCodec = ""
     self.audioCodec = "libmp3lame"
     self.outputExtension = "mp3"
     self.acceptsBitrate = true
   end,
   __base = _base_0,
   __name = "MP3",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 MP3 = _class_0
end
formats["mp3"] = MP3()
local GIF
do
 local _class_0
 local _parent_0 = Format
 local _base_0 = {
   postCommandModifier = function(self, command, region, startTime, endTime)
     local new_command = { }
     local start_ts = seconds_to_time_string(startTime, false, true)
     local end_ts = seconds_to_time_string(endTime, false, true)
     start_ts = start_ts:gsub(":", "\\\\:")
     end_ts = end_ts:gsub(":", "\\\\:")
     local cfilter = "[vid1]trim=start=" .. tostring(start_ts) .. ":end=" .. tostring(end_ts) .. "[vidtmp];"
     if mp.get_property("deinterlace") == "yes" then
       cfilter = cfilter .. "[vidtmp]yadif=mode=1[vidtmp];"
     end
     for _, v in ipairs(command) do
       local _continue_0 = false
       repeat
         if v:match("^%-%-vf%-add=lavfi%-scale") or v:match("^%-%-vf%-add=lavfi%-crop") or v:match("^%-%-vf%-add=fps") or v:match("^%-%-vf%-add=lavfi%-eq") then
           local n = v:gsub("^%-%-vf%-add=", ""):gsub("^lavfi%-", "")
           cfilter = cfilter .. "[vidtmp]" .. tostring(n) .. "[vidtmp];"
         else
           if v:match("^%-%-video%-rotate=90") then
             cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];"
           else
             if v:match("^%-%-video%-rotate=270") then
               cfilter = cfilter .. "[vidtmp]transpose=2[vidtmp];"
             else
               if v:match("^%-%-video%-rotate=180") then
                 cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];[vidtmp]transpose=1[vidtmp];"
               else
                 if v:match("^%-%-deinterlace=") then
                   _continue_0 = true
                   break
                 else
                   append(new_command, {
                     v
                   })
                   _continue_0 = true
                   break
                 end
               end
             end
           end
         end
         _continue_0 = true
       until true
       if not _continue_0 then
         break
       end
     end
     cfilter = cfilter .. "[vidtmp]split[topal][vidf];"
     cfilter = cfilter .. "[topal]palettegen[pal];"
     cfilter = cfilter .. "[vidf]fifo[vidf];"
     if options.gif_dither == 6 then
       cfilter = cfilter .. "[vidf][pal]paletteuse[vo]"
     else
       cfilter = cfilter .. "[vidf][pal]paletteuse=dither=bayer:bayer_scale=" .. tostring(options.gif_dither) .. ":diff_mode=rectangle[vo]"
     end
     append(new_command, {
       "--lavfi-complex=" .. tostring(cfilter)
     })
     return new_command
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.displayName = "GIF"
     self.supportsTwopass = false
     self.videoCodec = "gif"
     self.audioCodec = ""
     self.outputExtension = "gif"
     self.acceptsBitrate = false
   end,
   __base = _base_0,
   __name = "GIF",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 GIF = _class_0
end
formats["gif"] = GIF()
local Page
do
 local _class_0
 local _base_0 = {
   add_keybinds = function(self)
     if not self.keybinds then
       return
     end
     for key, func in pairs(self.keybinds) do
       mp.add_forced_key_binding(key, key, func, {
         repeatable = true
       })
     end
   end,
   remove_keybinds = function(self)
     if not self.keybinds then
       return
     end
     for key, _ in pairs(self.keybinds) do
       mp.remove_key_binding(key)
     end
   end,
   observe_properties = function(self)
     self.sizeCallback = function()
       return self:draw()
     end
     local properties = {
       "keepaspect",
       "video-out-params",
       "video-unscaled",
       "panscan",
       "video-zoom",
       "video-align-x",
       "video-pan-x",
       "video-align-y",
       "video-pan-y",
       "osd-width",
       "osd-height"
     }
     for _index_0 = 1, #properties do
       local p = properties[_index_0]
       mp.observe_property(p, "native", self.sizeCallback)
     end
   end,
   unobserve_properties = function(self)
     if self.sizeCallback then
       mp.unobserve_property(self.sizeCallback)
       self.sizeCallback = nil
     end
   end,
   clear = function(self)
     local window_w, window_h = mp.get_osd_size()
     mp.set_osd_ass(window_w, window_h, "")
     return mp.osd_message("", 0)
   end,
   prepare = function(self)
     return nil
   end,
   dispose = function(self)
     return nil
   end,
   show = function(self)
     if self.visible then
       return
     end
     self.visible = true
     self:observe_properties()
     self:add_keybinds()
     self:prepare()
     self:clear()
     return self:draw()
   end,
   hide = function(self)
     if not self.visible then
       return
     end
     self.visible = false
     self:unobserve_properties()
     self:remove_keybinds()
     self:clear()
     return self:dispose()
   end,
   setup_text = function(self, ass)
     local scale = calculate_scale_factor()
     local margin = options.margin * scale
     ass:append("{\\an7}")
     ass:pos(margin, margin)
     return ass:append("{\\fs" .. tostring(options.font_size * scale) .. "}")
   end
 }
 _base_0.__index = _base_0
 _class_0 = setmetatable({
   __init = function() end,
   __base = _base_0,
   __name = "Page"
 }, {
   __index = _base_0,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 Page = _class_0
end
local EncodeWithProgress
do
 local _class_0
 local _parent_0 = Page
 local _base_0 = {
   draw = function(self)
     local progress = 100 * ((self.currentTime - self.startTime) / self.duration)
     local progressText = string.format("%d%%", progress)
     local window_w, window_h = mp.get_osd_size()
     local ass = assdraw.ass_new()
     ass:new_event()
     self:setup_text(ass)
     ass:append("Encoding (" .. tostring(bold(progressText)) .. ")\\N")
     return mp.set_osd_ass(window_w, window_h, ass.text)
   end,
   parseLine = function(self, line)
     local matchTime = string.match(line, "Encode time[-]pos: ([0-9.]+)")
     local matchExit = string.match(line, "Exiting... [(]([%a ]+)[)]")
     if matchTime == nil and matchExit == nil then
       return
     end
     if matchTime ~= nil and tonumber(matchTime) > self.currentTime then
       self.currentTime = tonumber(matchTime)
     end
     if matchExit ~= nil then
       self.finished = true
       self.finishedReason = matchExit
     end
   end,
   startEncode = function(self, command_line)
     local copy_command_line
     do
       local _accum_0 = { }
       local _len_0 = 1
       for _index_0 = 1, #command_line do
         local arg = command_line[_index_0]
         _accum_0[_len_0] = arg
         _len_0 = _len_0 + 1
       end
       copy_command_line = _accum_0
     end
     append(copy_command_line, {
       '--term-status-msg=Encode time-pos: ${=time-pos}\\n'
     })
     self:show()
     local processFd = run_subprocess_popen(copy_command_line)
     for line in processFd:lines() do
       msg.verbose(string.format('%q', line))
       self:parseLine(line)
       self:draw()
     end
     processFd:close()
     self:hide()
     if self.finishedReason == "End of file" then
       return true
     end
     return false
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self, startTime, endTime)
     self.startTime = startTime
     self.endTime = endTime
     self.duration = endTime - startTime
     self.currentTime = startTime
   end,
   __base = _base_0,
   __name = "EncodeWithProgress",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 EncodeWithProgress = _class_0
end
local get_active_tracks
get_active_tracks = function()
 local accepted = {
   video = true,
   audio = not mp.get_property_bool("mute"),
   sub = mp.get_property_bool("sub-visibility")
 }
 local active = {
   video = { },
   audio = { },
   sub = { }
 }
 for _, track in ipairs(mp.get_property_native("track-list")) do
   if track["selected"] and accepted[track["type"]] then
     local count = #active[track["type"]]
     active[track["type"]][count + 1] = track
   end
 end
 return active
end
local filter_tracks_supported_by_format
filter_tracks_supported_by_format = function(active_tracks, format)
 local has_video_codec = format.videoCodec ~= ""
 local has_audio_codec = format.audioCodec ~= ""
 local supported = {
   video = has_video_codec and active_tracks["video"] or { },
   audio = has_audio_codec and active_tracks["audio"] or { },
   sub = has_video_codec and active_tracks["sub"] or { }
 }
 return supported
end
local append_track
append_track = function(out, track)
 local external_flag = {
   ["audio"] = "audio-file",
   ["sub"] = "sub-file"
 }
 local internal_flag = {
   ["video"] = "vid",
   ["audio"] = "aid",
   ["sub"] = "sid"
 }
 if track['external'] and string.len(track['external-filename']) <= 2048 then
   return append(out, {
     "--" .. tostring(external_flag[track['type']]) .. "=" .. tostring(track['external-filename'])
   })
 else
   return append(out, {
     "--" .. tostring(internal_flag[track['type']]) .. "=" .. tostring(track['id'])
   })
 end
end
local append_audio_tracks
append_audio_tracks = function(out, tracks)
 local internal_tracks = { }
 for _index_0 = 1, #tracks do
   local track = tracks[_index_0]
   if track['external'] then
     append_track(out, track)
   else
     append(internal_tracks, {
       track
     })
   end
 end
 if #internal_tracks > 1 then
   local filter_string = ""
   for _index_0 = 1, #internal_tracks do
     local track = internal_tracks[_index_0]
     filter_string = filter_string .. "[aid" .. tostring(track['id']) .. "]"
   end
   filter_string = filter_string .. "amix[ao]"
   return append(out, {
     "--lavfi-complex=" .. tostring(filter_string)
   })
 else
   if #internal_tracks == 1 then
     return append_track(out, internal_tracks[1])
   end
 end
end
local get_scale_filters
get_scale_filters = function()
 local filters = { }
 if options.force_square_pixels then
   append(filters, {
     "lavfi-scale=iw*sar:ih"
   })
 end
 if options.scale_height > 0 then
   append(filters, {
     "lavfi-scale=-2:" .. tostring(options.scale_height)
   })
 end
 return filters
end
local get_fps_filters
get_fps_filters = function()
 if options.fps > 0 then
   return {
     "fps=" .. tostring(options.fps)
   }
 end
 return { }
end
local get_contrast_brightness_and_saturation_filters
get_contrast_brightness_and_saturation_filters = function()
 local mpv_brightness = mp.get_property("brightness")
 local mpv_contrast = mp.get_property("contrast")
 local mpv_saturation = mp.get_property("saturation")
 if mpv_brightness == 0 and mpv_contrast == 0 and mpv_saturation == 0 then
   return { }
 end
 local eq_saturation = (mpv_saturation + 100) / 100.0
 local eq_contrast = (mpv_contrast + 100) / 100.0
 local eq_brightness = (mpv_brightness / 50.0 + eq_contrast - 1) / 2.0
 return {
   "lavfi-eq=contrast=" .. tostring(eq_contrast) .. ":saturation=" .. tostring(eq_saturation) .. ":brightness=" .. tostring(eq_brightness)
 }
end
local append_property
append_property = function(out, property_name, option_name)
 option_name = option_name or property_name
 local prop = mp.get_property(property_name)
 if prop and prop ~= "" then
   return append(out, {
     "--" .. tostring(option_name) .. "=" .. tostring(prop)
   })
 end
end
local append_list_options
append_list_options = function(out, property_name, option_prefix)
 option_prefix = option_prefix or property_name
 local prop = mp.get_property_native(property_name)
 if prop then
   for _index_0 = 1, #prop do
     local value = prop[_index_0]
     append(out, {
       "--" .. tostring(option_prefix) .. "-append=" .. tostring(value)
     })
   end
 end
end
local get_playback_options
get_playback_options = function()
 local ret = { }
 append_property(ret, "sub-ass-override")
 append_property(ret, "sub-ass-force-style")
 append_property(ret, "sub-ass-vsfilter-aspect-compat")
 append_property(ret, "sub-auto")
 append_property(ret, "sub-delay")
 append_property(ret, "video-rotate")
 append_property(ret, "ytdl-format")
 append_property(ret, "deinterlace")
 return ret
end
local get_speed_flags
get_speed_flags = function()
 local ret = { }
 local speed = mp.get_property_native("speed")
 if speed ~= 1 then
   append(ret, {
     "--vf-add=setpts=PTS/" .. tostring(speed),
     "--af-add=atempo=" .. tostring(speed),
     "--sub-speed=1/" .. tostring(speed)
   })
 end
 return ret
end
local get_metadata_flags
get_metadata_flags = function()
 local title = mp.get_property("filename/no-ext")
 return {
   "--oset-metadata=title=%" .. tostring(string.len(title)) .. "%" .. tostring(title)
 }
end
local apply_current_filters
apply_current_filters = function(filters)
 local vf = mp.get_property_native("vf")
 msg.verbose("apply_current_filters: got " .. tostring(#vf) .. " currently applied.")
 for _index_0 = 1, #vf do
   local _continue_0 = false
   repeat
     local filter = vf[_index_0]
     msg.verbose("apply_current_filters: filter name: " .. tostring(filter['name']))
     if filter["enabled"] == false then
       _continue_0 = true
       break
     end
     local str = filter["name"]
     local params = filter["params"] or { }
     for k, v in pairs(params) do
       str = str .. ":" .. tostring(k) .. "=%" .. tostring(string.len(v)) .. "%" .. tostring(v)
     end
     append(filters, {
       str
     })
     _continue_0 = true
   until true
   if not _continue_0 then
     break
   end
 end
end
local get_video_filters
get_video_filters = function(format, region)
 local filters = { }
 append(filters, format:getPreFilters())
 if options.apply_current_filters then
   apply_current_filters(filters)
 end
 if region and region:is_valid() then
   append(filters, {
     "lavfi-crop=" .. tostring(region.w) .. ":" .. tostring(region.h) .. ":" .. tostring(region.x) .. ":" .. tostring(region.y)
   })
 end
 append(filters, get_scale_filters())
 append(filters, get_fps_filters())
 append(filters, get_contrast_brightness_and_saturation_filters())
 append(filters, format:getPostFilters())
 return filters
end
local get_video_encode_flags
get_video_encode_flags = function(format, region)
 local flags = { }
 append(flags, get_playback_options())
 local filters = get_video_filters(format, region)
 for _index_0 = 1, #filters do
   local f = filters[_index_0]
   append(flags, {
     "--vf-add=" .. tostring(f)
   })
 end
 append(flags, get_speed_flags())
 return flags
end
local calculate_bitrate
calculate_bitrate = function(active_tracks, format, length)
 if format.videoCodec == "" then
   return nil, options.target_filesize * 8 / length
 end
 local video_kilobits = options.target_filesize * 8
 local audio_kilobits = nil
 local has_audio_track = #active_tracks["audio"] > 0
 if options.strict_filesize_constraint and has_audio_track then
   audio_kilobits = length * options.strict_audio_bitrate
   video_kilobits = video_kilobits - audio_kilobits
 end
 local video_bitrate = math.floor(video_kilobits / length)
 local audio_bitrate = audio_kilobits and math.floor(audio_kilobits / length) or nil
 return video_bitrate, audio_bitrate
end
local find_path
find_path = function(startTime, endTime)
 local path = mp.get_property('path')
 if not path then
   return nil, nil, nil, nil, nil
 end
 local is_stream = not file_exists(path)
 local is_temporary = false
 if is_stream then
   if mp.get_property('file-format') == 'hls' then
     path = utils.join_path(parse_directory('~'), 'cache_dump.ts')
     mp.command_native({
       'dump_cache',
       seconds_to_time_string(startTime, false, true),
       seconds_to_time_string(endTime + 5, false, true),
       path
     })
     endTime = endTime - startTime
     startTime = 0
     is_temporary = true
   end
 end
 return path, is_stream, is_temporary, startTime, endTime
end
local encode
encode = function(region, startTime, endTime)
 local format = formats[options.output_format]
 local originalStartTime = startTime
 local originalEndTime = endTime
 local path, is_stream, is_temporary
 path, is_stream, is_temporary, startTime, endTime = find_path(startTime, endTime)
 if not path then
   message("No file is being played")
   return
 end
 local command = {
   "mpv",
   path,
   "--start=" .. seconds_to_time_string(startTime, false, true),
   "--end=" .. seconds_to_time_string(endTime, false, true),
   "--loop-file=no",
   "--no-pause"
 }
 append(command, format:getCodecFlags())
 local active_tracks = get_active_tracks()
 local supported_active_tracks = filter_tracks_supported_by_format(active_tracks, format)
 for track_type, tracks in pairs(supported_active_tracks) do
   if track_type == "audio" then
     append_audio_tracks(command, tracks)
   else
     for _index_0 = 1, #tracks do
       local track = tracks[_index_0]
       append_track(command, track)
     end
   end
 end
 for track_type, tracks in pairs(supported_active_tracks) do
   local _continue_0 = false
   repeat
     if #tracks > 0 then
       _continue_0 = true
       break
     end
     local _exp_0 = track_type
     if "video" == _exp_0 then
       append(command, {
         "--vid=no"
       })
     elseif "audio" == _exp_0 then
       append(command, {
         "--aid=no"
       })
     elseif "sub" == _exp_0 then
       append(command, {
         "--sid=no"
       })
     end
     _continue_0 = true
   until true
   if not _continue_0 then
     break
   end
 end
 if format.videoCodec ~= "" then
   append(command, get_video_encode_flags(format, region))
 end
 append(command, format:getFlags())
 if options.write_filename_on_metadata then
   append(command, get_metadata_flags())
 end
 if format.acceptsBitrate then
   if options.target_filesize > 0 then
     local length = endTime - startTime
     local video_bitrate, audio_bitrate = calculate_bitrate(supported_active_tracks, format, length)
     if video_bitrate then
       append(command, {
         "--ovcopts-add=b=" .. tostring(video_bitrate) .. "k"
       })
     end
     if audio_bitrate then
       append(command, {
         "--oacopts-add=b=" .. tostring(audio_bitrate) .. "k"
       })
     end
     if options.strict_filesize_constraint then
       local type = format.videoCodec ~= "" and "ovc" or "oac"
       append(command, {
         "--" .. tostring(type) .. "opts-add=minrate=" .. tostring(bitrate) .. "k",
         "--" .. tostring(type) .. "opts-add=maxrate=" .. tostring(bitrate) .. "k"
       })
     end
   else
     local type = format.videoCodec ~= "" and "ovc" or "oac"
     append(command, {
       "--" .. tostring(type) .. "opts-add=b=0"
     })
   end
 end
 for token in string.gmatch(options.additional_flags, "[^%s]+") do
   command[#command + 1] = token
 end
 if not options.strict_filesize_constraint then
   for token in string.gmatch(options.non_strict_additional_flags, "[^%s]+") do
     command[#command + 1] = token
   end
   if options.crf >= 0 then
     append(command, {
       "--ovcopts-add=crf=" .. tostring(options.crf)
     })
   end
 end
 local dir = ""
 if is_stream then
   dir = parse_directory("~")
 else
   local _
   dir, _ = utils.split_path(path)
 end
 if options.output_directory ~= "" then
   dir = parse_directory(options.output_directory)
 end
 local formatted_filename = format_filename(originalStartTime, originalEndTime, format)
 local out_path = utils.join_path(dir, formatted_filename)
 append(command, {
   "--o=" .. tostring(out_path)
 })
 emit_event("encode-started")
 if options.twopass and format.supportsTwopass and not is_stream then
   local first_pass_cmdline
   do
     local _accum_0 = { }
     local _len_0 = 1
     for _index_0 = 1, #command do
       local arg = command[_index_0]
       _accum_0[_len_0] = arg
       _len_0 = _len_0 + 1
     end
     first_pass_cmdline = _accum_0
   end
   append(first_pass_cmdline, {
     "--ovcopts-add=flags=+pass1"
   })
   message("Starting first pass...")
   msg.verbose("First-pass command line: ", table.concat(first_pass_cmdline, " "))
   local res = run_subprocess({
     args = first_pass_cmdline,
     cancellable = false
   })
   if not res then
     message("First pass failed! Check the logs for details.")
     emit_event("encode-finished", "fail")
     return
   end
   append(command, {
     "--ovcopts-add=flags=+pass2"
   })
   if format.videoCodec == "libvpx" then
     msg.verbose("Patching libvpx pass log file...")
     vp8_patch_logfile(get_pass_logfile_path(out_path), endTime - startTime)
   end
 end
 command = format:postCommandModifier(command, region, startTime, endTime)
 msg.info("Encoding to", out_path)
 msg.verbose("Command line:", table.concat(command, " "))
 if options.run_detached then
   message("Started encode, process was detached.")
   return utils.subprocess_detached({
     args = command
   })
 else
   local res = false
   if not should_display_progress() then
     message("Started encode...")
     res = run_subprocess({
       args = command,
       cancellable = false
     })
   else
     local ewp = EncodeWithProgress(startTime, endTime)
     res = ewp:startEncode(command)
   end
   if res then
     message("Encoded successfully! Saved to\\N" .. tostring(bold(out_path)))
     emit_event("encode-finished", "success")
   else
     message("Encode failed! Check the logs for details.")
     emit_event("encode-finished", "fail")
   end
   os.remove(get_pass_logfile_path(out_path))
   if is_temporary then
     return os.remove(path)
   end
 end
end
local CropPage
do
 local _class_0
 local _parent_0 = Page
 local _base_0 = {
   reset = function(self)
     local dimensions = get_video_dimensions()
     local xa, ya
     do
       local _obj_0 = dimensions.top_left
       xa, ya = _obj_0.x, _obj_0.y
     end
     self.pointA:set_from_screen(xa, ya)
     local xb, yb
     do
       local _obj_0 = dimensions.bottom_right
       xb, yb = _obj_0.x, _obj_0.y
     end
     self.pointB:set_from_screen(xb, yb)
     if self.visible then
       return self:draw()
     end
   end,
   setPointA = function(self)
     local posX, posY = mp.get_mouse_pos()
     self.pointA:set_from_screen(posX, posY)
     if self.visible then
       return self:draw()
     end
   end,
   setPointB = function(self)
     local posX, posY = mp.get_mouse_pos()
     self.pointB:set_from_screen(posX, posY)
     if self.visible then
       return self:draw()
     end
   end,
   cancel = function(self)
     self:hide()
     return self.callback(false, nil)
   end,
   finish = function(self)
     local region = Region()
     region:set_from_points(self.pointA, self.pointB)
     self:hide()
     return self.callback(true, region)
   end,
   draw_box = function(self, ass)
     local region = Region()
     region:set_from_points(self.pointA:to_screen(), self.pointB:to_screen())
     local d = get_video_dimensions()
     ass:new_event()
     ass:append("{\\an7}")
     ass:pos(0, 0)
     ass:append('{\\bord0}')
     ass:append('{\\shad0}')
     ass:append('{\\c&H000000&}')
     ass:append('{\\alpha&H77}')
     ass:draw_start()
     ass:rect_cw(d.top_left.x, d.top_left.y, region.x, region.y + region.h)
     ass:rect_cw(region.x, d.top_left.y, d.bottom_right.x, region.y)
     ass:rect_cw(d.top_left.x, region.y + region.h, region.x + region.w, d.bottom_right.y)
     ass:rect_cw(region.x + region.w, region.y, d.bottom_right.x, d.bottom_right.y)
     return ass:draw_stop()
   end,
   draw = function(self)
     local window = { }
     window.w, window.h = mp.get_osd_size()
     local ass = assdraw.ass_new()
     self:draw_box(ass)
     ass:new_event()
     self:setup_text(ass)
     ass:append(tostring(bold('Crop:')) .. "\\N")
     ass:append(tostring(bold('1:')) .. " change point A (" .. tostring(self.pointA.x) .. ", " .. tostring(self.pointA.y) .. ")\\N")
     ass:append(tostring(bold('2:')) .. " change point B (" .. tostring(self.pointB.x) .. ", " .. tostring(self.pointB.y) .. ")\\N")
     ass:append(tostring(bold('r:')) .. " reset to whole screen\\N")
     ass:append(tostring(bold('ESC:')) .. " cancel crop\\N")
     local width, height = math.abs(self.pointA.x - self.pointB.x), math.abs(self.pointA.y - self.pointB.y)
     ass:append(tostring(bold('ENTER:')) .. " confirm crop (" .. tostring(width) .. "x" .. tostring(height) .. ")\\N")
     return mp.set_osd_ass(window.w, window.h, ass.text)
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self, callback, region)
     self.pointA = VideoPoint()
     self.pointB = VideoPoint()
     self.keybinds = {
       ["1"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.setPointA
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["2"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.setPointB
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["r"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.reset
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["ESC"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.cancel
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["ENTER"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.finish
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)()
     }
     self:reset()
     self.callback = callback
     if region and region:is_valid() then
       self.pointA.x = region.x
       self.pointA.y = region.y
       self.pointB.x = region.x + region.w
       self.pointB.y = region.y + region.h
     end
   end,
   __base = _base_0,
   __name = "CropPage",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 CropPage = _class_0
end
local Option
do
 local _class_0
 local _base_0 = {
   hasPrevious = function(self)
     local _exp_0 = self.optType
     if "bool" == _exp_0 then
       return true
     elseif "int" == _exp_0 then
       if self.opts.min then
         return self.value > self.opts.min
       else
         return true
       end
     elseif "list" == _exp_0 then
       return self.value > 1
     end
   end,
   hasNext = function(self)
     local _exp_0 = self.optType
     if "bool" == _exp_0 then
       return true
     elseif "int" == _exp_0 then
       if self.opts.max then
         return self.value < self.opts.max
       else
         return true
       end
     elseif "list" == _exp_0 then
       return self.value < #self.opts.possibleValues
     end
   end,
   leftKey = function(self)
     local _exp_0 = self.optType
     if "bool" == _exp_0 then
       self.value = not self.value
     elseif "int" == _exp_0 then
       self.value = self.value - self.opts.step
       if self.opts.min and self.opts.min > self.value then
         self.value = self.opts.min
       end
     elseif "list" == _exp_0 then
       if self.value > 1 then
         self.value = self.value - 1
       end
     end
   end,
   rightKey = function(self)
     local _exp_0 = self.optType
     if "bool" == _exp_0 then
       self.value = not self.value
     elseif "int" == _exp_0 then
       self.value = self.value + self.opts.step
       if self.opts.max and self.opts.max < self.value then
         self.value = self.opts.max
       end
     elseif "list" == _exp_0 then
       if self.value < #self.opts.possibleValues then
         self.value = self.value + 1
       end
     end
   end,
   getValue = function(self)
     local _exp_0 = self.optType
     if "bool" == _exp_0 then
       return self.value
     elseif "int" == _exp_0 then
       return self.value
     elseif "list" == _exp_0 then
       local value, _
       do
         local _obj_0 = self.opts.possibleValues[self.value]
         value, _ = _obj_0[1], _obj_0[2]
       end
       return value
     end
   end,
   setValue = function(self, value)
     local _exp_0 = self.optType
     if "bool" == _exp_0 then
       self.value = value
     elseif "int" == _exp_0 then
       self.value = value
     elseif "list" == _exp_0 then
       local set = false
       for i, possiblePair in ipairs(self.opts.possibleValues) do
         local possibleValue, _
         possibleValue, _ = possiblePair[1], possiblePair[2]
         if possibleValue == value then
           set = true
           self.value = i
           break
         end
       end
       if not set then
         return msg.warn("Tried to set invalid value " .. tostring(value) .. " to " .. tostring(self.displayText) .. " option.")
       end
     end
   end,
   getDisplayValue = function(self)
     local _exp_0 = self.optType
     if "bool" == _exp_0 then
       return self.value and "yes" or "no"
     elseif "int" == _exp_0 then
       if self.opts.altDisplayNames and self.opts.altDisplayNames[self.value] then
         return self.opts.altDisplayNames[self.value]
       else
         return tostring(self.value)
       end
     elseif "list" == _exp_0 then
       local value, displayValue
       do
         local _obj_0 = self.opts.possibleValues[self.value]
         value, displayValue = _obj_0[1], _obj_0[2]
       end
       return displayValue or value
     end
   end,
   draw = function(self, ass, selected)
     if selected then
       ass:append(tostring(bold(self.displayText)) .. ": ")
     else
       ass:append(tostring(self.displayText) .. ": ")
     end
     if self:hasPrevious() then
       ass:append("◀ ")
     end
     ass:append(self:getDisplayValue())
     if self:hasNext() then
       ass:append(" ▶")
     end
     return ass:append("\\N")
   end,
   optVisible = function(self)
     if self.visibleCheckFn == nil then
       return true
     else
       return self.visibleCheckFn()
     end
   end
 }
 _base_0.__index = _base_0
 _class_0 = setmetatable({
   __init = function(self, optType, displayText, value, opts, visibleCheckFn)
     self.optType = optType
     self.displayText = displayText
     self.opts = opts
     self.value = 1
     self.visibleCheckFn = visibleCheckFn
     return self:setValue(value)
   end,
   __base = _base_0,
   __name = "Option"
 }, {
   __index = _base_0,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 Option = _class_0
end
local EncodeOptionsPage
do
 local _class_0
 local _parent_0 = Page
 local _base_0 = {
   getCurrentOption = function(self)
     return self.options[self.currentOption][2]
   end,
   leftKey = function(self)
     (self:getCurrentOption()):leftKey()
     return self:draw()
   end,
   rightKey = function(self)
     (self:getCurrentOption()):rightKey()
     return self:draw()
   end,
   prevOpt = function(self)
     for i = self.currentOption - 1, 1, -1 do
       if self.options[i][2]:optVisible() then
         self.currentOption = i
         break
       end
     end
     return self:draw()
   end,
   nextOpt = function(self)
     for i = self.currentOption + 1, #self.options do
       if self.options[i][2]:optVisible() then
         self.currentOption = i
         break
       end
     end
     return self:draw()
   end,
   confirmOpts = function(self)
     for _, optPair in ipairs(self.options) do
       local optName, opt
       optName, opt = optPair[1], optPair[2]
       options[optName] = opt:getValue()
     end
     self:hide()
     return self.callback(true)
   end,
   cancelOpts = function(self)
     self:hide()
     return self.callback(false)
   end,
   draw = function(self)
     local window_w, window_h = mp.get_osd_size()
     local ass = assdraw.ass_new()
     ass:new_event()
     self:setup_text(ass)
     ass:append(tostring(bold('Options:')) .. "\\N\\N")
     for i, optPair in ipairs(self.options) do
       local opt = optPair[2]
       if opt:optVisible() then
         opt:draw(ass, self.currentOption == i)
       end
     end
     ass:append("\\N▲ / ▼: navigate\\N")
     ass:append(tostring(bold('ENTER:')) .. " confirm options\\N")
     ass:append(tostring(bold('ESC:')) .. " cancel\\N")
     return mp.set_osd_ass(window_w, window_h, ass.text)
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self, callback)
     self.callback = callback
     self.currentOption = 1
     local scaleHeightOpts = {
       possibleValues = {
         {
           -1,
           "no"
         },
         {
           144
         },
         {
           240
         },
         {
           360
         },
         {
           480
         },
         {
           540
         },
         {
           720
         },
         {
           1080
         },
         {
           1440
         },
         {
           2160
         }
       }
     }
     local filesizeOpts = {
       step = 250,
       min = 0,
       altDisplayNames = {
         [0] = "0 (constant quality)"
       }
     }
     local crfOpts = {
       step = 1,
       min = -1,
       altDisplayNames = {
         [-1] = "disabled"
       }
     }
     local fpsOpts = {
       possibleValues = {
         {
           -1,
           "source"
         },
         {
           15
         },
         {
           24
         },
         {
           30
         },
         {
           48
         },
         {
           50
         },
         {
           60
         },
         {
           120
         },
         {
           240
         }
       }
     }
     local formatIds = {
       "webm-vp8",
       "webm-vp9",
       "mp4",
       "mp4-nvenc",
       "raw",
       "mp3",
       "gif"
     }
     local formatOpts = {
       possibleValues = (function()
         local _accum_0 = { }
         local _len_0 = 1
         for _index_0 = 1, #formatIds do
           local fId = formatIds[_index_0]
           _accum_0[_len_0] = {
             fId,
             formats[fId].displayName
           }
           _len_0 = _len_0 + 1
         end
         return _accum_0
       end)()
     }
     local gifDitherOpts = {
       possibleValues = {
         {
           0,
           "bayer_scale 0"
         },
         {
           1,
           "bayer_scale 1"
         },
         {
           2,
           "bayer_scale 2"
         },
         {
           3,
           "bayer_scale 3"
         },
         {
           4,
           "bayer_scale 4"
         },
         {
           5,
           "bayer_scale 5"
         },
         {
           6,
           "sierra2_4a"
         }
       }
     }
     self.options = {
       {
         "output_format",
         Option("list", "Output Format", options.output_format, formatOpts)
       },
       {
         "twopass",
         Option("bool", "Two Pass", options.twopass)
       },
       {
         "apply_current_filters",
         Option("bool", "Apply Current Video Filters", options.apply_current_filters)
       },
       {
         "scale_height",
         Option("list", "Scale Height", options.scale_height, scaleHeightOpts)
       },
       {
         "strict_filesize_constraint",
         Option("bool", "Strict Filesize Constraint", options.strict_filesize_constraint)
       },
       {
         "write_filename_on_metadata",
         Option("bool", "Write Filename on Metadata", options.write_filename_on_metadata)
       },
       {
         "target_filesize",
         Option("int", "Target Filesize", options.target_filesize, filesizeOpts)
       },
       {
         "crf",
         Option("int", "CRF", options.crf, crfOpts)
       },
       {
         "fps",
         Option("list", "FPS", options.fps, fpsOpts)
       },
       {
         "gif_dither",
         Option("list", "GIF Dither Type", options.gif_dither, gifDitherOpts, function()
           return self.options[1][2]:getValue() == "gif"
         end)
       },
       {
         "force_square_pixels",
         Option("bool", "Force Square Pixels", options.force_square_pixels)
       }
     }
     self.keybinds = {
       ["LEFT"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.leftKey
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["RIGHT"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.rightKey
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["UP"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.prevOpt
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["DOWN"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.nextOpt
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["ENTER"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.confirmOpts
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["ESC"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.cancelOpts
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)()
     }
   end,
   __base = _base_0,
   __name = "EncodeOptionsPage",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 EncodeOptionsPage = _class_0
end
local PreviewPage
do
 local _class_0
 local _parent_0 = Page
 local _base_0 = {
   prepare = function(self)
     local vf = mp.get_property_native("vf")
     vf[#vf + 1] = {
       name = "sub"
     }
     if self.region:is_valid() then
       vf[#vf + 1] = {
         name = "crop",
         params = {
           w = tostring(self.region.w),
           h = tostring(self.region.h),
           x = tostring(self.region.x),
           y = tostring(self.region.y)
         }
       }
     end
     mp.set_property_native("vf", vf)
     if self.startTime > -1 and self.endTime > -1 then
       mp.set_property_native("ab-loop-a", self.startTime)
       mp.set_property_native("ab-loop-b", self.endTime)
       mp.set_property_native("time-pos", self.startTime)
     end
     return mp.set_property_native("pause", false)
   end,
   dispose = function(self)
     mp.set_property("ab-loop-a", "no")
     mp.set_property("ab-loop-b", "no")
     for prop, value in pairs(self.originalProperties) do
       mp.set_property_native(prop, value)
     end
   end,
   draw = function(self)
     local window_w, window_h = mp.get_osd_size()
     local ass = assdraw.ass_new()
     ass:new_event()
     self:setup_text(ass)
     ass:append("Press " .. tostring(bold('ESC')) .. " to exit preview.\\N")
     return mp.set_osd_ass(window_w, window_h, ass.text)
   end,
   cancel = function(self)
     self:hide()
     return self.callback()
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self, callback, region, startTime, endTime)
     self.callback = callback
     self.originalProperties = {
       ["vf"] = mp.get_property_native("vf"),
       ["time-pos"] = mp.get_property_native("time-pos"),
       ["pause"] = mp.get_property_native("pause")
     }
     self.keybinds = {
       ["ESC"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.cancel
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)()
     }
     self.region = region
     self.startTime = startTime
     self.endTime = endTime
     self.isLoop = false
   end,
   __base = _base_0,
   __name = "PreviewPage",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 PreviewPage = _class_0
end
local MainPage
do
 local _class_0
 local _parent_0 = Page
 local _base_0 = {
   setStartTime = function(self)
     self.startTime = mp.get_property_number("time-pos")
     if self.visible then
       self:clear()
       return self:draw()
     end
   end,
   setEndTime = function(self)
     self.endTime = mp.get_property_number("time-pos")
     if self.visible then
       self:clear()
       return self:draw()
     end
   end,
   setupStartAndEndTimes = function(self)
     if mp.get_property_native("duration") then
       self.startTime = 0
       self.endTime = mp.get_property_native("duration")
     else
       self.startTime = -1
       self.endTime = -1
     end
     if self.visible then
       self:clear()
       return self:draw()
     end
   end,
   draw = function(self)
     local window_w, window_h = mp.get_osd_size()
     local ass = assdraw.ass_new()
     ass:new_event()
     self:setup_text(ass)
     ass:append(tostring(bold('WebM maker')) .. "\\N\\N")
     ass:append(tostring(bold('c:')) .. " crop\\N")
     ass:append(tostring(bold('1:')) .. " set start time (current is " .. tostring(seconds_to_time_string(self.startTime)) .. ")\\N")
     ass:append(tostring(bold('2:')) .. " set end time (current is " .. tostring(seconds_to_time_string(self.endTime)) .. ")\\N")
     ass:append(tostring(bold('o:')) .. " change encode options\\N")
     ass:append(tostring(bold('p:')) .. " preview\\N")
     ass:append(tostring(bold('e:')) .. " encode\\N\\N")
     ass:append(tostring(bold('ESC:')) .. " close\\N")
     return mp.set_osd_ass(window_w, window_h, ass.text)
   end,
   show = function(self)
     _class_0.__parent.show(self)
     return emit_event("show-main-page")
   end,
   onUpdateCropRegion = function(self, updated, newRegion)
     if updated then
       self.region = newRegion
     end
     return self:show()
   end,
   crop = function(self)
     self:hide()
     local cropPage = CropPage((function()
       local _base_1 = self
       local _fn_0 = _base_1.onUpdateCropRegion
       return function(...)
         return _fn_0(_base_1, ...)
       end
     end)(), self.region)
     return cropPage:show()
   end,
   onOptionsChanged = function(self, updated)
     return self:show()
   end,
   changeOptions = function(self)
     self:hide()
     local encodeOptsPage = EncodeOptionsPage((function()
       local _base_1 = self
       local _fn_0 = _base_1.onOptionsChanged
       return function(...)
         return _fn_0(_base_1, ...)
       end
     end)())
     return encodeOptsPage:show()
   end,
   onPreviewEnded = function(self)
     return self:show()
   end,
   preview = function(self)
     self:hide()
     local previewPage = PreviewPage((function()
       local _base_1 = self
       local _fn_0 = _base_1.onPreviewEnded
       return function(...)
         return _fn_0(_base_1, ...)
       end
     end)(), self.region, self.startTime, self.endTime)
     return previewPage:show()
   end,
   encode = function(self)
     self:hide()
     if self.startTime < 0 then
       message("No start time, aborting")
       return
     end
     if self.endTime < 0 then
       message("No end time, aborting")
       return
     end
     if self.startTime >= self.endTime then
       message("Start time is ahead of end time, aborting")
       return
     end
     return encode(self.region, self.startTime, self.endTime)
   end
 }
 _base_0.__index = _base_0
 setmetatable(_base_0, _parent_0.__base)
 _class_0 = setmetatable({
   __init = function(self)
     self.keybinds = {
       ["c"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.crop
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["1"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.setStartTime
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["2"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.setEndTime
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["o"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.changeOptions
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["p"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.preview
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["e"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.encode
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)(),
       ["ESC"] = (function()
         local _base_1 = self
         local _fn_0 = _base_1.hide
         return function(...)
           return _fn_0(_base_1, ...)
         end
       end)()
     }
     self.startTime = -1
     self.endTime = -1
     self.region = Region()
   end,
   __base = _base_0,
   __name = "MainPage",
   __parent = _parent_0
 }, {
   __index = function(cls, name)
     local val = rawget(_base_0, name)
     if val == nil then
       local parent = rawget(cls, "__parent")
       if parent then
         return parent[name]
       end
     else
       return val
     end
   end,
   __call = function(cls, ...)
     local _self_0 = setmetatable({}, _base_0)
     cls.__init(_self_0, ...)
     return _self_0
   end
 })
 _base_0.__class = _class_0
 if _parent_0.__inherited then
   _parent_0.__inherited(_parent_0, _class_0)
 end
 MainPage = _class_0
end
monitor_dimensions()
local mainPage = MainPage()
mp.add_key_binding(options.keybind, "display-webm-encoder", (function()
 local _base_0 = mainPage
 local _fn_0 = _base_0.show
 return function(...)
   return _fn_0(_base_0, ...)
 end
end)(), {
 repeatable = false
})
mp.register_event("file-loaded", (function()
 local _base_0 = mainPage
 local _fn_0 = _base_0.setupStartAndEndTimes
 return function(...)
   return _fn_0(_base_0, ...)
 end
end)())
msg.verbose("Loaded mpv-webm script!")
return emit_event("script-loaded")