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")