dotfiles

My collection of dotfiles
git clone git://git.stellar-nexus.ru/dotfiles
Log | Files | Refs

webm.lua (88905B)


      1 local mp = require("mp")
      2 local assdraw = require("mp.assdraw")
      3 local msg = require("mp.msg")
      4 local utils = require("mp.utils")
      5 local mpopts = require("mp.options")
      6 local options = {
      7 	-- Defaults to shift+w
      8 	keybind = "W",
      9 	-- If empty, saves on the same directory of the playing video.
     10 	-- A starting "~" will be replaced by the home dir.
     11 	-- This field is delimited by double-square-brackets - [[ and ]] - instead of
     12 	-- quotes, because Windows users might run into a issue when using
     13 	-- backslashes as a path separator. Examples of valid inputs for this field
     14 	-- would be: [[]] (the default, empty value), [[C:\Users\John]] (on Windows),
     15 	-- and [[/home/john]] (on Unix-like systems eg. Linux).
     16 	-- The [[]] delimiter is not needed when using from a configuration file
     17 	-- in the script-opts folder.
     18 	output_directory = [[]],
     19 	run_detached = false,
     20 	-- Template string for the output file
     21 	-- %f - Filename, with extension
     22 	-- %F - Filename, without extension
     23 	-- %T - Media title, if it exists, or filename, with extension (useful for some streams, such as YouTube).
     24 	-- %s, %e - Start and end time, with milliseconds
     25 	-- %S, %E - Start and end time, without milliseconds
     26 	-- %M - "-audio", if audio is enabled, empty otherwise
     27 	-- %R - "-(height)p", where height is the video's height, or scale_height, if it's enabled.
     28 	-- More specifiers are supported, see https://mpv.io/manual/master/#options-screenshot-template
     29 	-- Property expansion is supported (with %{} at top level, ${} when nested), see https://mpv.io/manual/master/#property-expansion
     30 	output_template = "%F-[%s-%e]%M",
     31 	-- Scale video to a certain height, keeping the aspect ratio. -1 disables it.
     32 	scale_height = -1,
     33 	-- Change the FPS of the output video, dropping or duplicating frames as needed.
     34 	-- -1 means the FPS will be unchanged from the source.
     35 	fps = -1,
     36 	-- Target filesize, in kB. This will be used to calculate the bitrate
     37 	-- used on the encode. If this is set to <= 0, the video bitrate will be set
     38 	-- to 0, which might enable constant quality modes, depending on the
     39 	-- video codec that's used (VP8 and VP9, for example).
     40 	target_filesize = 2500,
     41 	-- If true, will use stricter flags to ensure the resulting file doesn't
     42 	-- overshoot the target filesize. Not recommended, as constrained quality
     43 	-- mode should work well, unless you're really having trouble hitting
     44 	-- the target size.
     45 	strict_filesize_constraint = false,
     46 	strict_bitrate_multiplier = 0.95,
     47 	-- In kilobits.
     48 	strict_audio_bitrate = 64,
     49 	-- Sets the output format, from a few predefined ones.
     50 	-- Currently we have:
     51 	-- av1
     52 	-- hevc
     53 	-- webm-vp9 (libvpx-vp9/libopus)
     54 	-- avc (h264/AAC)
     55 	-- avc-nvenc (h264-NVENC/AAC)
     56 	-- webm-vp8 (libvpx/libvorbis)
     57 	-- gif
     58 	-- mp3 (libmp3lame)
     59 	-- and raw (rawvideo/pcm_s16le).
     60 	output_format = "webm-vp8",
     61 	twopass = true,
     62 	-- If set, applies the video filters currently used on the playback to the encode.
     63 	apply_current_filters = true,
     64 	-- If set, writes the video's filename to the "Title" field on the metadata.
     65 	write_filename_on_metadata = false,
     66 	-- Set the number of encoding threads, for codecs libvpx and libvpx-vp9
     67 	threads = 4,
     68 	additional_flags = "",
     69 	-- Constant Rate Factor (CRF). The value meaning and limits may change,
     70 	-- from codec to codec. Set to -1 to disable.
     71 	crf = 10,
     72 	-- Useful for flags that may impact output filesize, such as qmin, qmax etc
     73 	-- Won't be applied when strict_filesize_constraint is on.
     74 	non_strict_additional_flags = "",
     75 	-- Display the encode progress, in %. Requires run_detached to be disabled.
     76 	-- On Windows, it shows a cmd popup. "auto" will display progress on non-Windows platforms.
     77 	display_progress = "auto",
     78 	-- The font size used in the menu. Isn't used for the notifications (started encode, finished encode etc)
     79 	font_size = 28,
     80 	margin = 10,
     81 	message_duration = 5,
     82 	-- gif dither mode, 0-5 for bayer w/ bayer_scale 0-5, 6 for paletteuse default (sierra2_4a)
     83 	gif_dither = 2,
     84 	-- Force square pixels on output video
     85 	-- Some players like recent Firefox versions display videos with non-square pixels with wrong aspect ratio
     86 	force_square_pixels = false,
     87 }
     88 
     89 mpopts.read_options(options)
     90 local base64_chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
     91 
     92 -- encoding
     93 function base64_encode(data)
     94     return ((data:gsub('.', function(x) 
     95         local r,b='',x:byte()
     96         for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
     97         return r;
     98     end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
     99         if (#x < 6) then return '' end
    100         local c=0
    101         for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
    102         return base64_chars:sub(c+1,c+1)
    103     end)..({ '', '==', '=' })[#data%3+1])
    104 end
    105 
    106 -- decoding
    107 function base64_decode(data)
    108     data = string.gsub(data, '[^'..base64_chars..'=]', '')
    109     return (data:gsub('.', function(x)
    110         if (x == '=') then return '' end
    111         local r,f='',(base64_chars:find(x)-1)
    112         for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
    113         return r;
    114     end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
    115         if (#x ~= 8) then return '' end
    116         local c=0
    117         for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
    118         return string.char(c)
    119     end))
    120 end
    121 local emit_event
    122 emit_event = function(event_name, ...)
    123   return mp.commandv("script-message", "webm-" .. tostring(event_name), ...)
    124 end
    125 local test_set_options
    126 test_set_options = function(new_options_json)
    127   local new_options = utils.parse_json(new_options_json)
    128   for k, v in pairs(new_options) do
    129     options[k] = v
    130   end
    131 end
    132 mp.register_script_message("mpv-webm-set-options", test_set_options)
    133 local bold
    134 bold = function(text)
    135   return "{\\b1}" .. tostring(text) .. "{\\b0}"
    136 end
    137 local message
    138 message = function(text, duration)
    139   local ass = mp.get_property_osd("osd-ass-cc/0")
    140   ass = ass .. text
    141   return mp.osd_message(ass, duration or options.message_duration)
    142 end
    143 local append
    144 append = function(a, b)
    145   for _, val in ipairs(b) do
    146     a[#a + 1] = val
    147   end
    148   return a
    149 end
    150 local seconds_to_time_string
    151 seconds_to_time_string = function(seconds, no_ms, full)
    152   if seconds < 0 then
    153     return "unknown"
    154   end
    155   local ret = ""
    156   if not (no_ms) then
    157     ret = string.format(".%03d", seconds * 1000 % 1000)
    158   end
    159   ret = string.format("%02d:%02d%s", math.floor(seconds / 60) % 60, math.floor(seconds) % 60, ret)
    160   if full or seconds > 3600 then
    161     ret = string.format("%d:%s", math.floor(seconds / 3600), ret)
    162   end
    163   return ret
    164 end
    165 local seconds_to_path_element
    166 seconds_to_path_element = function(seconds, no_ms, full)
    167   local time_string = seconds_to_time_string(seconds, no_ms, full)
    168   local _
    169   time_string, _ = time_string:gsub(":", ".")
    170   return time_string
    171 end
    172 local file_exists
    173 file_exists = function(name)
    174   local info, err = utils.file_info(name)
    175   if info ~= nil then
    176     return true
    177   end
    178   return false
    179 end
    180 local expand_properties
    181 expand_properties = function(text, magic)
    182   if magic == nil then
    183     magic = "$"
    184   end
    185   for prefix, raw, prop, colon, fallback, closing in text:gmatch("%" .. magic .. "{([?!]?)(=?)([^}:]*)(:?)([^}]*)(}*)}") do
    186     local err
    187     local prop_value
    188     local compare_value
    189     local original_prop = prop
    190     local get_property = mp.get_property_osd
    191     if raw == "=" then
    192       get_property = mp.get_property
    193     end
    194     if prefix ~= "" then
    195       for actual_prop, compare in prop:gmatch("(.-)==(.*)") do
    196         prop = actual_prop
    197         compare_value = compare
    198       end
    199     end
    200     if colon == ":" then
    201       prop_value, err = get_property(prop, fallback)
    202     else
    203       prop_value, err = get_property(prop, "(error)")
    204     end
    205     prop_value = tostring(prop_value)
    206     if prefix == "?" then
    207       if compare_value == nil then
    208         prop_value = err == nil and fallback .. closing or ""
    209       else
    210         prop_value = prop_value == compare_value and fallback .. closing or ""
    211       end
    212       prefix = "%" .. prefix
    213     elseif prefix == "!" then
    214       if compare_value == nil then
    215         prop_value = err ~= nil and fallback .. closing or ""
    216       else
    217         prop_value = prop_value ~= compare_value and fallback .. closing or ""
    218       end
    219     else
    220       prop_value = prop_value .. closing
    221     end
    222     if colon == ":" then
    223       local _
    224       text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. ":" .. fallback:gsub("%W", "%%%1") .. closing .. "}", expand_properties(prop_value))
    225     else
    226       local _
    227       text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. closing .. "}", prop_value)
    228     end
    229   end
    230   return text
    231 end
    232 local format_filename
    233 format_filename = function(startTime, endTime, videoFormat)
    234   local hasAudioCodec = videoFormat.audioCodec ~= ""
    235   local replaceFirst = {
    236     ["%%mp"] = "%%mH.%%mM.%%mS",
    237     ["%%mP"] = "%%mH.%%mM.%%mS.%%mT",
    238     ["%%p"] = "%%wH.%%wM.%%wS",
    239     ["%%P"] = "%%wH.%%wM.%%wS.%%wT"
    240   }
    241   local replaceTable = {
    242     ["%%wH"] = string.format("%02d", math.floor(startTime / (60 * 60))),
    243     ["%%wh"] = string.format("%d", math.floor(startTime / (60 * 60))),
    244     ["%%wM"] = string.format("%02d", math.floor(startTime / 60 % 60)),
    245     ["%%wm"] = string.format("%d", math.floor(startTime / 60)),
    246     ["%%wS"] = string.format("%02d", math.floor(startTime % 60)),
    247     ["%%ws"] = string.format("%d", math.floor(startTime)),
    248     ["%%wf"] = string.format("%s", startTime),
    249     ["%%wT"] = string.sub(string.format("%.3f", startTime % 1), 3),
    250     ["%%mH"] = string.format("%02d", math.floor(endTime / (60 * 60))),
    251     ["%%mh"] = string.format("%d", math.floor(endTime / (60 * 60))),
    252     ["%%mM"] = string.format("%02d", math.floor(endTime / 60 % 60)),
    253     ["%%mm"] = string.format("%d", math.floor(endTime / 60)),
    254     ["%%mS"] = string.format("%02d", math.floor(endTime % 60)),
    255     ["%%ms"] = string.format("%d", math.floor(endTime)),
    256     ["%%mf"] = string.format("%s", endTime),
    257     ["%%mT"] = string.sub(string.format("%.3f", endTime % 1), 3),
    258     ["%%f"] = mp.get_property("filename"),
    259     ["%%F"] = mp.get_property("filename/no-ext"),
    260     ["%%s"] = seconds_to_path_element(startTime),
    261     ["%%S"] = seconds_to_path_element(startTime, true),
    262     ["%%e"] = seconds_to_path_element(endTime),
    263     ["%%E"] = seconds_to_path_element(endTime, true),
    264     ["%%T"] = mp.get_property("media-title"),
    265     ["%%M"] = (mp.get_property_native('aid') and not mp.get_property_native('mute') and hasAudioCodec) and '-audio' or '',
    266     ["%%R"] = (options.scale_height ~= -1) and "-" .. tostring(options.scale_height) .. "p" or "-" .. tostring(mp.get_property_native('height')) .. "p",
    267     ["%%mb"] = options.target_filesize / 1000,
    268     ["%%t%%"] = "%%"
    269   }
    270   local filename = options.output_template
    271   for format, value in pairs(replaceFirst) do
    272     local _
    273     filename, _ = filename:gsub(format, value)
    274   end
    275   for format, value in pairs(replaceTable) do
    276     local _
    277     filename, _ = filename:gsub(format, value)
    278   end
    279   if mp.get_property_bool("demuxer-via-network", false) then
    280     local _
    281     filename, _ = filename:gsub("%%X{([^}]*)}", "%1")
    282     filename, _ = filename:gsub("%%x", "")
    283   else
    284     local x = string.gsub(mp.get_property("stream-open-filename", ""), string.gsub(mp.get_property("filename", ""), "%W", "%%%1") .. "$", "")
    285     local _
    286     filename, _ = filename:gsub("%%X{[^}]*}", x)
    287     filename, _ = filename:gsub("%%x", x)
    288   end
    289   filename = expand_properties(filename, "%")
    290   for format in filename:gmatch("%%t([aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ])") do
    291     local _
    292     filename, _ = filename:gsub("%%t" .. format, os.date("%" .. format))
    293   end
    294   local _
    295   filename, _ = filename:gsub("[<>:\"/\\|?*]", "")
    296   return tostring(filename) .. "." .. tostring(videoFormat.outputExtension)
    297 end
    298 local parse_directory
    299 parse_directory = function(dir)
    300   local home_dir = os.getenv("HOME")
    301   if not home_dir then
    302     home_dir = os.getenv("USERPROFILE")
    303   end
    304   if not home_dir then
    305     local drive = os.getenv("HOMEDRIVE")
    306     local path = os.getenv("HOMEPATH")
    307     if drive and path then
    308       home_dir = utils.join_path(drive, path)
    309     else
    310       msg.warn("Couldn't find home dir.")
    311       home_dir = ""
    312     end
    313   end
    314   local _
    315   dir, _ = dir:gsub("^~", home_dir)
    316   return dir
    317 end
    318 local is_windows = type(package) == "table" and type(package.config) == "string" and package.config:sub(1, 1) == "\\"
    319 local trim
    320 trim = function(s)
    321   return s:match("^%s*(.-)%s*$")
    322 end
    323 local get_null_path
    324 get_null_path = function()
    325   if file_exists("/dev/null") then
    326     return "/dev/null"
    327   end
    328   return "NUL"
    329 end
    330 local run_subprocess
    331 run_subprocess = function(params)
    332   local res = utils.subprocess(params)
    333   msg.verbose("Command stdout: ")
    334   msg.verbose(res.stdout)
    335   if res.status ~= 0 then
    336     msg.verbose("Command failed! Reason: ", res.error, " Killed by us? ", res.killed_by_us and "yes" or "no")
    337     return false
    338   end
    339   return true
    340 end
    341 local shell_escape
    342 shell_escape = function(args)
    343   local ret = { }
    344   for i, a in ipairs(args) do
    345     local s = tostring(a)
    346     if string.match(s, "[^A-Za-z0-9_/:=-]") then
    347       if is_windows then
    348         s = '"' .. string.gsub(s, '"', '"\\""') .. '"'
    349       else
    350         s = "'" .. string.gsub(s, "'", "'\\''") .. "'"
    351       end
    352     end
    353     table.insert(ret, s)
    354   end
    355   local concat = table.concat(ret, " ")
    356   if is_windows then
    357     concat = '"' .. concat .. '"'
    358   end
    359   return concat
    360 end
    361 local run_subprocess_popen
    362 run_subprocess_popen = function(command_line)
    363   local command_line_string = shell_escape(command_line)
    364   command_line_string = command_line_string .. " 2>&1"
    365   msg.verbose("run_subprocess_popen: running " .. tostring(command_line_string))
    366   return io.popen(command_line_string)
    367 end
    368 local calculate_scale_factor
    369 calculate_scale_factor = function()
    370   local baseResY = 720
    371   local osd_w, osd_h = mp.get_osd_size()
    372   return osd_h / baseResY
    373 end
    374 local should_display_progress
    375 should_display_progress = function()
    376   if options.display_progress == "auto" then
    377     return not is_windows
    378   end
    379   return options.display_progress
    380 end
    381 local reverse
    382 reverse = function(list)
    383   local _accum_0 = { }
    384   local _len_0 = 1
    385   local _max_0 = 1
    386   for _index_0 = #list, _max_0 < 0 and #list + _max_0 or _max_0, -1 do
    387     local element = list[_index_0]
    388     _accum_0[_len_0] = element
    389     _len_0 = _len_0 + 1
    390   end
    391   return _accum_0
    392 end
    393 local get_pass_logfile_path
    394 get_pass_logfile_path = function(encode_out_path)
    395   return tostring(encode_out_path) .. "-video-pass1.log"
    396 end
    397 local dimensions_changed = true
    398 local _video_dimensions = { }
    399 local get_video_dimensions
    400 get_video_dimensions = function()
    401   if not (dimensions_changed) then
    402     return _video_dimensions
    403   end
    404   local video_params = mp.get_property_native("video-out-params")
    405   if not video_params then
    406     return nil
    407   end
    408   dimensions_changed = false
    409   local keep_aspect = mp.get_property_bool("keepaspect")
    410   local w = video_params["w"]
    411   local h = video_params["h"]
    412   local dw = video_params["dw"]
    413   local dh = video_params["dh"]
    414   if mp.get_property_number("video-rotate") % 180 == 90 then
    415     w, h = h, w
    416     dw, dh = dh, dw
    417   end
    418   _video_dimensions = {
    419     top_left = { },
    420     bottom_right = { },
    421     ratios = { }
    422   }
    423   local window_w, window_h = mp.get_osd_size()
    424   if keep_aspect then
    425     local unscaled = mp.get_property_native("video-unscaled")
    426     local panscan = mp.get_property_number("panscan")
    427     local fwidth = window_w
    428     local fheight = math.floor(window_w / dw * dh)
    429     if fheight > window_h or fheight < h then
    430       local tmpw = math.floor(window_h / dh * dw)
    431       if tmpw <= window_w then
    432         fheight = window_h
    433         fwidth = tmpw
    434       end
    435     end
    436     local vo_panscan_area = window_h - fheight
    437     local f_w = fwidth / fheight
    438     local f_h = 1
    439     if vo_panscan_area == 0 then
    440       vo_panscan_area = window_h - fwidth
    441       f_w = 1
    442       f_h = fheight / fwidth
    443     end
    444     if unscaled or unscaled == "downscale-big" then
    445       vo_panscan_area = 0
    446       if unscaled or (dw <= window_w and dh <= window_h) then
    447         fwidth = dw
    448         fheight = dh
    449       end
    450     end
    451     local scaled_width = fwidth + math.floor(vo_panscan_area * panscan * f_w)
    452     local scaled_height = fheight + math.floor(vo_panscan_area * panscan * f_h)
    453     local split_scaling
    454     split_scaling = function(dst_size, scaled_src_size, zoom, align, pan)
    455       scaled_src_size = math.floor(scaled_src_size * 2 ^ zoom)
    456       align = (align + 1) / 2
    457       local dst_start = math.floor((dst_size - scaled_src_size) * align + pan * scaled_src_size)
    458       if dst_start < 0 then
    459         dst_start = dst_start + 1
    460       end
    461       local dst_end = dst_start + scaled_src_size
    462       if dst_start >= dst_end then
    463         dst_start = 0
    464         dst_end = 1
    465       end
    466       return dst_start, dst_end
    467     end
    468     local zoom = mp.get_property_number("video-zoom")
    469     local align_x = mp.get_property_number("video-align-x")
    470     local pan_x = mp.get_property_number("video-pan-x")
    471     _video_dimensions.top_left.x, _video_dimensions.bottom_right.x = split_scaling(window_w, scaled_width, zoom, align_x, pan_x)
    472     local align_y = mp.get_property_number("video-align-y")
    473     local pan_y = mp.get_property_number("video-pan-y")
    474     _video_dimensions.top_left.y, _video_dimensions.bottom_right.y = split_scaling(window_h, scaled_height, zoom, align_y, pan_y)
    475   else
    476     _video_dimensions.top_left.x = 0
    477     _video_dimensions.bottom_right.x = window_w
    478     _video_dimensions.top_left.y = 0
    479     _video_dimensions.bottom_right.y = window_h
    480   end
    481   _video_dimensions.ratios.w = w / (_video_dimensions.bottom_right.x - _video_dimensions.top_left.x)
    482   _video_dimensions.ratios.h = h / (_video_dimensions.bottom_right.y - _video_dimensions.top_left.y)
    483   return _video_dimensions
    484 end
    485 local set_dimensions_changed
    486 set_dimensions_changed = function()
    487   dimensions_changed = true
    488 end
    489 local monitor_dimensions
    490 monitor_dimensions = function()
    491   local properties = {
    492     "keepaspect",
    493     "video-out-params",
    494     "video-unscaled",
    495     "panscan",
    496     "video-zoom",
    497     "video-align-x",
    498     "video-pan-x",
    499     "video-align-y",
    500     "video-pan-y",
    501     "osd-width",
    502     "osd-height"
    503   }
    504   for _, p in ipairs(properties) do
    505     mp.observe_property(p, "native", set_dimensions_changed)
    506   end
    507 end
    508 local clamp
    509 clamp = function(min, val, max)
    510   if val <= min then
    511     return min
    512   end
    513   if val >= max then
    514     return max
    515   end
    516   return val
    517 end
    518 local clamp_point
    519 clamp_point = function(top_left, point, bottom_right)
    520   return {
    521     x = clamp(top_left.x, point.x, bottom_right.x),
    522     y = clamp(top_left.y, point.y, bottom_right.y)
    523   }
    524 end
    525 local VideoPoint
    526 do
    527   local _class_0
    528   local _base_0 = {
    529     set_from_screen = function(self, sx, sy)
    530       local d = get_video_dimensions()
    531       local point = clamp_point(d.top_left, {
    532         x = sx,
    533         y = sy
    534       }, d.bottom_right)
    535       self.x = math.floor(d.ratios.w * (point.x - d.top_left.x) + 0.5)
    536       self.y = math.floor(d.ratios.h * (point.y - d.top_left.y) + 0.5)
    537     end,
    538     to_screen = function(self)
    539       local d = get_video_dimensions()
    540       return {
    541         x = math.floor(self.x / d.ratios.w + d.top_left.x + 0.5),
    542         y = math.floor(self.y / d.ratios.h + d.top_left.y + 0.5)
    543       }
    544     end
    545   }
    546   _base_0.__index = _base_0
    547   _class_0 = setmetatable({
    548     __init = function(self)
    549       self.x = -1
    550       self.y = -1
    551     end,
    552     __base = _base_0,
    553     __name = "VideoPoint"
    554   }, {
    555     __index = _base_0,
    556     __call = function(cls, ...)
    557       local _self_0 = setmetatable({}, _base_0)
    558       cls.__init(_self_0, ...)
    559       return _self_0
    560     end
    561   })
    562   _base_0.__class = _class_0
    563   VideoPoint = _class_0
    564 end
    565 local Region
    566 do
    567   local _class_0
    568   local _base_0 = {
    569     is_valid = function(self)
    570       return self.x > -1 and self.y > -1 and self.w > -1 and self.h > -1
    571     end,
    572     set_from_points = function(self, p1, p2)
    573       self.x = math.min(p1.x, p2.x)
    574       self.y = math.min(p1.y, p2.y)
    575       self.w = math.abs(p1.x - p2.x)
    576       self.h = math.abs(p1.y - p2.y)
    577     end
    578   }
    579   _base_0.__index = _base_0
    580   _class_0 = setmetatable({
    581     __init = function(self)
    582       self.x = -1
    583       self.y = -1
    584       self.w = -1
    585       self.h = -1
    586     end,
    587     __base = _base_0,
    588     __name = "Region"
    589   }, {
    590     __index = _base_0,
    591     __call = function(cls, ...)
    592       local _self_0 = setmetatable({}, _base_0)
    593       cls.__init(_self_0, ...)
    594       return _self_0
    595     end
    596   })
    597   _base_0.__class = _class_0
    598   Region = _class_0
    599 end
    600 local make_fullscreen_region
    601 make_fullscreen_region = function()
    602   local r = Region()
    603   local d = get_video_dimensions()
    604   local a = VideoPoint()
    605   local b = VideoPoint()
    606   local xa, ya
    607   do
    608     local _obj_0 = d.top_left
    609     xa, ya = _obj_0.x, _obj_0.y
    610   end
    611   a:set_from_screen(xa, ya)
    612   local xb, yb
    613   do
    614     local _obj_0 = d.bottom_right
    615     xb, yb = _obj_0.x, _obj_0.y
    616   end
    617   b:set_from_screen(xb, yb)
    618   r:set_from_points(a, b)
    619   return r
    620 end
    621 local read_double
    622 read_double = function(bytes)
    623   local sign = 1
    624   local mantissa = bytes[2] % 2 ^ 4
    625   for i = 3, 8 do
    626     mantissa = mantissa * 256 + bytes[i]
    627   end
    628   if bytes[1] > 127 then
    629     sign = -1
    630   end
    631   local exponent = (bytes[1] % 128) * 2 ^ 4 + math.floor(bytes[2] / 2 ^ 4)
    632   if exponent == 0 then
    633     return 0
    634   end
    635   mantissa = (math.ldexp(mantissa, -52) + 1) * sign
    636   return math.ldexp(mantissa, exponent - 1023)
    637 end
    638 local write_double
    639 write_double = function(num)
    640   local bytes = {
    641     0,
    642     0,
    643     0,
    644     0,
    645     0,
    646     0,
    647     0,
    648     0
    649   }
    650   if num == 0 then
    651     return bytes
    652   end
    653   local anum = math.abs(num)
    654   local mantissa, exponent = math.frexp(anum)
    655   exponent = exponent - 1
    656   mantissa = mantissa * 2 - 1
    657   local sign = num ~= anum and 128 or 0
    658   exponent = exponent + 1023
    659   bytes[1] = sign + math.floor(exponent / 2 ^ 4)
    660   mantissa = mantissa * 2 ^ 4
    661   local currentmantissa = math.floor(mantissa)
    662   mantissa = mantissa - currentmantissa
    663   bytes[2] = (exponent % 2 ^ 4) * 2 ^ 4 + currentmantissa
    664   for i = 3, 8 do
    665     mantissa = mantissa * 2 ^ 8
    666     currentmantissa = math.floor(mantissa)
    667     mantissa = mantissa - currentmantissa
    668     bytes[i] = currentmantissa
    669   end
    670   return bytes
    671 end
    672 local FirstpassStats
    673 do
    674   local _class_0
    675   local duration_multiplier, fields_before_duration, fields_after_duration
    676   local _base_0 = {
    677     get_duration = function(self)
    678       local big_endian_binary_duration = reverse(self.binary_duration)
    679       return read_double(reversed_binary_duration) / duration_multiplier
    680     end,
    681     set_duration = function(self, duration)
    682       local big_endian_binary_duration = write_double(duration * duration_multiplier)
    683       self.binary_duration = reverse(big_endian_binary_duration)
    684     end,
    685     _bytes_to_string = function(self, bytes)
    686       return string.char(unpack(bytes))
    687     end,
    688     as_binary_string = function(self)
    689       local before_duration_string = self:_bytes_to_string(self.binary_data_before_duration)
    690       local duration_string = self:_bytes_to_string(self.binary_duration)
    691       local after_duration_string = self:_bytes_to_string(self.binary_data_after_duration)
    692       return before_duration_string .. duration_string .. after_duration_string
    693     end
    694   }
    695   _base_0.__index = _base_0
    696   _class_0 = setmetatable({
    697     __init = function(self, before_duration, duration, after_duration)
    698       self.binary_data_before_duration = before_duration
    699       self.binary_duration = duration
    700       self.binary_data_after_duration = after_duration
    701     end,
    702     __base = _base_0,
    703     __name = "FirstpassStats"
    704   }, {
    705     __index = _base_0,
    706     __call = function(cls, ...)
    707       local _self_0 = setmetatable({}, _base_0)
    708       cls.__init(_self_0, ...)
    709       return _self_0
    710     end
    711   })
    712   _base_0.__class = _class_0
    713   local self = _class_0
    714   duration_multiplier = 10000000.0
    715   fields_before_duration = 16
    716   fields_after_duration = 1
    717   self.data_before_duration_size = function(self)
    718     return fields_before_duration * 8
    719   end
    720   self.data_after_duration_size = function(self)
    721     return fields_after_duration * 8
    722   end
    723   self.size = function(self)
    724     return (fields_before_duration + 1 + fields_after_duration) * 8
    725   end
    726   self.from_bytes = function(self, bytes)
    727     local before_duration
    728     do
    729       local _accum_0 = { }
    730       local _len_0 = 1
    731       local _max_0 = self:data_before_duration_size()
    732       for _index_0 = 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do
    733         local b = bytes[_index_0]
    734         _accum_0[_len_0] = b
    735         _len_0 = _len_0 + 1
    736       end
    737       before_duration = _accum_0
    738     end
    739     local duration
    740     do
    741       local _accum_0 = { }
    742       local _len_0 = 1
    743       local _max_0 = self:data_before_duration_size() + 8
    744       for _index_0 = self:data_before_duration_size() + 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do
    745         local b = bytes[_index_0]
    746         _accum_0[_len_0] = b
    747         _len_0 = _len_0 + 1
    748       end
    749       duration = _accum_0
    750     end
    751     local after_duration
    752     do
    753       local _accum_0 = { }
    754       local _len_0 = 1
    755       for _index_0 = self:data_before_duration_size() + 8 + 1, #bytes do
    756         local b = bytes[_index_0]
    757         _accum_0[_len_0] = b
    758         _len_0 = _len_0 + 1
    759       end
    760       after_duration = _accum_0
    761     end
    762     return self(before_duration, duration, after_duration)
    763   end
    764   FirstpassStats = _class_0
    765 end
    766 local read_logfile_into_stats_array
    767 read_logfile_into_stats_array = function(logfile_path)
    768   local file = assert(io.open(logfile_path, "rb"))
    769   local logfile_string = base64_decode(file:read())
    770   file:close()
    771   local stats_size = FirstpassStats:size()
    772   assert(logfile_string:len() % stats_size == 0)
    773   local stats = { }
    774   for offset = 1, #logfile_string, stats_size do
    775     local bytes = {
    776       logfile_string:byte(offset, offset + stats_size - 1)
    777     }
    778     assert(#bytes == stats_size)
    779     stats[#stats + 1] = FirstpassStats:from_bytes(bytes)
    780   end
    781   return stats
    782 end
    783 local write_stats_array_to_logfile
    784 write_stats_array_to_logfile = function(stats_array, logfile_path)
    785   local file = assert(io.open(logfile_path, "wb"))
    786   local logfile_string = ""
    787   for _index_0 = 1, #stats_array do
    788     local stat = stats_array[_index_0]
    789     logfile_string = logfile_string .. stat:as_binary_string()
    790   end
    791   file:write(base64_encode(logfile_string))
    792   return file:close()
    793 end
    794 local vp8_patch_logfile
    795 vp8_patch_logfile = function(logfile_path, encode_total_duration)
    796   local stats_array = read_logfile_into_stats_array(logfile_path)
    797   local average_duration = encode_total_duration / (#stats_array - 1)
    798   for i = 1, #stats_array - 1 do
    799     stats_array[i]:set_duration(average_duration)
    800   end
    801   stats_array[#stats_array]:set_duration(encode_total_duration)
    802   return write_stats_array_to_logfile(stats_array, logfile_path)
    803 end
    804 local formats = { }
    805 local Format
    806 do
    807   local _class_0
    808   local _base_0 = {
    809     getPreFilters = function(self)
    810       return { }
    811     end,
    812     getPostFilters = function(self)
    813       return { }
    814     end,
    815     getFlags = function(self)
    816       return { }
    817     end,
    818     getCodecFlags = function(self)
    819       local codecs = { }
    820       if self.videoCodec ~= "" then
    821         codecs[#codecs + 1] = "--ovc=" .. tostring(self.videoCodec)
    822       end
    823       if self.audioCodec ~= "" then
    824         codecs[#codecs + 1] = "--oac=" .. tostring(self.audioCodec)
    825       end
    826       return codecs
    827     end,
    828     postCommandModifier = function(self, command, region, startTime, endTime)
    829       return command
    830     end
    831   }
    832   _base_0.__index = _base_0
    833   _class_0 = setmetatable({
    834     __init = function(self)
    835       self.displayName = "Basic"
    836       self.supportsTwopass = true
    837       self.videoCodec = ""
    838       self.audioCodec = ""
    839       self.outputExtension = ""
    840       self.acceptsBitrate = true
    841     end,
    842     __base = _base_0,
    843     __name = "Format"
    844   }, {
    845     __index = _base_0,
    846     __call = function(cls, ...)
    847       local _self_0 = setmetatable({}, _base_0)
    848       cls.__init(_self_0, ...)
    849       return _self_0
    850     end
    851   })
    852   _base_0.__class = _class_0
    853   Format = _class_0
    854 end
    855 local RawVideo
    856 do
    857   local _class_0
    858   local _parent_0 = Format
    859   local _base_0 = {
    860     getColorspace = function(self)
    861       local csp = mp.get_property("colormatrix")
    862       local _exp_0 = csp
    863       if "bt.601" == _exp_0 then
    864         return "bt601"
    865       elseif "bt.709" == _exp_0 then
    866         return "bt709"
    867       elseif "bt.2020" == _exp_0 then
    868         return "bt2020"
    869       elseif "smpte-240m" == _exp_0 then
    870         return "smpte240m"
    871       else
    872         msg.info("Warning, unknown colorspace " .. tostring(csp) .. " detected, using bt.601.")
    873         return "bt601"
    874       end
    875     end,
    876     getPostFilters = function(self)
    877       return {
    878         "format=yuv444p16",
    879         "lavfi-scale=in_color_matrix=" .. self:getColorspace(),
    880         "format=bgr24"
    881       }
    882     end
    883   }
    884   _base_0.__index = _base_0
    885   setmetatable(_base_0, _parent_0.__base)
    886   _class_0 = setmetatable({
    887     __init = function(self)
    888       self.displayName = "Raw"
    889       self.supportsTwopass = false
    890       self.videoCodec = "rawvideo"
    891       self.audioCodec = "pcm_s16le"
    892       self.outputExtension = "avi"
    893       self.acceptsBitrate = false
    894     end,
    895     __base = _base_0,
    896     __name = "RawVideo",
    897     __parent = _parent_0
    898   }, {
    899     __index = function(cls, name)
    900       local val = rawget(_base_0, name)
    901       if val == nil then
    902         local parent = rawget(cls, "__parent")
    903         if parent then
    904           return parent[name]
    905         end
    906       else
    907         return val
    908       end
    909     end,
    910     __call = function(cls, ...)
    911       local _self_0 = setmetatable({}, _base_0)
    912       cls.__init(_self_0, ...)
    913       return _self_0
    914     end
    915   })
    916   _base_0.__class = _class_0
    917   if _parent_0.__inherited then
    918     _parent_0.__inherited(_parent_0, _class_0)
    919   end
    920   RawVideo = _class_0
    921 end
    922 formats["raw"] = RawVideo()
    923 local WebmVP8
    924 do
    925   local _class_0
    926   local _parent_0 = Format
    927   local _base_0 = {
    928     getPreFilters = function(self)
    929       local colormatrixFilter = {
    930         ["bt.709"] = "bt709",
    931         ["bt.2020"] = "bt2020",
    932         ["smpte-240m"] = "smpte240m"
    933       }
    934       local ret = { }
    935       local colormatrix = mp.get_property_native("video-params/colormatrix")
    936       if colormatrixFilter[colormatrix] then
    937         append(ret, {
    938           "lavfi-colormatrix=" .. tostring(colormatrixFilter[colormatrix]) .. ":bt601"
    939         })
    940       end
    941       return ret
    942     end,
    943     getFlags = function(self)
    944       return {
    945         "--ovcopts-add=threads=" .. tostring(options.threads),
    946         "--ovcopts-add=auto-alt-ref=1",
    947         "--ovcopts-add=lag-in-frames=25",
    948         "--ovcopts-add=quality=good",
    949         "--ovcopts-add=cpu-used=0"
    950       }
    951     end
    952   }
    953   _base_0.__index = _base_0
    954   setmetatable(_base_0, _parent_0.__base)
    955   _class_0 = setmetatable({
    956     __init = function(self)
    957       self.displayName = "WebM"
    958       self.supportsTwopass = true
    959       self.videoCodec = "libvpx"
    960       self.audioCodec = "libvorbis"
    961       self.outputExtension = "webm"
    962       self.acceptsBitrate = true
    963     end,
    964     __base = _base_0,
    965     __name = "WebmVP8",
    966     __parent = _parent_0
    967   }, {
    968     __index = function(cls, name)
    969       local val = rawget(_base_0, name)
    970       if val == nil then
    971         local parent = rawget(cls, "__parent")
    972         if parent then
    973           return parent[name]
    974         end
    975       else
    976         return val
    977       end
    978     end,
    979     __call = function(cls, ...)
    980       local _self_0 = setmetatable({}, _base_0)
    981       cls.__init(_self_0, ...)
    982       return _self_0
    983     end
    984   })
    985   _base_0.__class = _class_0
    986   if _parent_0.__inherited then
    987     _parent_0.__inherited(_parent_0, _class_0)
    988   end
    989   WebmVP8 = _class_0
    990 end
    991 formats["webm-vp8"] = WebmVP8()
    992 local WebmVP9
    993 do
    994   local _class_0
    995   local _parent_0 = Format
    996   local _base_0 = {
    997     getFlags = function(self)
    998       return {
    999         "--ovcopts-add=threads=" .. tostring(options.threads)
   1000       }
   1001     end
   1002   }
   1003   _base_0.__index = _base_0
   1004   setmetatable(_base_0, _parent_0.__base)
   1005   _class_0 = setmetatable({
   1006     __init = function(self)
   1007       self.displayName = "WebM (VP9)"
   1008       self.supportsTwopass = false
   1009       self.videoCodec = "libvpx-vp9"
   1010       self.audioCodec = "libopus"
   1011       self.outputExtension = "webm"
   1012       self.acceptsBitrate = true
   1013     end,
   1014     __base = _base_0,
   1015     __name = "WebmVP9",
   1016     __parent = _parent_0
   1017   }, {
   1018     __index = function(cls, name)
   1019       local val = rawget(_base_0, name)
   1020       if val == nil then
   1021         local parent = rawget(cls, "__parent")
   1022         if parent then
   1023           return parent[name]
   1024         end
   1025       else
   1026         return val
   1027       end
   1028     end,
   1029     __call = function(cls, ...)
   1030       local _self_0 = setmetatable({}, _base_0)
   1031       cls.__init(_self_0, ...)
   1032       return _self_0
   1033     end
   1034   })
   1035   _base_0.__class = _class_0
   1036   if _parent_0.__inherited then
   1037     _parent_0.__inherited(_parent_0, _class_0)
   1038   end
   1039   WebmVP9 = _class_0
   1040 end
   1041 formats["webm-vp9"] = WebmVP9()
   1042 local AVC
   1043 do
   1044   local _class_0
   1045   local _parent_0 = Format
   1046   local _base_0 = {
   1047     getFlags = function(self)
   1048       return {
   1049         "--ovcopts-add=threads=" .. tostring(options.threads)
   1050       }
   1051     end
   1052   }
   1053   _base_0.__index = _base_0
   1054   setmetatable(_base_0, _parent_0.__base)
   1055   _class_0 = setmetatable({
   1056     __init = function(self)
   1057       self.displayName = "AVC (h264/AAC)"
   1058       self.supportsTwopass = true
   1059       self.videoCodec = "libx264"
   1060       self.audioCodec = "aac"
   1061       self.outputExtension = "mp4"
   1062       self.acceptsBitrate = true
   1063     end,
   1064     __base = _base_0,
   1065     __name = "AVC",
   1066     __parent = _parent_0
   1067   }, {
   1068     __index = function(cls, name)
   1069       local val = rawget(_base_0, name)
   1070       if val == nil then
   1071         local parent = rawget(cls, "__parent")
   1072         if parent then
   1073           return parent[name]
   1074         end
   1075       else
   1076         return val
   1077       end
   1078     end,
   1079     __call = function(cls, ...)
   1080       local _self_0 = setmetatable({}, _base_0)
   1081       cls.__init(_self_0, ...)
   1082       return _self_0
   1083     end
   1084   })
   1085   _base_0.__class = _class_0
   1086   if _parent_0.__inherited then
   1087     _parent_0.__inherited(_parent_0, _class_0)
   1088   end
   1089   AVC = _class_0
   1090 end
   1091 formats["avc"] = AVC()
   1092 local AVCNVENC
   1093 do
   1094   local _class_0
   1095   local _parent_0 = Format
   1096   local _base_0 = {
   1097     getFlags = function(self)
   1098       return {
   1099         "--ovcopts-add=bf=0"
   1100       }
   1101     end
   1102   }
   1103   _base_0.__index = _base_0
   1104   setmetatable(_base_0, _parent_0.__base)
   1105   _class_0 = setmetatable({
   1106     __init = function(self)
   1107       self.displayName = "AVC (h264-NVENC/AAC)"
   1108       self.supportsTwopass = true
   1109       self.videoCodec = "h264_nvenc"
   1110       self.audioCodec = "aac"
   1111       self.outputExtension = "mp4"
   1112       self.acceptsBitrate = true
   1113     end,
   1114     __base = _base_0,
   1115     __name = "AVCNVENC",
   1116     __parent = _parent_0
   1117   }, {
   1118     __index = function(cls, name)
   1119       local val = rawget(_base_0, name)
   1120       if val == nil then
   1121         local parent = rawget(cls, "__parent")
   1122         if parent then
   1123           return parent[name]
   1124         end
   1125       else
   1126         return val
   1127       end
   1128     end,
   1129     __call = function(cls, ...)
   1130       local _self_0 = setmetatable({}, _base_0)
   1131       cls.__init(_self_0, ...)
   1132       return _self_0
   1133     end
   1134   })
   1135   _base_0.__class = _class_0
   1136   if _parent_0.__inherited then
   1137     _parent_0.__inherited(_parent_0, _class_0)
   1138   end
   1139   AVCNVENC = _class_0
   1140 end
   1141 formats["avc-nvenc"] = AVCNVENC()
   1142 local AV1
   1143 do
   1144   local _class_0
   1145   local _parent_0 = Format
   1146   local _base_0 = {
   1147     getFlags = function(self)
   1148       return {
   1149         "--ovcopts-add=threads=" .. tostring(options.threads)
   1150       }
   1151     end
   1152   }
   1153   _base_0.__index = _base_0
   1154   setmetatable(_base_0, _parent_0.__base)
   1155   _class_0 = setmetatable({
   1156     __init = function(self)
   1157       self.displayName = "AV1"
   1158       self.supportsTwopass = true
   1159       self.videoCodec = "libaom-av1"
   1160       self.audioCodec = "aac"
   1161       self.outputExtension = "mp4"
   1162       self.acceptsBitrate = true
   1163     end,
   1164     __base = _base_0,
   1165     __name = "AV1",
   1166     __parent = _parent_0
   1167   }, {
   1168     __index = function(cls, name)
   1169       local val = rawget(_base_0, name)
   1170       if val == nil then
   1171         local parent = rawget(cls, "__parent")
   1172         if parent then
   1173           return parent[name]
   1174         end
   1175       else
   1176         return val
   1177       end
   1178     end,
   1179     __call = function(cls, ...)
   1180       local _self_0 = setmetatable({}, _base_0)
   1181       cls.__init(_self_0, ...)
   1182       return _self_0
   1183     end
   1184   })
   1185   _base_0.__class = _class_0
   1186   if _parent_0.__inherited then
   1187     _parent_0.__inherited(_parent_0, _class_0)
   1188   end
   1189   AV1 = _class_0
   1190 end
   1191 formats["av1"] = AV1()
   1192 local HEVC
   1193 do
   1194   local _class_0
   1195   local _parent_0 = Format
   1196   local _base_0 = {
   1197     getFlags = function(self)
   1198       return {
   1199         "--ovcopts-add=threads=" .. tostring(options.threads)
   1200       }
   1201     end
   1202   }
   1203   _base_0.__index = _base_0
   1204   setmetatable(_base_0, _parent_0.__base)
   1205   _class_0 = setmetatable({
   1206     __init = function(self)
   1207       self.displayName = "HEVC"
   1208       self.supportsTwopass = true
   1209       self.videoCodec = "libx265"
   1210       self.audioCodec = "aac"
   1211       self.outputExtension = "mp4"
   1212       self.acceptsBitrate = true
   1213     end,
   1214     __base = _base_0,
   1215     __name = "HEVC",
   1216     __parent = _parent_0
   1217   }, {
   1218     __index = function(cls, name)
   1219       local val = rawget(_base_0, name)
   1220       if val == nil then
   1221         local parent = rawget(cls, "__parent")
   1222         if parent then
   1223           return parent[name]
   1224         end
   1225       else
   1226         return val
   1227       end
   1228     end,
   1229     __call = function(cls, ...)
   1230       local _self_0 = setmetatable({}, _base_0)
   1231       cls.__init(_self_0, ...)
   1232       return _self_0
   1233     end
   1234   })
   1235   _base_0.__class = _class_0
   1236   if _parent_0.__inherited then
   1237     _parent_0.__inherited(_parent_0, _class_0)
   1238   end
   1239   HEVC = _class_0
   1240 end
   1241 formats["hevc"] = HEVC()
   1242 local MP3
   1243 do
   1244   local _class_0
   1245   local _parent_0 = Format
   1246   local _base_0 = { }
   1247   _base_0.__index = _base_0
   1248   setmetatable(_base_0, _parent_0.__base)
   1249   _class_0 = setmetatable({
   1250     __init = function(self)
   1251       self.displayName = "MP3 (libmp3lame)"
   1252       self.supportsTwopass = false
   1253       self.videoCodec = ""
   1254       self.audioCodec = "libmp3lame"
   1255       self.outputExtension = "mp3"
   1256       self.acceptsBitrate = true
   1257     end,
   1258     __base = _base_0,
   1259     __name = "MP3",
   1260     __parent = _parent_0
   1261   }, {
   1262     __index = function(cls, name)
   1263       local val = rawget(_base_0, name)
   1264       if val == nil then
   1265         local parent = rawget(cls, "__parent")
   1266         if parent then
   1267           return parent[name]
   1268         end
   1269       else
   1270         return val
   1271       end
   1272     end,
   1273     __call = function(cls, ...)
   1274       local _self_0 = setmetatable({}, _base_0)
   1275       cls.__init(_self_0, ...)
   1276       return _self_0
   1277     end
   1278   })
   1279   _base_0.__class = _class_0
   1280   if _parent_0.__inherited then
   1281     _parent_0.__inherited(_parent_0, _class_0)
   1282   end
   1283   MP3 = _class_0
   1284 end
   1285 formats["mp3"] = MP3()
   1286 local GIF
   1287 do
   1288   local _class_0
   1289   local _parent_0 = Format
   1290   local _base_0 = {
   1291     postCommandModifier = function(self, command, region, startTime, endTime)
   1292       local new_command = { }
   1293       local start_ts = seconds_to_time_string(startTime, false, true)
   1294       local end_ts = seconds_to_time_string(endTime, false, true)
   1295       start_ts = start_ts:gsub(":", "\\\\:")
   1296       end_ts = end_ts:gsub(":", "\\\\:")
   1297       local cfilter = "[vid1]trim=start=" .. tostring(start_ts) .. ":end=" .. tostring(end_ts) .. "[vidtmp];"
   1298       if mp.get_property("deinterlace") == "yes" then
   1299         cfilter = cfilter .. "[vidtmp]yadif=mode=1[vidtmp];"
   1300       end
   1301       for _, v in ipairs(command) do
   1302         local _continue_0 = false
   1303         repeat
   1304           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
   1305             local n = v:gsub("^%-%-vf%-add=", ""):gsub("^lavfi%-", "")
   1306             cfilter = cfilter .. "[vidtmp]" .. tostring(n) .. "[vidtmp];"
   1307           else
   1308             if v:match("^%-%-video%-rotate=90") then
   1309               cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];"
   1310             else
   1311               if v:match("^%-%-video%-rotate=270") then
   1312                 cfilter = cfilter .. "[vidtmp]transpose=2[vidtmp];"
   1313               else
   1314                 if v:match("^%-%-video%-rotate=180") then
   1315                   cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];[vidtmp]transpose=1[vidtmp];"
   1316                 else
   1317                   if v:match("^%-%-deinterlace=") then
   1318                     _continue_0 = true
   1319                     break
   1320                   else
   1321                     append(new_command, {
   1322                       v
   1323                     })
   1324                     _continue_0 = true
   1325                     break
   1326                   end
   1327                 end
   1328               end
   1329             end
   1330           end
   1331           _continue_0 = true
   1332         until true
   1333         if not _continue_0 then
   1334           break
   1335         end
   1336       end
   1337       cfilter = cfilter .. "[vidtmp]split[topal][vidf];"
   1338       cfilter = cfilter .. "[topal]palettegen[pal];"
   1339       cfilter = cfilter .. "[vidf][pal]paletteuse=diff_mode=rectangle"
   1340       if options.gif_dither ~= 6 then
   1341         cfilter = cfilter .. ":dither=bayer:bayer_scale=" .. tostring(options.gif_dither)
   1342       end
   1343       cfilter = cfilter .. "[vo]"
   1344       append(new_command, {
   1345         "--lavfi-complex=" .. tostring(cfilter)
   1346       })
   1347       return new_command
   1348     end
   1349   }
   1350   _base_0.__index = _base_0
   1351   setmetatable(_base_0, _parent_0.__base)
   1352   _class_0 = setmetatable({
   1353     __init = function(self)
   1354       self.displayName = "GIF"
   1355       self.supportsTwopass = false
   1356       self.videoCodec = "gif"
   1357       self.audioCodec = ""
   1358       self.outputExtension = "gif"
   1359       self.acceptsBitrate = false
   1360     end,
   1361     __base = _base_0,
   1362     __name = "GIF",
   1363     __parent = _parent_0
   1364   }, {
   1365     __index = function(cls, name)
   1366       local val = rawget(_base_0, name)
   1367       if val == nil then
   1368         local parent = rawget(cls, "__parent")
   1369         if parent then
   1370           return parent[name]
   1371         end
   1372       else
   1373         return val
   1374       end
   1375     end,
   1376     __call = function(cls, ...)
   1377       local _self_0 = setmetatable({}, _base_0)
   1378       cls.__init(_self_0, ...)
   1379       return _self_0
   1380     end
   1381   })
   1382   _base_0.__class = _class_0
   1383   if _parent_0.__inherited then
   1384     _parent_0.__inherited(_parent_0, _class_0)
   1385   end
   1386   GIF = _class_0
   1387 end
   1388 formats["gif"] = GIF()
   1389 local Page
   1390 do
   1391   local _class_0
   1392   local _base_0 = {
   1393     add_keybinds = function(self)
   1394       if not self.keybinds then
   1395         return 
   1396       end
   1397       for key, func in pairs(self.keybinds) do
   1398         mp.add_forced_key_binding(key, key, func, {
   1399           repeatable = true
   1400         })
   1401       end
   1402     end,
   1403     remove_keybinds = function(self)
   1404       if not self.keybinds then
   1405         return 
   1406       end
   1407       for key, _ in pairs(self.keybinds) do
   1408         mp.remove_key_binding(key)
   1409       end
   1410     end,
   1411     observe_properties = function(self)
   1412       self.sizeCallback = function()
   1413         return self:draw()
   1414       end
   1415       local properties = {
   1416         "keepaspect",
   1417         "video-out-params",
   1418         "video-unscaled",
   1419         "panscan",
   1420         "video-zoom",
   1421         "video-align-x",
   1422         "video-pan-x",
   1423         "video-align-y",
   1424         "video-pan-y",
   1425         "osd-width",
   1426         "osd-height"
   1427       }
   1428       for _index_0 = 1, #properties do
   1429         local p = properties[_index_0]
   1430         mp.observe_property(p, "native", self.sizeCallback)
   1431       end
   1432     end,
   1433     unobserve_properties = function(self)
   1434       if self.sizeCallback then
   1435         mp.unobserve_property(self.sizeCallback)
   1436         self.sizeCallback = nil
   1437       end
   1438     end,
   1439     clear = function(self)
   1440       local window_w, window_h = mp.get_osd_size()
   1441       mp.set_osd_ass(window_w, window_h, "")
   1442       return mp.osd_message("", 0)
   1443     end,
   1444     prepare = function(self)
   1445       return nil
   1446     end,
   1447     dispose = function(self)
   1448       return nil
   1449     end,
   1450     show = function(self)
   1451       if self.visible then
   1452         return 
   1453       end
   1454       self.visible = true
   1455       self:observe_properties()
   1456       self:add_keybinds()
   1457       self:prepare()
   1458       self:clear()
   1459       return self:draw()
   1460     end,
   1461     hide = function(self)
   1462       if not self.visible then
   1463         return 
   1464       end
   1465       self.visible = false
   1466       self:unobserve_properties()
   1467       self:remove_keybinds()
   1468       self:clear()
   1469       return self:dispose()
   1470     end,
   1471     setup_text = function(self, ass)
   1472       local scale = calculate_scale_factor()
   1473       local margin = options.margin * scale
   1474       ass:append("{\\an7}")
   1475       ass:pos(margin, margin)
   1476       return ass:append("{\\fs" .. tostring(options.font_size * scale) .. "}")
   1477     end
   1478   }
   1479   _base_0.__index = _base_0
   1480   _class_0 = setmetatable({
   1481     __init = function() end,
   1482     __base = _base_0,
   1483     __name = "Page"
   1484   }, {
   1485     __index = _base_0,
   1486     __call = function(cls, ...)
   1487       local _self_0 = setmetatable({}, _base_0)
   1488       cls.__init(_self_0, ...)
   1489       return _self_0
   1490     end
   1491   })
   1492   _base_0.__class = _class_0
   1493   Page = _class_0
   1494 end
   1495 local EncodeWithProgress
   1496 do
   1497   local _class_0
   1498   local _parent_0 = Page
   1499   local _base_0 = {
   1500     draw = function(self)
   1501       local progress = 100 * ((self.currentTime - self.startTime) / self.duration)
   1502       local progressText = string.format("%d%%", progress)
   1503       local window_w, window_h = mp.get_osd_size()
   1504       local ass = assdraw.ass_new()
   1505       ass:new_event()
   1506       self:setup_text(ass)
   1507       ass:append("Encoding (" .. tostring(bold(progressText)) .. ")\\N")
   1508       return mp.set_osd_ass(window_w, window_h, ass.text)
   1509     end,
   1510     parseLine = function(self, line)
   1511       local matchTime = string.match(line, "Encode time[-]pos: ([0-9.]+)")
   1512       local matchExit = string.match(line, "Exiting... [(]([%a ]+)[)]")
   1513       if matchTime == nil and matchExit == nil then
   1514         return 
   1515       end
   1516       if matchTime ~= nil and tonumber(matchTime) > self.currentTime then
   1517         self.currentTime = tonumber(matchTime)
   1518       end
   1519       if matchExit ~= nil then
   1520         self.finished = true
   1521         self.finishedReason = matchExit
   1522       end
   1523     end,
   1524     startEncode = function(self, command_line)
   1525       local copy_command_line
   1526       do
   1527         local _accum_0 = { }
   1528         local _len_0 = 1
   1529         for _index_0 = 1, #command_line do
   1530           local arg = command_line[_index_0]
   1531           _accum_0[_len_0] = arg
   1532           _len_0 = _len_0 + 1
   1533         end
   1534         copy_command_line = _accum_0
   1535       end
   1536       append(copy_command_line, {
   1537         '--term-status-msg=Encode time-pos: ${=time-pos}\\n'
   1538       })
   1539       self:show()
   1540       local processFd = run_subprocess_popen(copy_command_line)
   1541       for line in processFd:lines() do
   1542         msg.verbose(string.format('%q', line))
   1543         self:parseLine(line)
   1544         self:draw()
   1545       end
   1546       processFd:close()
   1547       self:hide()
   1548       if self.finishedReason == "End of file" then
   1549         return true
   1550       end
   1551       return false
   1552     end
   1553   }
   1554   _base_0.__index = _base_0
   1555   setmetatable(_base_0, _parent_0.__base)
   1556   _class_0 = setmetatable({
   1557     __init = function(self, startTime, endTime)
   1558       self.startTime = startTime
   1559       self.endTime = endTime
   1560       self.duration = endTime - startTime
   1561       self.currentTime = startTime
   1562     end,
   1563     __base = _base_0,
   1564     __name = "EncodeWithProgress",
   1565     __parent = _parent_0
   1566   }, {
   1567     __index = function(cls, name)
   1568       local val = rawget(_base_0, name)
   1569       if val == nil then
   1570         local parent = rawget(cls, "__parent")
   1571         if parent then
   1572           return parent[name]
   1573         end
   1574       else
   1575         return val
   1576       end
   1577     end,
   1578     __call = function(cls, ...)
   1579       local _self_0 = setmetatable({}, _base_0)
   1580       cls.__init(_self_0, ...)
   1581       return _self_0
   1582     end
   1583   })
   1584   _base_0.__class = _class_0
   1585   if _parent_0.__inherited then
   1586     _parent_0.__inherited(_parent_0, _class_0)
   1587   end
   1588   EncodeWithProgress = _class_0
   1589 end
   1590 local get_active_tracks
   1591 get_active_tracks = function()
   1592   local accepted = {
   1593     video = true,
   1594     audio = not mp.get_property_bool("mute"),
   1595     sub = mp.get_property_bool("sub-visibility")
   1596   }
   1597   local active = {
   1598     video = { },
   1599     audio = { },
   1600     sub = { }
   1601   }
   1602   for _, track in ipairs(mp.get_property_native("track-list")) do
   1603     if track["selected"] and accepted[track["type"]] then
   1604       local count = #active[track["type"]]
   1605       active[track["type"]][count + 1] = track
   1606     end
   1607   end
   1608   return active
   1609 end
   1610 local filter_tracks_supported_by_format
   1611 filter_tracks_supported_by_format = function(active_tracks, format)
   1612   local has_video_codec = format.videoCodec ~= ""
   1613   local has_audio_codec = format.audioCodec ~= ""
   1614   local supported = {
   1615     video = has_video_codec and active_tracks["video"] or { },
   1616     audio = has_audio_codec and active_tracks["audio"] or { },
   1617     sub = has_video_codec and active_tracks["sub"] or { }
   1618   }
   1619   return supported
   1620 end
   1621 local append_track
   1622 append_track = function(out, track)
   1623   local external_flag = {
   1624     ["audio"] = "audio-file",
   1625     ["sub"] = "sub-file"
   1626   }
   1627   local internal_flag = {
   1628     ["video"] = "vid",
   1629     ["audio"] = "aid",
   1630     ["sub"] = "sid"
   1631   }
   1632   if track['external'] and string.len(track['external-filename']) <= 2048 then
   1633     return append(out, {
   1634       "--" .. tostring(external_flag[track['type']]) .. "=" .. tostring(track['external-filename'])
   1635     })
   1636   else
   1637     return append(out, {
   1638       "--" .. tostring(internal_flag[track['type']]) .. "=" .. tostring(track['id'])
   1639     })
   1640   end
   1641 end
   1642 local append_audio_tracks
   1643 append_audio_tracks = function(out, tracks)
   1644   local internal_tracks = { }
   1645   for _index_0 = 1, #tracks do
   1646     local track = tracks[_index_0]
   1647     if track['external'] then
   1648       append_track(out, track)
   1649     else
   1650       append(internal_tracks, {
   1651         track
   1652       })
   1653     end
   1654   end
   1655   if #internal_tracks > 1 then
   1656     local filter_string = ""
   1657     for _index_0 = 1, #internal_tracks do
   1658       local track = internal_tracks[_index_0]
   1659       filter_string = filter_string .. "[aid" .. tostring(track['id']) .. "]"
   1660     end
   1661     filter_string = filter_string .. "amix[ao]"
   1662     return append(out, {
   1663       "--lavfi-complex=" .. tostring(filter_string)
   1664     })
   1665   else
   1666     if #internal_tracks == 1 then
   1667       return append_track(out, internal_tracks[1])
   1668     end
   1669   end
   1670 end
   1671 local get_scale_filters
   1672 get_scale_filters = function()
   1673   local filters = { }
   1674   if options.force_square_pixels then
   1675     append(filters, {
   1676       "lavfi-scale=iw*sar:ih"
   1677     })
   1678   end
   1679   if options.scale_height > 0 then
   1680     append(filters, {
   1681       "lavfi-scale=-2:" .. tostring(options.scale_height)
   1682     })
   1683   end
   1684   return filters
   1685 end
   1686 local get_fps_filters
   1687 get_fps_filters = function()
   1688   if options.fps > 0 then
   1689     return {
   1690       "fps=" .. tostring(options.fps)
   1691     }
   1692   end
   1693   return { }
   1694 end
   1695 local get_contrast_brightness_and_saturation_filters
   1696 get_contrast_brightness_and_saturation_filters = function()
   1697   local mpv_brightness = mp.get_property("brightness")
   1698   local mpv_contrast = mp.get_property("contrast")
   1699   local mpv_saturation = mp.get_property("saturation")
   1700   if mpv_brightness == 0 and mpv_contrast == 0 and mpv_saturation == 0 then
   1701     return { }
   1702   end
   1703   local eq_saturation = (mpv_saturation + 100) / 100.0
   1704   local eq_contrast = (mpv_contrast + 100) / 100.0
   1705   local eq_brightness = (mpv_brightness / 50.0 + eq_contrast - 1) / 2.0
   1706   return {
   1707     "lavfi-eq=contrast=" .. tostring(eq_contrast) .. ":saturation=" .. tostring(eq_saturation) .. ":brightness=" .. tostring(eq_brightness)
   1708   }
   1709 end
   1710 local append_property
   1711 append_property = function(out, property_name, option_name)
   1712   option_name = option_name or property_name
   1713   local prop = mp.get_property(property_name)
   1714   if prop and prop ~= "" then
   1715     return append(out, {
   1716       "--" .. tostring(option_name) .. "=" .. tostring(prop)
   1717     })
   1718   end
   1719 end
   1720 local append_list_options
   1721 append_list_options = function(out, property_name, option_prefix)
   1722   option_prefix = option_prefix or property_name
   1723   local prop = mp.get_property_native(property_name)
   1724   if prop then
   1725     for _index_0 = 1, #prop do
   1726       local value = prop[_index_0]
   1727       append(out, {
   1728         "--" .. tostring(option_prefix) .. "-append=" .. tostring(value)
   1729       })
   1730     end
   1731   end
   1732 end
   1733 local get_playback_options
   1734 get_playback_options = function()
   1735   local ret = { }
   1736   append_property(ret, "sub-ass-override")
   1737   append_property(ret, "sub-ass-force-style")
   1738   append_property(ret, "sub-ass-vsfilter-aspect-compat")
   1739   append_property(ret, "sub-auto")
   1740   append_property(ret, "sub-pos")
   1741   append_property(ret, "sub-delay")
   1742   append_property(ret, "video-rotate")
   1743   append_property(ret, "ytdl-format")
   1744   append_property(ret, "deinterlace")
   1745   return ret
   1746 end
   1747 local get_speed_flags
   1748 get_speed_flags = function()
   1749   local ret = { }
   1750   local speed = mp.get_property_native("speed")
   1751   if speed ~= 1 then
   1752     append(ret, {
   1753       "--vf-add=setpts=PTS/" .. tostring(speed),
   1754       "--af-add=atempo=" .. tostring(speed),
   1755       "--sub-speed=1/" .. tostring(speed)
   1756     })
   1757   end
   1758   return ret
   1759 end
   1760 local get_metadata_flags
   1761 get_metadata_flags = function()
   1762   local title = mp.get_property("filename/no-ext")
   1763   return {
   1764     "--oset-metadata=title=%" .. tostring(string.len(title)) .. "%" .. tostring(title)
   1765   }
   1766 end
   1767 local apply_current_filters
   1768 apply_current_filters = function(filters)
   1769   local vf = mp.get_property_native("vf")
   1770   msg.verbose("apply_current_filters: got " .. tostring(#vf) .. " currently applied.")
   1771   for _index_0 = 1, #vf do
   1772     local _continue_0 = false
   1773     repeat
   1774       local filter = vf[_index_0]
   1775       msg.verbose("apply_current_filters: filter name: " .. tostring(filter['name']))
   1776       if filter["enabled"] == false then
   1777         _continue_0 = true
   1778         break
   1779       end
   1780       local str = filter["name"]
   1781       local params = filter["params"] or { }
   1782       for k, v in pairs(params) do
   1783         str = str .. ":" .. tostring(k) .. "=%" .. tostring(string.len(v)) .. "%" .. tostring(v)
   1784       end
   1785       append(filters, {
   1786         str
   1787       })
   1788       _continue_0 = true
   1789     until true
   1790     if not _continue_0 then
   1791       break
   1792     end
   1793   end
   1794 end
   1795 local get_video_filters
   1796 get_video_filters = function(format, region)
   1797   local filters = { }
   1798   append(filters, format:getPreFilters())
   1799   if options.apply_current_filters then
   1800     apply_current_filters(filters)
   1801   end
   1802   if region and region:is_valid() then
   1803     append(filters, {
   1804       "lavfi-crop=" .. tostring(region.w) .. ":" .. tostring(region.h) .. ":" .. tostring(region.x) .. ":" .. tostring(region.y)
   1805     })
   1806   end
   1807   append(filters, get_scale_filters())
   1808   append(filters, get_fps_filters())
   1809   append(filters, get_contrast_brightness_and_saturation_filters())
   1810   append(filters, format:getPostFilters())
   1811   return filters
   1812 end
   1813 local get_video_encode_flags
   1814 get_video_encode_flags = function(format, region)
   1815   local flags = { }
   1816   append(flags, get_playback_options())
   1817   local filters = get_video_filters(format, region)
   1818   for _index_0 = 1, #filters do
   1819     local f = filters[_index_0]
   1820     append(flags, {
   1821       "--vf-add=" .. tostring(f)
   1822     })
   1823   end
   1824   append(flags, get_speed_flags())
   1825   return flags
   1826 end
   1827 local calculate_bitrate
   1828 calculate_bitrate = function(active_tracks, format, length)
   1829   if format.videoCodec == "" then
   1830     return nil, options.target_filesize * 8 / length
   1831   end
   1832   local video_kilobits = options.target_filesize * 8
   1833   local audio_kilobits = nil
   1834   local has_audio_track = #active_tracks["audio"] > 0
   1835   if options.strict_filesize_constraint and has_audio_track then
   1836     audio_kilobits = length * options.strict_audio_bitrate
   1837     video_kilobits = video_kilobits - audio_kilobits
   1838   end
   1839   local video_bitrate = math.floor(video_kilobits / length)
   1840   local audio_bitrate = audio_kilobits and math.floor(audio_kilobits / length) or nil
   1841   return video_bitrate, audio_bitrate
   1842 end
   1843 local find_path
   1844 find_path = function(startTime, endTime)
   1845   local path = mp.get_property('path')
   1846   if not path then
   1847     return nil, nil, nil, nil, nil
   1848   end
   1849   local is_stream = not file_exists(path)
   1850   local is_temporary = false
   1851   if is_stream then
   1852     if mp.get_property('file-format') == 'hls' then
   1853       path = utils.join_path(parse_directory('~'), 'cache_dump.ts')
   1854       mp.command_native({
   1855         'dump_cache',
   1856         seconds_to_time_string(startTime, false, true),
   1857         seconds_to_time_string(endTime + 5, false, true),
   1858         path
   1859       })
   1860       endTime = endTime - startTime
   1861       startTime = 0
   1862       is_temporary = true
   1863     end
   1864   end
   1865   return path, is_stream, is_temporary, startTime, endTime
   1866 end
   1867 local encode
   1868 encode = function(region, startTime, endTime)
   1869   local format = formats[options.output_format]
   1870   local originalStartTime = startTime
   1871   local originalEndTime = endTime
   1872   local path, is_stream, is_temporary
   1873   path, is_stream, is_temporary, startTime, endTime = find_path(startTime, endTime)
   1874   if not path then
   1875     message("No file is being played")
   1876     return 
   1877   end
   1878   local command = {
   1879     "mpv",
   1880     path,
   1881     "--start=" .. seconds_to_time_string(startTime, false, true),
   1882     "--end=" .. seconds_to_time_string(endTime, false, true),
   1883     "--loop-file=no",
   1884     "--no-pause"
   1885   }
   1886   append(command, format:getCodecFlags())
   1887   local active_tracks = get_active_tracks()
   1888   local supported_active_tracks = filter_tracks_supported_by_format(active_tracks, format)
   1889   for track_type, tracks in pairs(supported_active_tracks) do
   1890     if track_type == "audio" then
   1891       append_audio_tracks(command, tracks)
   1892     else
   1893       for _index_0 = 1, #tracks do
   1894         local track = tracks[_index_0]
   1895         append_track(command, track)
   1896       end
   1897     end
   1898   end
   1899   for track_type, tracks in pairs(supported_active_tracks) do
   1900     local _continue_0 = false
   1901     repeat
   1902       if #tracks > 0 then
   1903         _continue_0 = true
   1904         break
   1905       end
   1906       local _exp_0 = track_type
   1907       if "video" == _exp_0 then
   1908         append(command, {
   1909           "--vid=no"
   1910         })
   1911       elseif "audio" == _exp_0 then
   1912         append(command, {
   1913           "--aid=no"
   1914         })
   1915       elseif "sub" == _exp_0 then
   1916         append(command, {
   1917           "--sid=no"
   1918         })
   1919       end
   1920       _continue_0 = true
   1921     until true
   1922     if not _continue_0 then
   1923       break
   1924     end
   1925   end
   1926   if format.videoCodec ~= "" then
   1927     append(command, get_video_encode_flags(format, region))
   1928   end
   1929   append(command, format:getFlags())
   1930   if options.write_filename_on_metadata then
   1931     append(command, get_metadata_flags())
   1932   end
   1933   if format.acceptsBitrate then
   1934     if options.target_filesize > 0 then
   1935       local length = endTime - startTime
   1936       local video_bitrate, audio_bitrate = calculate_bitrate(supported_active_tracks, format, length)
   1937       if video_bitrate then
   1938         append(command, {
   1939           "--ovcopts-add=b=" .. tostring(video_bitrate) .. "k"
   1940         })
   1941       end
   1942       if audio_bitrate then
   1943         append(command, {
   1944           "--oacopts-add=b=" .. tostring(audio_bitrate) .. "k"
   1945         })
   1946       end
   1947       if options.strict_filesize_constraint then
   1948         local type = format.videoCodec ~= "" and "ovc" or "oac"
   1949         append(command, {
   1950           "--" .. tostring(type) .. "opts-add=minrate=" .. tostring(bitrate) .. "k",
   1951           "--" .. tostring(type) .. "opts-add=maxrate=" .. tostring(bitrate) .. "k"
   1952         })
   1953       end
   1954     else
   1955       local type = format.videoCodec ~= "" and "ovc" or "oac"
   1956       append(command, {
   1957         "--" .. tostring(type) .. "opts-add=b=0"
   1958       })
   1959     end
   1960   end
   1961   for token in string.gmatch(options.additional_flags, "[^%s]+") do
   1962     command[#command + 1] = token
   1963   end
   1964   if not options.strict_filesize_constraint then
   1965     for token in string.gmatch(options.non_strict_additional_flags, "[^%s]+") do
   1966       command[#command + 1] = token
   1967     end
   1968     if options.crf >= 0 then
   1969       append(command, {
   1970         "--ovcopts-add=crf=" .. tostring(options.crf)
   1971       })
   1972     end
   1973   end
   1974   local dir = ""
   1975   if is_stream then
   1976     dir = parse_directory("~")
   1977   else
   1978     local _
   1979     dir, _ = utils.split_path(path)
   1980   end
   1981   if options.output_directory ~= "" then
   1982     dir = parse_directory(options.output_directory)
   1983   end
   1984   local formatted_filename = format_filename(originalStartTime, originalEndTime, format)
   1985   local out_path = utils.join_path(dir, formatted_filename)
   1986   append(command, {
   1987     "--o=" .. tostring(out_path)
   1988   })
   1989   emit_event("encode-started")
   1990   if options.twopass and format.supportsTwopass and not is_stream then
   1991     local first_pass_cmdline
   1992     do
   1993       local _accum_0 = { }
   1994       local _len_0 = 1
   1995       for _index_0 = 1, #command do
   1996         local arg = command[_index_0]
   1997         _accum_0[_len_0] = arg
   1998         _len_0 = _len_0 + 1
   1999       end
   2000       first_pass_cmdline = _accum_0
   2001     end
   2002     append(first_pass_cmdline, {
   2003       "--ovcopts-add=flags=+pass1"
   2004     })
   2005     message("Starting first pass...")
   2006     msg.verbose("First-pass command line: ", table.concat(first_pass_cmdline, " "))
   2007     local res = run_subprocess({
   2008       args = first_pass_cmdline,
   2009       cancellable = false
   2010     })
   2011     if not res then
   2012       message("First pass failed! Check the logs for details.")
   2013       emit_event("encode-finished", "fail")
   2014       return 
   2015     end
   2016     append(command, {
   2017       "--ovcopts-add=flags=+pass2"
   2018     })
   2019     if format.videoCodec == "libvpx" then
   2020       msg.verbose("Patching libvpx pass log file...")
   2021       vp8_patch_logfile(get_pass_logfile_path(out_path), endTime - startTime)
   2022     end
   2023   end
   2024   command = format:postCommandModifier(command, region, startTime, endTime)
   2025   msg.info("Encoding to", out_path)
   2026   msg.verbose("Command line:", table.concat(command, " "))
   2027   if options.run_detached then
   2028     message("Started encode, process was detached.")
   2029     return utils.subprocess_detached({
   2030       args = command
   2031     })
   2032   else
   2033     local res = false
   2034     if not should_display_progress() then
   2035       message("Started encode...")
   2036       res = run_subprocess({
   2037         args = command,
   2038         cancellable = false
   2039       })
   2040     else
   2041       local ewp = EncodeWithProgress(startTime, endTime)
   2042       res = ewp:startEncode(command)
   2043     end
   2044     if res then
   2045       message("Encoded successfully! Saved to\\N" .. tostring(bold(out_path)))
   2046       emit_event("encode-finished", "success")
   2047     else
   2048       message("Encode failed! Check the logs for details.")
   2049       emit_event("encode-finished", "fail")
   2050     end
   2051     os.remove(get_pass_logfile_path(out_path))
   2052     if is_temporary then
   2053       return os.remove(path)
   2054     end
   2055   end
   2056 end
   2057 local CropPage
   2058 do
   2059   local _class_0
   2060   local _parent_0 = Page
   2061   local _base_0 = {
   2062     reset = function(self)
   2063       local dimensions = get_video_dimensions()
   2064       local xa, ya
   2065       do
   2066         local _obj_0 = dimensions.top_left
   2067         xa, ya = _obj_0.x, _obj_0.y
   2068       end
   2069       self.pointA:set_from_screen(xa, ya)
   2070       local xb, yb
   2071       do
   2072         local _obj_0 = dimensions.bottom_right
   2073         xb, yb = _obj_0.x, _obj_0.y
   2074       end
   2075       self.pointB:set_from_screen(xb, yb)
   2076       if self.visible then
   2077         return self:draw()
   2078       end
   2079     end,
   2080     setPointA = function(self)
   2081       local posX, posY = mp.get_mouse_pos()
   2082       self.pointA:set_from_screen(posX, posY)
   2083       if self.visible then
   2084         return self:draw()
   2085       end
   2086     end,
   2087     setPointB = function(self)
   2088       local posX, posY = mp.get_mouse_pos()
   2089       self.pointB:set_from_screen(posX, posY)
   2090       if self.visible then
   2091         return self:draw()
   2092       end
   2093     end,
   2094     cancel = function(self)
   2095       self:hide()
   2096       return self.callback(false, nil)
   2097     end,
   2098     finish = function(self)
   2099       local region = Region()
   2100       region:set_from_points(self.pointA, self.pointB)
   2101       self:hide()
   2102       return self.callback(true, region)
   2103     end,
   2104     draw_box = function(self, ass)
   2105       local region = Region()
   2106       region:set_from_points(self.pointA:to_screen(), self.pointB:to_screen())
   2107       local d = get_video_dimensions()
   2108       ass:new_event()
   2109       ass:append("{\\an7}")
   2110       ass:pos(0, 0)
   2111       ass:append('{\\bord0}')
   2112       ass:append('{\\shad0}')
   2113       ass:append('{\\c&H000000&}')
   2114       ass:append('{\\alpha&H77}')
   2115       ass:draw_start()
   2116       ass:rect_cw(d.top_left.x, d.top_left.y, region.x, region.y + region.h)
   2117       ass:rect_cw(region.x, d.top_left.y, d.bottom_right.x, region.y)
   2118       ass:rect_cw(d.top_left.x, region.y + region.h, region.x + region.w, d.bottom_right.y)
   2119       ass:rect_cw(region.x + region.w, region.y, d.bottom_right.x, d.bottom_right.y)
   2120       return ass:draw_stop()
   2121     end,
   2122     draw = function(self)
   2123       local window = { }
   2124       window.w, window.h = mp.get_osd_size()
   2125       local ass = assdraw.ass_new()
   2126       self:draw_box(ass)
   2127       ass:new_event()
   2128       self:setup_text(ass)
   2129       ass:append(tostring(bold('Crop:')) .. "\\N")
   2130       ass:append(tostring(bold('1:')) .. " change point A (" .. tostring(self.pointA.x) .. ", " .. tostring(self.pointA.y) .. ")\\N")
   2131       ass:append(tostring(bold('2:')) .. " change point B (" .. tostring(self.pointB.x) .. ", " .. tostring(self.pointB.y) .. ")\\N")
   2132       ass:append(tostring(bold('r:')) .. " reset to whole screen\\N")
   2133       ass:append(tostring(bold('ESC:')) .. " cancel crop\\N")
   2134       local width, height = math.abs(self.pointA.x - self.pointB.x), math.abs(self.pointA.y - self.pointB.y)
   2135       ass:append(tostring(bold('ENTER:')) .. " confirm crop (" .. tostring(width) .. "x" .. tostring(height) .. ")\\N")
   2136       return mp.set_osd_ass(window.w, window.h, ass.text)
   2137     end
   2138   }
   2139   _base_0.__index = _base_0
   2140   setmetatable(_base_0, _parent_0.__base)
   2141   _class_0 = setmetatable({
   2142     __init = function(self, callback, region)
   2143       self.pointA = VideoPoint()
   2144       self.pointB = VideoPoint()
   2145       self.keybinds = {
   2146         ["1"] = (function()
   2147           local _base_1 = self
   2148           local _fn_0 = _base_1.setPointA
   2149           return function(...)
   2150             return _fn_0(_base_1, ...)
   2151           end
   2152         end)(),
   2153         ["2"] = (function()
   2154           local _base_1 = self
   2155           local _fn_0 = _base_1.setPointB
   2156           return function(...)
   2157             return _fn_0(_base_1, ...)
   2158           end
   2159         end)(),
   2160         ["r"] = (function()
   2161           local _base_1 = self
   2162           local _fn_0 = _base_1.reset
   2163           return function(...)
   2164             return _fn_0(_base_1, ...)
   2165           end
   2166         end)(),
   2167         ["ESC"] = (function()
   2168           local _base_1 = self
   2169           local _fn_0 = _base_1.cancel
   2170           return function(...)
   2171             return _fn_0(_base_1, ...)
   2172           end
   2173         end)(),
   2174         ["ENTER"] = (function()
   2175           local _base_1 = self
   2176           local _fn_0 = _base_1.finish
   2177           return function(...)
   2178             return _fn_0(_base_1, ...)
   2179           end
   2180         end)()
   2181       }
   2182       self:reset()
   2183       self.callback = callback
   2184       if region and region:is_valid() then
   2185         self.pointA.x = region.x
   2186         self.pointA.y = region.y
   2187         self.pointB.x = region.x + region.w
   2188         self.pointB.y = region.y + region.h
   2189       end
   2190     end,
   2191     __base = _base_0,
   2192     __name = "CropPage",
   2193     __parent = _parent_0
   2194   }, {
   2195     __index = function(cls, name)
   2196       local val = rawget(_base_0, name)
   2197       if val == nil then
   2198         local parent = rawget(cls, "__parent")
   2199         if parent then
   2200           return parent[name]
   2201         end
   2202       else
   2203         return val
   2204       end
   2205     end,
   2206     __call = function(cls, ...)
   2207       local _self_0 = setmetatable({}, _base_0)
   2208       cls.__init(_self_0, ...)
   2209       return _self_0
   2210     end
   2211   })
   2212   _base_0.__class = _class_0
   2213   if _parent_0.__inherited then
   2214     _parent_0.__inherited(_parent_0, _class_0)
   2215   end
   2216   CropPage = _class_0
   2217 end
   2218 local Option
   2219 do
   2220   local _class_0
   2221   local _base_0 = {
   2222     hasPrevious = function(self)
   2223       local _exp_0 = self.optType
   2224       if "bool" == _exp_0 then
   2225         return true
   2226       elseif "int" == _exp_0 then
   2227         if self.opts.min then
   2228           return self.value > self.opts.min
   2229         else
   2230           return true
   2231         end
   2232       elseif "list" == _exp_0 then
   2233         return self.value > 1
   2234       end
   2235     end,
   2236     hasNext = function(self)
   2237       local _exp_0 = self.optType
   2238       if "bool" == _exp_0 then
   2239         return true
   2240       elseif "int" == _exp_0 then
   2241         if self.opts.max then
   2242           return self.value < self.opts.max
   2243         else
   2244           return true
   2245         end
   2246       elseif "list" == _exp_0 then
   2247         return self.value < #self.opts.possibleValues
   2248       end
   2249     end,
   2250     leftKey = function(self)
   2251       local _exp_0 = self.optType
   2252       if "bool" == _exp_0 then
   2253         self.value = not self.value
   2254       elseif "int" == _exp_0 then
   2255         self.value = self.value - self.opts.step
   2256         if self.opts.min and self.opts.min > self.value then
   2257           self.value = self.opts.min
   2258         end
   2259       elseif "list" == _exp_0 then
   2260         if self.value > 1 then
   2261           self.value = self.value - 1
   2262         end
   2263       end
   2264     end,
   2265     rightKey = function(self)
   2266       local _exp_0 = self.optType
   2267       if "bool" == _exp_0 then
   2268         self.value = not self.value
   2269       elseif "int" == _exp_0 then
   2270         self.value = self.value + self.opts.step
   2271         if self.opts.max and self.opts.max < self.value then
   2272           self.value = self.opts.max
   2273         end
   2274       elseif "list" == _exp_0 then
   2275         if self.value < #self.opts.possibleValues then
   2276           self.value = self.value + 1
   2277         end
   2278       end
   2279     end,
   2280     getValue = function(self)
   2281       local _exp_0 = self.optType
   2282       if "bool" == _exp_0 then
   2283         return self.value
   2284       elseif "int" == _exp_0 then
   2285         return self.value
   2286       elseif "list" == _exp_0 then
   2287         local value, _
   2288         do
   2289           local _obj_0 = self.opts.possibleValues[self.value]
   2290           value, _ = _obj_0[1], _obj_0[2]
   2291         end
   2292         return value
   2293       end
   2294     end,
   2295     setValue = function(self, value)
   2296       local _exp_0 = self.optType
   2297       if "bool" == _exp_0 then
   2298         self.value = value
   2299       elseif "int" == _exp_0 then
   2300         self.value = value
   2301       elseif "list" == _exp_0 then
   2302         local set = false
   2303         for i, possiblePair in ipairs(self.opts.possibleValues) do
   2304           local possibleValue, _
   2305           possibleValue, _ = possiblePair[1], possiblePair[2]
   2306           if possibleValue == value then
   2307             set = true
   2308             self.value = i
   2309             break
   2310           end
   2311         end
   2312         if not set then
   2313           return msg.warn("Tried to set invalid value " .. tostring(value) .. " to " .. tostring(self.displayText) .. " option.")
   2314         end
   2315       end
   2316     end,
   2317     getDisplayValue = function(self)
   2318       local _exp_0 = self.optType
   2319       if "bool" == _exp_0 then
   2320         return self.value and "yes" or "no"
   2321       elseif "int" == _exp_0 then
   2322         if self.opts.altDisplayNames and self.opts.altDisplayNames[self.value] then
   2323           return self.opts.altDisplayNames[self.value]
   2324         else
   2325           return tostring(self.value)
   2326         end
   2327       elseif "list" == _exp_0 then
   2328         local value, displayValue
   2329         do
   2330           local _obj_0 = self.opts.possibleValues[self.value]
   2331           value, displayValue = _obj_0[1], _obj_0[2]
   2332         end
   2333         return displayValue or value
   2334       end
   2335     end,
   2336     draw = function(self, ass, selected)
   2337       if selected then
   2338         ass:append(tostring(bold(self.displayText)) .. ": ")
   2339       else
   2340         ass:append(tostring(self.displayText) .. ": ")
   2341       end
   2342       if self:hasPrevious() then
   2343         ass:append("◀ ")
   2344       end
   2345       ass:append(self:getDisplayValue())
   2346       if self:hasNext() then
   2347         ass:append(" ▶")
   2348       end
   2349       return ass:append("\\N")
   2350     end,
   2351     optVisible = function(self)
   2352       if self.visibleCheckFn == nil then
   2353         return true
   2354       else
   2355         return self.visibleCheckFn()
   2356       end
   2357     end
   2358   }
   2359   _base_0.__index = _base_0
   2360   _class_0 = setmetatable({
   2361     __init = function(self, optType, displayText, value, opts, visibleCheckFn)
   2362       self.optType = optType
   2363       self.displayText = displayText
   2364       self.opts = opts
   2365       self.value = 1
   2366       self.visibleCheckFn = visibleCheckFn
   2367       return self:setValue(value)
   2368     end,
   2369     __base = _base_0,
   2370     __name = "Option"
   2371   }, {
   2372     __index = _base_0,
   2373     __call = function(cls, ...)
   2374       local _self_0 = setmetatable({}, _base_0)
   2375       cls.__init(_self_0, ...)
   2376       return _self_0
   2377     end
   2378   })
   2379   _base_0.__class = _class_0
   2380   Option = _class_0
   2381 end
   2382 local EncodeOptionsPage
   2383 do
   2384   local _class_0
   2385   local _parent_0 = Page
   2386   local _base_0 = {
   2387     getCurrentOption = function(self)
   2388       return self.options[self.currentOption][2]
   2389     end,
   2390     leftKey = function(self)
   2391       (self:getCurrentOption()):leftKey()
   2392       return self:draw()
   2393     end,
   2394     rightKey = function(self)
   2395       (self:getCurrentOption()):rightKey()
   2396       return self:draw()
   2397     end,
   2398     prevOpt = function(self)
   2399       for i = self.currentOption - 1, 1, -1 do
   2400         if self.options[i][2]:optVisible() then
   2401           self.currentOption = i
   2402           break
   2403         end
   2404       end
   2405       return self:draw()
   2406     end,
   2407     nextOpt = function(self)
   2408       for i = self.currentOption + 1, #self.options do
   2409         if self.options[i][2]:optVisible() then
   2410           self.currentOption = i
   2411           break
   2412         end
   2413       end
   2414       return self:draw()
   2415     end,
   2416     confirmOpts = function(self)
   2417       for _, optPair in ipairs(self.options) do
   2418         local optName, opt
   2419         optName, opt = optPair[1], optPair[2]
   2420         options[optName] = opt:getValue()
   2421       end
   2422       self:hide()
   2423       return self.callback(true)
   2424     end,
   2425     cancelOpts = function(self)
   2426       self:hide()
   2427       return self.callback(false)
   2428     end,
   2429     draw = function(self)
   2430       local window_w, window_h = mp.get_osd_size()
   2431       local ass = assdraw.ass_new()
   2432       ass:new_event()
   2433       self:setup_text(ass)
   2434       ass:append(tostring(bold('Options:')) .. "\\N\\N")
   2435       for i, optPair in ipairs(self.options) do
   2436         local opt = optPair[2]
   2437         if opt:optVisible() then
   2438           opt:draw(ass, self.currentOption == i)
   2439         end
   2440       end
   2441       ass:append("\\N▲ / ▼: navigate\\N")
   2442       ass:append(tostring(bold('ENTER:')) .. " confirm options\\N")
   2443       ass:append(tostring(bold('ESC:')) .. " cancel\\N")
   2444       return mp.set_osd_ass(window_w, window_h, ass.text)
   2445     end
   2446   }
   2447   _base_0.__index = _base_0
   2448   setmetatable(_base_0, _parent_0.__base)
   2449   _class_0 = setmetatable({
   2450     __init = function(self, callback)
   2451       self.callback = callback
   2452       self.currentOption = 1
   2453       local scaleHeightOpts = {
   2454         possibleValues = {
   2455           {
   2456             -1,
   2457             "no"
   2458           },
   2459           {
   2460             144
   2461           },
   2462           {
   2463             240
   2464           },
   2465           {
   2466             360
   2467           },
   2468           {
   2469             480
   2470           },
   2471           {
   2472             540
   2473           },
   2474           {
   2475             720
   2476           },
   2477           {
   2478             1080
   2479           },
   2480           {
   2481             1440
   2482           },
   2483           {
   2484             2160
   2485           }
   2486         }
   2487       }
   2488       local filesizeOpts = {
   2489         step = 250,
   2490         min = 0,
   2491         altDisplayNames = {
   2492           [0] = "0 (constant quality)"
   2493         }
   2494       }
   2495       local crfOpts = {
   2496         step = 1,
   2497         min = -1,
   2498         altDisplayNames = {
   2499           [-1] = "disabled"
   2500         }
   2501       }
   2502       local fpsOpts = {
   2503         possibleValues = {
   2504           {
   2505             -1,
   2506             "source"
   2507           },
   2508           {
   2509             15
   2510           },
   2511           {
   2512             24
   2513           },
   2514           {
   2515             30
   2516           },
   2517           {
   2518             48
   2519           },
   2520           {
   2521             50
   2522           },
   2523           {
   2524             60
   2525           },
   2526           {
   2527             120
   2528           },
   2529           {
   2530             240
   2531           }
   2532         }
   2533       }
   2534       local formatIds = {
   2535         "av1",
   2536         "hevc",
   2537         "webm-vp9",
   2538         "avc",
   2539         "avc-nvenc",
   2540         "webm-vp8",
   2541         "gif",
   2542         "mp3",
   2543         "raw"
   2544       }
   2545       local formatOpts = {
   2546         possibleValues = (function()
   2547           local _accum_0 = { }
   2548           local _len_0 = 1
   2549           for _index_0 = 1, #formatIds do
   2550             local fId = formatIds[_index_0]
   2551             _accum_0[_len_0] = {
   2552               fId,
   2553               formats[fId].displayName
   2554             }
   2555             _len_0 = _len_0 + 1
   2556           end
   2557           return _accum_0
   2558         end)()
   2559       }
   2560       local gifDitherOpts = {
   2561         possibleValues = {
   2562           {
   2563             0,
   2564             "bayer_scale 0"
   2565           },
   2566           {
   2567             1,
   2568             "bayer_scale 1"
   2569           },
   2570           {
   2571             2,
   2572             "bayer_scale 2"
   2573           },
   2574           {
   2575             3,
   2576             "bayer_scale 3"
   2577           },
   2578           {
   2579             4,
   2580             "bayer_scale 4"
   2581           },
   2582           {
   2583             5,
   2584             "bayer_scale 5"
   2585           },
   2586           {
   2587             6,
   2588             "sierra2_4a"
   2589           }
   2590         }
   2591       }
   2592       self.options = {
   2593         {
   2594           "output_format",
   2595           Option("list", "Output Format", options.output_format, formatOpts)
   2596         },
   2597         {
   2598           "twopass",
   2599           Option("bool", "Two Pass", options.twopass)
   2600         },
   2601         {
   2602           "apply_current_filters",
   2603           Option("bool", "Apply Current Video Filters", options.apply_current_filters)
   2604         },
   2605         {
   2606           "scale_height",
   2607           Option("list", "Scale Height", options.scale_height, scaleHeightOpts)
   2608         },
   2609         {
   2610           "strict_filesize_constraint",
   2611           Option("bool", "Strict Filesize Constraint", options.strict_filesize_constraint)
   2612         },
   2613         {
   2614           "write_filename_on_metadata",
   2615           Option("bool", "Write Filename on Metadata", options.write_filename_on_metadata)
   2616         },
   2617         {
   2618           "target_filesize",
   2619           Option("int", "Target Filesize", options.target_filesize, filesizeOpts)
   2620         },
   2621         {
   2622           "crf",
   2623           Option("int", "CRF", options.crf, crfOpts)
   2624         },
   2625         {
   2626           "fps",
   2627           Option("list", "FPS", options.fps, fpsOpts)
   2628         },
   2629         {
   2630           "gif_dither",
   2631           Option("list", "GIF Dither Type", options.gif_dither, gifDitherOpts, function()
   2632             return self.options[1][2]:getValue() == "gif"
   2633           end)
   2634         },
   2635         {
   2636           "force_square_pixels",
   2637           Option("bool", "Force Square Pixels", options.force_square_pixels)
   2638         }
   2639       }
   2640       self.keybinds = {
   2641         ["LEFT"] = (function()
   2642           local _base_1 = self
   2643           local _fn_0 = _base_1.leftKey
   2644           return function(...)
   2645             return _fn_0(_base_1, ...)
   2646           end
   2647         end)(),
   2648         ["RIGHT"] = (function()
   2649           local _base_1 = self
   2650           local _fn_0 = _base_1.rightKey
   2651           return function(...)
   2652             return _fn_0(_base_1, ...)
   2653           end
   2654         end)(),
   2655         ["UP"] = (function()
   2656           local _base_1 = self
   2657           local _fn_0 = _base_1.prevOpt
   2658           return function(...)
   2659             return _fn_0(_base_1, ...)
   2660           end
   2661         end)(),
   2662         ["DOWN"] = (function()
   2663           local _base_1 = self
   2664           local _fn_0 = _base_1.nextOpt
   2665           return function(...)
   2666             return _fn_0(_base_1, ...)
   2667           end
   2668         end)(),
   2669         ["ENTER"] = (function()
   2670           local _base_1 = self
   2671           local _fn_0 = _base_1.confirmOpts
   2672           return function(...)
   2673             return _fn_0(_base_1, ...)
   2674           end
   2675         end)(),
   2676         ["ESC"] = (function()
   2677           local _base_1 = self
   2678           local _fn_0 = _base_1.cancelOpts
   2679           return function(...)
   2680             return _fn_0(_base_1, ...)
   2681           end
   2682         end)()
   2683       }
   2684     end,
   2685     __base = _base_0,
   2686     __name = "EncodeOptionsPage",
   2687     __parent = _parent_0
   2688   }, {
   2689     __index = function(cls, name)
   2690       local val = rawget(_base_0, name)
   2691       if val == nil then
   2692         local parent = rawget(cls, "__parent")
   2693         if parent then
   2694           return parent[name]
   2695         end
   2696       else
   2697         return val
   2698       end
   2699     end,
   2700     __call = function(cls, ...)
   2701       local _self_0 = setmetatable({}, _base_0)
   2702       cls.__init(_self_0, ...)
   2703       return _self_0
   2704     end
   2705   })
   2706   _base_0.__class = _class_0
   2707   if _parent_0.__inherited then
   2708     _parent_0.__inherited(_parent_0, _class_0)
   2709   end
   2710   EncodeOptionsPage = _class_0
   2711 end
   2712 local PreviewPage
   2713 do
   2714   local _class_0
   2715   local _parent_0 = Page
   2716   local _base_0 = {
   2717     prepare = function(self)
   2718       local vf = mp.get_property_native("vf")
   2719       vf[#vf + 1] = {
   2720         name = "sub"
   2721       }
   2722       if self.region:is_valid() then
   2723         vf[#vf + 1] = {
   2724           name = "crop",
   2725           params = {
   2726             w = tostring(self.region.w),
   2727             h = tostring(self.region.h),
   2728             x = tostring(self.region.x),
   2729             y = tostring(self.region.y)
   2730           }
   2731         }
   2732       end
   2733       mp.set_property_native("vf", vf)
   2734       if self.startTime > -1 and self.endTime > -1 then
   2735         mp.set_property_native("ab-loop-a", self.startTime)
   2736         mp.set_property_native("ab-loop-b", self.endTime)
   2737         mp.set_property_native("time-pos", self.startTime)
   2738       end
   2739       return mp.set_property_native("pause", false)
   2740     end,
   2741     dispose = function(self)
   2742       mp.set_property("ab-loop-a", "no")
   2743       mp.set_property("ab-loop-b", "no")
   2744       for prop, value in pairs(self.originalProperties) do
   2745         mp.set_property_native(prop, value)
   2746       end
   2747     end,
   2748     draw = function(self)
   2749       local window_w, window_h = mp.get_osd_size()
   2750       local ass = assdraw.ass_new()
   2751       ass:new_event()
   2752       self:setup_text(ass)
   2753       ass:append("Press " .. tostring(bold('ESC')) .. " to exit preview.\\N")
   2754       return mp.set_osd_ass(window_w, window_h, ass.text)
   2755     end,
   2756     cancel = function(self)
   2757       self:hide()
   2758       return self.callback()
   2759     end
   2760   }
   2761   _base_0.__index = _base_0
   2762   setmetatable(_base_0, _parent_0.__base)
   2763   _class_0 = setmetatable({
   2764     __init = function(self, callback, region, startTime, endTime)
   2765       self.callback = callback
   2766       self.originalProperties = {
   2767         ["vf"] = mp.get_property_native("vf"),
   2768         ["time-pos"] = mp.get_property_native("time-pos"),
   2769         ["pause"] = mp.get_property_native("pause")
   2770       }
   2771       self.keybinds = {
   2772         ["ESC"] = (function()
   2773           local _base_1 = self
   2774           local _fn_0 = _base_1.cancel
   2775           return function(...)
   2776             return _fn_0(_base_1, ...)
   2777           end
   2778         end)()
   2779       }
   2780       self.region = region
   2781       self.startTime = startTime
   2782       self.endTime = endTime
   2783       self.isLoop = false
   2784     end,
   2785     __base = _base_0,
   2786     __name = "PreviewPage",
   2787     __parent = _parent_0
   2788   }, {
   2789     __index = function(cls, name)
   2790       local val = rawget(_base_0, name)
   2791       if val == nil then
   2792         local parent = rawget(cls, "__parent")
   2793         if parent then
   2794           return parent[name]
   2795         end
   2796       else
   2797         return val
   2798       end
   2799     end,
   2800     __call = function(cls, ...)
   2801       local _self_0 = setmetatable({}, _base_0)
   2802       cls.__init(_self_0, ...)
   2803       return _self_0
   2804     end
   2805   })
   2806   _base_0.__class = _class_0
   2807   if _parent_0.__inherited then
   2808     _parent_0.__inherited(_parent_0, _class_0)
   2809   end
   2810   PreviewPage = _class_0
   2811 end
   2812 local MainPage
   2813 do
   2814   local _class_0
   2815   local _parent_0 = Page
   2816   local _base_0 = {
   2817     setStartTime = function(self)
   2818       self.startTime = mp.get_property_number("time-pos")
   2819       if self.visible then
   2820         self:clear()
   2821         return self:draw()
   2822       end
   2823     end,
   2824     setEndTime = function(self)
   2825       self.endTime = mp.get_property_number("time-pos")
   2826       if self.visible then
   2827         self:clear()
   2828         return self:draw()
   2829       end
   2830     end,
   2831     jumpToStartTime = function(self)
   2832       return mp.set_property("time-pos", self.startTime)
   2833     end,
   2834     jumpToEndTime = function(self)
   2835       return mp.set_property("time-pos", self.endTime)
   2836     end,
   2837     setupStartAndEndTimes = function(self)
   2838       if mp.get_property_native("duration") then
   2839         self.startTime = 0
   2840         self.endTime = mp.get_property_native("duration")
   2841       else
   2842         self.startTime = -1
   2843         self.endTime = -1
   2844       end
   2845       if self.visible then
   2846         self:clear()
   2847         return self:draw()
   2848       end
   2849     end,
   2850     draw = function(self)
   2851       local window_w, window_h = mp.get_osd_size()
   2852       local ass = assdraw.ass_new()
   2853       ass:new_event()
   2854       self:setup_text(ass)
   2855       ass:append(tostring(bold('WebM maker')) .. "\\N\\N")
   2856       ass:append(tostring(bold('c:')) .. " crop\\N")
   2857       ass:append(tostring(bold('1:')) .. " set start time (current is " .. tostring(seconds_to_time_string(self.startTime)) .. ")\\N")
   2858       ass:append(tostring(bold('2:')) .. " set end time (current is " .. tostring(seconds_to_time_string(self.endTime)) .. ")\\N")
   2859       ass:append(tostring(bold('!:')) .. " jump to start time\\N")
   2860       ass:append(tostring(bold('@:')) .. " jump to end time\\N")
   2861       ass:append(tostring(bold('o:')) .. " change encode options\\N")
   2862       ass:append(tostring(bold('p:')) .. " preview\\N")
   2863       ass:append(tostring(bold('e:')) .. " encode\\N\\N")
   2864       ass:append(tostring(bold('ESC:')) .. " close\\N")
   2865       return mp.set_osd_ass(window_w, window_h, ass.text)
   2866     end,
   2867     show = function(self)
   2868       _class_0.__parent.show(self)
   2869       return emit_event("show-main-page")
   2870     end,
   2871     onUpdateCropRegion = function(self, updated, newRegion)
   2872       if updated then
   2873         self.region = newRegion
   2874       end
   2875       return self:show()
   2876     end,
   2877     crop = function(self)
   2878       self:hide()
   2879       local cropPage = CropPage((function()
   2880         local _base_1 = self
   2881         local _fn_0 = _base_1.onUpdateCropRegion
   2882         return function(...)
   2883           return _fn_0(_base_1, ...)
   2884         end
   2885       end)(), self.region)
   2886       return cropPage:show()
   2887     end,
   2888     onOptionsChanged = function(self, updated)
   2889       return self:show()
   2890     end,
   2891     changeOptions = function(self)
   2892       self:hide()
   2893       local encodeOptsPage = EncodeOptionsPage((function()
   2894         local _base_1 = self
   2895         local _fn_0 = _base_1.onOptionsChanged
   2896         return function(...)
   2897           return _fn_0(_base_1, ...)
   2898         end
   2899       end)())
   2900       return encodeOptsPage:show()
   2901     end,
   2902     onPreviewEnded = function(self)
   2903       return self:show()
   2904     end,
   2905     preview = function(self)
   2906       self:hide()
   2907       local previewPage = PreviewPage((function()
   2908         local _base_1 = self
   2909         local _fn_0 = _base_1.onPreviewEnded
   2910         return function(...)
   2911           return _fn_0(_base_1, ...)
   2912         end
   2913       end)(), self.region, self.startTime, self.endTime)
   2914       return previewPage:show()
   2915     end,
   2916     encode = function(self)
   2917       self:hide()
   2918       if self.startTime < 0 then
   2919         message("No start time, aborting")
   2920         return 
   2921       end
   2922       if self.endTime < 0 then
   2923         message("No end time, aborting")
   2924         return 
   2925       end
   2926       if self.startTime >= self.endTime then
   2927         message("Start time is ahead of end time, aborting")
   2928         return 
   2929       end
   2930       return encode(self.region, self.startTime, self.endTime)
   2931     end
   2932   }
   2933   _base_0.__index = _base_0
   2934   setmetatable(_base_0, _parent_0.__base)
   2935   _class_0 = setmetatable({
   2936     __init = function(self)
   2937       self.keybinds = {
   2938         ["c"] = (function()
   2939           local _base_1 = self
   2940           local _fn_0 = _base_1.crop
   2941           return function(...)
   2942             return _fn_0(_base_1, ...)
   2943           end
   2944         end)(),
   2945         ["1"] = (function()
   2946           local _base_1 = self
   2947           local _fn_0 = _base_1.setStartTime
   2948           return function(...)
   2949             return _fn_0(_base_1, ...)
   2950           end
   2951         end)(),
   2952         ["2"] = (function()
   2953           local _base_1 = self
   2954           local _fn_0 = _base_1.setEndTime
   2955           return function(...)
   2956             return _fn_0(_base_1, ...)
   2957           end
   2958         end)(),
   2959         ["!"] = (function()
   2960           local _base_1 = self
   2961           local _fn_0 = _base_1.jumpToStartTime
   2962           return function(...)
   2963             return _fn_0(_base_1, ...)
   2964           end
   2965         end)(),
   2966         ["@"] = (function()
   2967           local _base_1 = self
   2968           local _fn_0 = _base_1.jumpToEndTime
   2969           return function(...)
   2970             return _fn_0(_base_1, ...)
   2971           end
   2972         end)(),
   2973         ["o"] = (function()
   2974           local _base_1 = self
   2975           local _fn_0 = _base_1.changeOptions
   2976           return function(...)
   2977             return _fn_0(_base_1, ...)
   2978           end
   2979         end)(),
   2980         ["p"] = (function()
   2981           local _base_1 = self
   2982           local _fn_0 = _base_1.preview
   2983           return function(...)
   2984             return _fn_0(_base_1, ...)
   2985           end
   2986         end)(),
   2987         ["e"] = (function()
   2988           local _base_1 = self
   2989           local _fn_0 = _base_1.encode
   2990           return function(...)
   2991             return _fn_0(_base_1, ...)
   2992           end
   2993         end)(),
   2994         ["ESC"] = (function()
   2995           local _base_1 = self
   2996           local _fn_0 = _base_1.hide
   2997           return function(...)
   2998             return _fn_0(_base_1, ...)
   2999           end
   3000         end)()
   3001       }
   3002       self.startTime = -1
   3003       self.endTime = -1
   3004       self.region = Region()
   3005     end,
   3006     __base = _base_0,
   3007     __name = "MainPage",
   3008     __parent = _parent_0
   3009   }, {
   3010     __index = function(cls, name)
   3011       local val = rawget(_base_0, name)
   3012       if val == nil then
   3013         local parent = rawget(cls, "__parent")
   3014         if parent then
   3015           return parent[name]
   3016         end
   3017       else
   3018         return val
   3019       end
   3020     end,
   3021     __call = function(cls, ...)
   3022       local _self_0 = setmetatable({}, _base_0)
   3023       cls.__init(_self_0, ...)
   3024       return _self_0
   3025     end
   3026   })
   3027   _base_0.__class = _class_0
   3028   if _parent_0.__inherited then
   3029     _parent_0.__inherited(_parent_0, _class_0)
   3030   end
   3031   MainPage = _class_0
   3032 end
   3033 monitor_dimensions()
   3034 local mainPage = MainPage()
   3035 mp.add_key_binding(options.keybind, "display-webm-encoder", (function()
   3036   local _base_0 = mainPage
   3037   local _fn_0 = _base_0.show
   3038   return function(...)
   3039     return _fn_0(_base_0, ...)
   3040   end
   3041 end)(), {
   3042   repeatable = false
   3043 })
   3044 mp.register_event("file-loaded", (function()
   3045   local _base_0 = mainPage
   3046   local _fn_0 = _base_0.setupStartAndEndTimes
   3047   return function(...)
   3048     return _fn_0(_base_0, ...)
   3049   end
   3050 end)())
   3051 msg.verbose("Loaded mpv-webm script!")
   3052 return emit_event("script-loaded")