Guest User

Untitled

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