diff options
Diffstat (limited to 'config/common/mpv')
| -rw-r--r-- | config/common/mpv/input.conf | 204 | ||||
| -rwxr-xr-x | config/common/mpv/mpv.conf | 28 | ||||
| -rw-r--r-- | config/common/mpv/scripts/script-opts/webm.conf | 78 | ||||
| -rwxr-xr-x | config/common/mpv/scripts/script-opts/youtube-quality.conf | 41 | ||||
| -rw-r--r-- | config/common/mpv/scripts/webm.lua | 2914 | ||||
| -rwxr-xr-x | config/common/mpv/scripts/youtube-quality.lua | 275 | 
6 files changed, 3540 insertions, 0 deletions
diff --git a/config/common/mpv/input.conf b/config/common/mpv/input.conf new file mode 100644 index 0000000..9de1b1b --- /dev/null +++ b/config/common/mpv/input.conf @@ -0,0 +1,204 @@ +# mpv keybindings +# +# Location of user-defined bindings: ~/.config/mpv/input.conf +# +# Lines starting with # are comments. Use SHARP to assign the # key. +# Copy this file and uncomment and edit the bindings you want to change. +# +# List of commands and further details: DOCS/man/input.rst +# List of special keys: --input-keylist +# Keybindings testing mode: mpv --input-test --force-window --idle +# +# Use 'ignore' to unbind a key fully (e.g. 'ctrl+a ignore'). +# +# Strings need to be quoted and escaped: +#   KEY show-text "This is a single backslash: \\ and a quote: \" !" +# +# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with +# the modifiers Shift, Ctrl, Alt and Meta (may not work on the terminal). +# +# The default keybindings are hardcoded into the mpv binary. +# You can disable them completely with: --no-input-default-bindings + +# Developer note: +# On compilation, this file is baked into the mpv binary, and all lines are +# uncommented (unless '#' is followed by a space) - thus this file defines the +# default key bindings. + +# If this is enabled, treat all the following bindings as default. +#default-bindings start + +#MBTN_LEFT     ignore              # don't do anything +#MBTN_LEFT_DBL cycle fullscreen    # toggle fullscreen +#MBTN_RIGHT    cycle pause         # toggle pause/playback mode +#MBTN_BACK     playlist-prev       # skip to the previous file +#MBTN_FORWARD  playlist-next       # skip to the next file + +# Mouse wheels, touchpad or other input devices that have axes +# if the input devices supports precise scrolling it will also scale the +# numeric value accordingly +#WHEEL_UP      seek 10          # seek 10 seconds forward +#WHEEL_DOWN    seek -10         # seek 10 seconds backward +#WHEEL_LEFT    add volume -2 +#WHEEL_RIGHT   add volume 2 + +## Seek units are in seconds, but note that these are limited by keyframes +#RIGHT seek  5                          # seek 5 seconds forward +#LEFT  seek -5                          # seek 5 seconds backward +#UP    seek  60                         # seek 1 minute forward +#DOWN  seek -60                         # seek 1 minute backward +# Do smaller, always exact (non-keyframe-limited), seeks with shift. +# Don't show them on the OSD (no-osd). +#Shift+RIGHT no-osd seek  1 exact       # seek exactly 1 second forward +#Shift+LEFT  no-osd seek -1 exact       # seek exactly 1 second backward +#Shift+UP    no-osd seek  5 exact       # seek exactly 5 seconds forward +#Shift+DOWN  no-osd seek -5 exact       # seek exactly 5 seconds backward +#Ctrl+LEFT   no-osd sub-seek -1         # seek to the previous subtitle +#Ctrl+RIGHT  no-osd sub-seek  1         # seek to the next subtitle +#Ctrl+Shift+LEFT sub-step -1            # change subtitle timing such that the previous subtitle is displayed +#Ctrl+Shift+RIGHT sub-step 1            # change subtitle timing such that the next subtitle is displayed +#Alt+left  add video-pan-x  0.1         # move the video right +#Alt+right add video-pan-x -0.1         # move the video left +#Alt+up    add video-pan-y  0.1         # move the video down +#Alt+down  add video-pan-y -0.1         # move the video up +#Alt++     add video-zoom   0.1         # zoom in +#Alt+-     add video-zoom  -0.1         # zoom out +#Alt+BS set video-zoom 0 ; set video-pan-x 0 ; set video-pan-y 0 # reset zoom and pan settings +#PGUP add chapter 1                     # seek to the next chapter +#PGDWN add chapter -1                   # seek to the previous chapter +#Shift+PGUP seek 600                    # seek 10 minutes forward +#Shift+PGDWN seek -600                  # seek 10 minutes backward +#[ multiply speed 1/1.1                 # decrease the playback speed +#] multiply speed 1.1                   # increase the playback speed +#{ multiply speed 0.5                   # halve the playback speed +#} multiply speed 2.0                   # double the playback speed +#BS set speed 1.0                       # reset the speed to normal +#Shift+BS revert-seek                   # undo the previous (or marked) seek +#Shift+Ctrl+BS revert-seek mark         # mark the position for revert-seek +#q quit +#Q quit-watch-later                     # exit and remember the playback position +#q {encode} quit 4 +#ESC set fullscreen no                  # leave fullscreen +#ESC {encode} quit 4 +#p cycle pause                          # toggle pause/playback mode +#. frame-step                           # advance one frame and pause +#, frame-back-step                      # go back by one frame and pause +#SPACE cycle pause                      # toggle pause/playback mode +#> playlist-next                        # skip to the next file +#ENTER playlist-next                    # skip to the next file +#< playlist-prev                        # skip to the previous file +#O no-osd cycle-values osd-level 3 1    # toggle displaying the OSD on user interaction or always +#o show-progress                        # show playback progress +#P show-progress                        # show playback progress +#i script-binding stats/display-stats   # display information and statistics +#I script-binding stats/display-stats-toggle # toggle displaying information and statistics +#` script-binding console/enable        # open the console +#z add sub-delay -0.1                   # shift subtitles 100 ms earlier +#Z add sub-delay +0.1                   # delay subtitles by 100 ms +#x add sub-delay +0.1                   # delay subtitles by 100 ms +#ctrl++ add audio-delay 0.100           # change audio/video sync by delaying the audio +#ctrl+- add audio-delay -0.100          # change audio/video sync by shifting the audio earlier +#Shift+g add sub-scale +0.1             # increase the subtitle font size +#Shift+f add sub-scale -0.1             # decrease the subtitle font size +#9 add volume -2 +#/ add volume -2 +#0 add volume 2 +#* add volume 2 +#m cycle mute                           # toggle mute +#1 add contrast -1 +#2 add contrast 1 +#3 add brightness -1 +#4 add brightness 1 +#5 add gamma -1 +#6 add gamma 1 +#7 add saturation -1 +#8 add saturation 1 +#Alt+0 set current-window-scale 0.5     # halve the window size +#Alt+1 set current-window-scale 1.0     # reset the window size +#Alt+2 set current-window-scale 2.0     # double the window size +#d cycle deinterlace                    # toggle the deinterlacing filter +#r add sub-pos -1                       # move subtitles up +#R add sub-pos +1                       # move subtitles down +#t add sub-pos +1                       # move subtitles down +#v cycle sub-visibility                 # hide or show the subtitles +#Alt+v cycle secondary-sub-visibility   # hide or show the secondary subtitles +#V cycle sub-ass-vsfilter-aspect-compat # toggle stretching SSA/ASS subtitles with anamorphic videos to match the historical renderer +#u cycle-values sub-ass-override "force" "no" # toggle overriding SSA/ASS subtitle styles with the normal styles +#j cycle sub                            # switch subtitle track +#J cycle sub down                       # switch subtitle track backwards +#SHARP cycle audio                      # switch audio track +#_ cycle video                          # switch video track +#T cycle ontop                          # toggle placing the video on top of other windows +#f cycle fullscreen                     # toggle fullscreen +#s screenshot                           # take a screenshot of the video in its original resolution with subtitles +#S screenshot video                     # take a screenshot of the video in its original resolution without subtitles +#Ctrl+s screenshot window               # take a screenshot of the window with OSD and subtitles +#Alt+s screenshot each-frame            # automatically screenshot every frame; issue this command again to stop taking screenshots +#w add panscan -0.1                     # decrease panscan +#W add panscan +0.1                     # shrink black bars by cropping the video +#e add panscan +0.1                     # shrink black bars by cropping the video +#A cycle-values video-aspect-override "16:9" "4:3" "2.35:1" "-1" # cycle the video aspect ratio ("-1" is the container aspect) +#POWER quit +#PLAY cycle pause                       # toggle pause/playback mode +#PAUSE cycle pause                      # toggle pause/playback mode +#PLAYPAUSE cycle pause                  # toggle pause/playback mode +#PLAYONLY set pause no                  # unpause +#PAUSEONLY set pause yes                # pause +#STOP quit +#FORWARD seek 60                        # seek 1 minute forward +#REWIND seek -60                        # seek 1 minute backward +#NEXT playlist-next                     # skip to the next file +#PREV playlist-prev                     # skip to the previous file +#VOLUME_UP add volume 2 +#VOLUME_DOWN add volume -2 +#MUTE cycle mute                        # toggle mute +#CLOSE_WIN quit +#CLOSE_WIN {encode} quit 4 +#ctrl+w quit +#E cycle edition                        # switch edition +#l ab-loop                              # set/clear A-B loop points +#L cycle-values loop-file "inf" "no"    # toggle infinite looping +#ctrl+c quit 4 +#DEL script-binding osc/visibility      # cycle OSC visibility between never, auto (mouse-move) and always +#ctrl+h cycle-values hwdec "auto" "no"  # toggle hardware decoding +#F8 show-text ${playlist}               # show the playlist +#F9 show-text ${track-list}             # show the list of video, audio and sub tracks + +# +# Legacy bindings (may or may not be removed in the future) +# +#! add chapter -1                       # seek to the previous chapter +#@ add chapter 1                        # seek to the next chapter + +# +# Not assigned by default +# (not an exhaustive list of unbound commands) +# + +# ? cycle sub-forced-only               # toggle DVD forced subs +# ? stop                                # stop playback (quit or enter idle mode) +# + +q quit-watch-later + +# seeking +H add chapter -1 +L add chapter 1 +h seek -5 +l seek 5 +- seek -60 += seek 60 + +# Add/Decrease volume +j add volume -5  +k add volume 5  + +# Subtitles +c cycle sub                             # switch subtitle track up-order +C cycle sub down                        # switch subtitle track down-order +Alt+c cycle sub-visibility              # Toggle subtitles + +# Cycle audio tracks +v cycle audio                           # switch audio track +V cycle audio                           # switch audio track +ctrl+l cycle-values loop-file "inf" "no"    # toggle infinite looping diff --git a/config/common/mpv/mpv.conf b/config/common/mpv/mpv.conf new file mode 100755 index 0000000..4f5e251 --- /dev/null +++ b/config/common/mpv/mpv.conf @@ -0,0 +1,28 @@ +profile=gpu-hq + +# Set OSD font +osd-font='monospace' +# Set OSD font size +osd-font-size=30 + +# Set volume to 100% on startup. +volume=60  + +# Set player max vol to 100%. +volume-max=100  + +# Set max streaming quality as 1080p. +ytdl-format=bestvideo[height<=?1080]+bestaudio/best  + +# Default demuxer is 150/75 MB, note that this uses RAM so set a reasonable amount. +# 150MB, Max pre-load for network streams (1 MiB = 1048576 Bytes). +demuxer-max-bytes=150000000  + +# 75MB, Max loaded video kept after playback. +demuxer-max-back-bytes=75000000  + +# Force stream to be seekable even if disabled. +force-seekable=yes  + +slang=eng, en, english +alang=jpn diff --git a/config/common/mpv/scripts/script-opts/webm.conf b/config/common/mpv/scripts/script-opts/webm.conf new file mode 100644 index 0000000..9ea44dd --- /dev/null +++ b/config/common/mpv/scripts/script-opts/webm.conf @@ -0,0 +1,78 @@ +# Defaults to shift+w +keybind=W +# If empty, saves on the same directory of the playing video. +# A starting "~" will be replaced by the home dir. +# This field is delimited by double-square-brackets - [[ and ]] - instead of +# quotes, because Windows users might run into a issue when using +# backslashes as a path separator. Examples of valid inputs for this field +# would be: [[]] (the default, empty value), [[C:\Users\John]] (on Windows), +# and [[/home/john]] (on Unix-like systems eg. Linux). +# The [[]] delimiter is not needed when using from a configuration file +# in the script-opts folder. +output_directory= +run_detached=no +# Template string for the output file +# %f - Filename, with extension +# %F - Filename, without extension +# %T - Media title, if it exists, or filename, with extension (useful for some streams, such as YouTube). +# %s, %e - Start and end time, with milliseconds +# %S, %E - Start and end time, without milliseconds +# %M - "-audio", if audio is enabled, empty otherwise +# %R - "-(height)p", where height is the video's height, or scale_height, if it's enabled. +# More specifiers are supported, see https://mpv.io/manual/master/#options-screenshot-template +# Property expansion is supported (with %{} at top level, ${} when nested), see https://mpv.io/manual/master/#property-expansion +output_template=%F-[%s-%e]%M +# Scale video to a certain height, keeping the aspect ratio. -1 disables it. +scale_height=-1 +# Change the FPS of the output video, dropping or duplicating frames as needed. +# -1 means the FPS will be unchanged from the source. +fps=-1 +# Target filesize, in kB. This will be used to calculate the bitrate +# used on the encode. If this is set to <= 0, the video bitrate will be set +# to 0, which might enable constant quality modes, depending on the +# video codec that's used (VP8 and VP9, for example). +target_filesize=2500 +# If true, will use stricter flags to ensure the resulting file doesn't +# overshoot the target filesize. Not recommended, as constrained quality +# mode should work well, unless you're really having trouble hitting +# the target size. +strict_filesize_constraint=no +strict_bitrate_multiplier=0.95 +# In kilobits. +strict_audio_bitrate=64 +# Sets the output format, from a few predefined ones. +# Currently we have: +# webm-vp8 (libvpx/libvorbis) +# webm-vp9 (libvpx-vp9/libopus) +# mp4 (h264/AAC) +# mp4-nvenc (h264-NVENC/AAC) +# raw (rawvideo/pcm_s16le). +# mp3 (libmp3lame) +# and gif +output_format=webm-vp8 +twopass=yes +# If set, applies the video filters currently used on the playback to the encode. +apply_current_filters=yes +# If set, writes the video's filename to the "Title" field on the metadata. +write_filename_on_metadata=no +# Set the number of encoding threads, for codecs libvpx and libvpx-vp9 +libvpx_threads=4 +additional_flags= +# Constant Rate Factor (CRF). The value meaning and limits may change, +# from codec to codec. Set to -1 to disable. +crf=10 +# Useful for flags that may impact output filesize, such as qmin, qmax etc +# Won't be applied when strict_filesize_constraint is on. +non_strict_additional_flags= +# Display the encode progress, in %. Requires run_detached to be disabled. +# On Windows, it shows a cmd popup. "auto" will display progress on non-Windows platforms. +display_progress=auto +# The font size used in the menu. Isn't used for the notifications (started encode, finished encode etc) +font_size=28 +margin=10 +message_duration=5 +# gif dither mode, 0-5 for bayer w/ bayer_scale 0-5, 6 for paletteuse default (sierra2_4a) +gif_dither=2 +# Force square pixels on output video +# Some players like recent Firefox versions display videos with non-square pixels with wrong aspect ratio +force_square_pixels=no diff --git a/config/common/mpv/scripts/script-opts/youtube-quality.conf b/config/common/mpv/scripts/script-opts/youtube-quality.conf new file mode 100755 index 0000000..fc1361b --- /dev/null +++ b/config/common/mpv/scripts/script-opts/youtube-quality.conf @@ -0,0 +1,41 @@ +# KEY BINDINGS + +# invoke or dismiss the quality menu +toggle_menu_binding=ctrl+f +# move the menu cursor up +up_binding=UP +# move the menu cursor down +down_binding=DOWN +# select menu entry +select_binding=ENTER + +# formatting / cursors +selected_and_active=▶ -  +selected_and_inactive=● -  +unselected_and_active=▷ -  +unselected_and_inactive=○ -  + +# font size scales by window, if false requires larger font and padding sizes +scale_playlist_by_window=false + +# playlist ass style overrides inside curly brackets, \keyvalue is one field, extra \ for escape in lua +# example {\\fnUbuntu\\fs10\\b0\\bord1} equals: font=Ubuntu, size=10, bold=no, border=1 +# read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags +# undeclared tags will use default osd settings +# these styles will be used for the whole playlist. More specific styling will need to be hacked in +# +# (a monospaced font is recommended but not required) +style_ass_tags="{\\fnmonospace}" + +# paddings for top left corner +text_padding_x=1 +text_padding_y=1 + +# how many seconds until the quality menu times out +menu_timeout=10 + +#use youtube-dl to fetch a list of available formats (overrides quality_strings) +fetch_formats=yes + +# list of ytdl-format strings to choose from +quality_strings=[ {"4320p" : "bestvideo[height<=?4320p]+bestaudio/best"}, {"2160p" : "bestvideo[height<=?2160]+bestaudio/best"}, {"1440p" : "bestvideo[height<=?1440]+bestaudio/best"}, {"1080p" : "bestvideo[height<=?1080]+bestaudio/best"}, {"720p" : "bestvideo[height<=?720]+bestaudio/best"}, {"480p" : "bestvideo[height<=?480]+bestaudio/best"}, {"360p" : "bestvideo[height<=?360]+bestaudio/best"}, {"240p" : "bestvideo[height<=?240]+bestaudio/best"}, {"144p" : "bestvideo[height<=?144]+bestaudio/best"} ] diff --git a/config/common/mpv/scripts/webm.lua b/config/common/mpv/scripts/webm.lua new file mode 100644 index 0000000..4397b9b --- /dev/null +++ b/config/common/mpv/scripts/webm.lua @@ -0,0 +1,2914 @@ +local mp = require("mp") +local assdraw = require("mp.assdraw") +local msg = require("mp.msg") +local utils = require("mp.utils") +local mpopts = require("mp.options") +local options = { +	-- Defaults to shift+w +	keybind = "W", +	-- If empty, saves on the same directory of the playing video. +	-- A starting "~" will be replaced by the home dir. +	-- This field is delimited by double-square-brackets - [[ and ]] - instead of +	-- quotes, because Windows users might run into a issue when using +	-- backslashes as a path separator. Examples of valid inputs for this field +	-- would be: [[]] (the default, empty value), [[C:\Users\John]] (on Windows), +	-- and [[/home/john]] (on Unix-like systems eg. Linux). +	-- The [[]] delimiter is not needed when using from a configuration file +	-- in the script-opts folder. +	output_directory = [[]], +	run_detached = false, +	-- Template string for the output file +	-- %f - Filename, with extension +	-- %F - Filename, without extension +	-- %T - Media title, if it exists, or filename, with extension (useful for some streams, such as YouTube). +	-- %s, %e - Start and end time, with milliseconds +	-- %S, %E - Start and end time, without milliseconds +	-- %M - "-audio", if audio is enabled, empty otherwise +	-- %R - "-(height)p", where height is the video's height, or scale_height, if it's enabled. +	-- More specifiers are supported, see https://mpv.io/manual/master/#options-screenshot-template +	-- Property expansion is supported (with %{} at top level, ${} when nested), see https://mpv.io/manual/master/#property-expansion +	output_template = "%F-[%s-%e]%M", +	-- Scale video to a certain height, keeping the aspect ratio. -1 disables it. +	scale_height = -1, +	-- Change the FPS of the output video, dropping or duplicating frames as needed. +	-- -1 means the FPS will be unchanged from the source. +	fps = -1, +	-- Target filesize, in kB. This will be used to calculate the bitrate +	-- used on the encode. If this is set to <= 0, the video bitrate will be set +	-- to 0, which might enable constant quality modes, depending on the +	-- video codec that's used (VP8 and VP9, for example). +	target_filesize = 2500, +	-- If true, will use stricter flags to ensure the resulting file doesn't +	-- overshoot the target filesize. Not recommended, as constrained quality +	-- mode should work well, unless you're really having trouble hitting +	-- the target size. +	strict_filesize_constraint = false, +	strict_bitrate_multiplier = 0.95, +	-- In kilobits. +	strict_audio_bitrate = 64, +	-- Sets the output format, from a few predefined ones. +	-- Currently we have: +	-- webm-vp8 (libvpx/libvorbis) +	-- webm-vp9 (libvpx-vp9/libopus) +	-- mp4 (h264/AAC) +	-- mp4-nvenc (h264-NVENC/AAC) +	-- raw (rawvideo/pcm_s16le). +	-- mp3 (libmp3lame) +	-- and gif +	output_format = "webm-vp8", +	twopass = true, +	-- If set, applies the video filters currently used on the playback to the encode. +	apply_current_filters = true, +	-- If set, writes the video's filename to the "Title" field on the metadata. +	write_filename_on_metadata = false, +	-- Set the number of encoding threads, for codecs libvpx and libvpx-vp9 +	libvpx_threads = 4, +	additional_flags = "", +	-- Constant Rate Factor (CRF). The value meaning and limits may change, +	-- from codec to codec. Set to -1 to disable. +	crf = 10, +	-- Useful for flags that may impact output filesize, such as qmin, qmax etc +	-- Won't be applied when strict_filesize_constraint is on. +	non_strict_additional_flags = "", +	-- Display the encode progress, in %. Requires run_detached to be disabled. +	-- On Windows, it shows a cmd popup. "auto" will display progress on non-Windows platforms. +	display_progress = "auto", +	-- The font size used in the menu. Isn't used for the notifications (started encode, finished encode etc) +	font_size = 28, +	margin = 10, +	message_duration = 5, +	-- gif dither mode, 0-5 for bayer w/ bayer_scale 0-5, 6 for paletteuse default (sierra2_4a) +	gif_dither = 2, +	-- Force square pixels on output video +	-- Some players like recent Firefox versions display videos with non-square pixels with wrong aspect ratio +	force_square_pixels = false, +} + +mpopts.read_options(options) +local base64_chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + +-- encoding +function base64_encode(data) +    return ((data:gsub('.', function(x)  +        local r,b='',x:byte() +        for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end +        return r; +    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x) +        if (#x < 6) then return '' end +        local c=0 +        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end +        return base64_chars:sub(c+1,c+1) +    end)..({ '', '==', '=' })[#data%3+1]) +end + +-- decoding +function base64_decode(data) +    data = string.gsub(data, '[^'..base64_chars..'=]', '') +    return (data:gsub('.', function(x) +        if (x == '=') then return '' end +        local r,f='',(base64_chars:find(x)-1) +        for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end +        return r; +    end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x) +        if (#x ~= 8) then return '' end +        local c=0 +        for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end +        return string.char(c) +    end)) +end +local emit_event +emit_event = function(event_name, ...) +  return mp.commandv("script-message", "webm-" .. tostring(event_name), ...) +end +local test_set_options +test_set_options = function(new_options_json) +  local new_options = utils.parse_json(new_options_json) +  for k, v in pairs(new_options) do +    options[k] = v +  end +end +mp.register_script_message("mpv-webm-set-options", test_set_options) +local bold +bold = function(text) +  return "{\\b1}" .. tostring(text) .. "{\\b0}" +end +local message +message = function(text, duration) +  local ass = mp.get_property_osd("osd-ass-cc/0") +  ass = ass .. text +  return mp.osd_message(ass, duration or options.message_duration) +end +local append +append = function(a, b) +  for _, val in ipairs(b) do +    a[#a + 1] = val +  end +  return a +end +local seconds_to_time_string +seconds_to_time_string = function(seconds, no_ms, full) +  if seconds < 0 then +    return "unknown" +  end +  local ret = "" +  if not (no_ms) then +    ret = string.format(".%03d", seconds * 1000 % 1000) +  end +  ret = string.format("%02d:%02d%s", math.floor(seconds / 60) % 60, math.floor(seconds) % 60, ret) +  if full or seconds > 3600 then +    ret = string.format("%d:%s", math.floor(seconds / 3600), ret) +  end +  return ret +end +local seconds_to_path_element +seconds_to_path_element = function(seconds, no_ms, full) +  local time_string = seconds_to_time_string(seconds, no_ms, full) +  local _ +  time_string, _ = time_string:gsub(":", ".") +  return time_string +end +local file_exists +file_exists = function(name) +  local info, err = utils.file_info(name) +  if info ~= nil then +    return true +  end +  return false +end +local expand_properties +expand_properties = function(text, magic) +  if magic == nil then +    magic = "$" +  end +  for prefix, raw, prop, colon, fallback, closing in text:gmatch("%" .. magic .. "{([?!]?)(=?)([^}:]*)(:?)([^}]*)(}*)}") do +    local err +    local prop_value +    local compare_value +    local original_prop = prop +    local get_property = mp.get_property_osd +    if raw == "=" then +      get_property = mp.get_property +    end +    if prefix ~= "" then +      for actual_prop, compare in prop:gmatch("(.-)==(.*)") do +        prop = actual_prop +        compare_value = compare +      end +    end +    if colon == ":" then +      prop_value, err = get_property(prop, fallback) +    else +      prop_value, err = get_property(prop, "(error)") +    end +    prop_value = tostring(prop_value) +    if prefix == "?" then +      if compare_value == nil then +        prop_value = err == nil and fallback .. closing or "" +      else +        prop_value = prop_value == compare_value and fallback .. closing or "" +      end +      prefix = "%" .. prefix +    elseif prefix == "!" then +      if compare_value == nil then +        prop_value = err ~= nil and fallback .. closing or "" +      else +        prop_value = prop_value ~= compare_value and fallback .. closing or "" +      end +    else +      prop_value = prop_value .. closing +    end +    if colon == ":" then +      local _ +      text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. ":" .. fallback:gsub("%W", "%%%1") .. closing .. "}", expand_properties(prop_value)) +    else +      local _ +      text, _ = text:gsub("%" .. magic .. "{" .. prefix .. raw .. original_prop:gsub("%W", "%%%1") .. closing .. "}", prop_value) +    end +  end +  return text +end +local format_filename +format_filename = function(startTime, endTime, videoFormat) +  local hasAudioCodec = videoFormat.audioCodec ~= "" +  local replaceFirst = { +    ["%%mp"] = "%%mH.%%mM.%%mS", +    ["%%mP"] = "%%mH.%%mM.%%mS.%%mT", +    ["%%p"] = "%%wH.%%wM.%%wS", +    ["%%P"] = "%%wH.%%wM.%%wS.%%wT" +  } +  local replaceTable = { +    ["%%wH"] = string.format("%02d", math.floor(startTime / (60 * 60))), +    ["%%wh"] = string.format("%d", math.floor(startTime / (60 * 60))), +    ["%%wM"] = string.format("%02d", math.floor(startTime / 60 % 60)), +    ["%%wm"] = string.format("%d", math.floor(startTime / 60)), +    ["%%wS"] = string.format("%02d", math.floor(startTime % 60)), +    ["%%ws"] = string.format("%d", math.floor(startTime)), +    ["%%wf"] = string.format("%s", startTime), +    ["%%wT"] = string.sub(string.format("%.3f", startTime % 1), 3), +    ["%%mH"] = string.format("%02d", math.floor(endTime / (60 * 60))), +    ["%%mh"] = string.format("%d", math.floor(endTime / (60 * 60))), +    ["%%mM"] = string.format("%02d", math.floor(endTime / 60 % 60)), +    ["%%mm"] = string.format("%d", math.floor(endTime / 60)), +    ["%%mS"] = string.format("%02d", math.floor(endTime % 60)), +    ["%%ms"] = string.format("%d", math.floor(endTime)), +    ["%%mf"] = string.format("%s", endTime), +    ["%%mT"] = string.sub(string.format("%.3f", endTime % 1), 3), +    ["%%f"] = mp.get_property("filename"), +    ["%%F"] = mp.get_property("filename/no-ext"), +    ["%%s"] = seconds_to_path_element(startTime), +    ["%%S"] = seconds_to_path_element(startTime, true), +    ["%%e"] = seconds_to_path_element(endTime), +    ["%%E"] = seconds_to_path_element(endTime, true), +    ["%%T"] = mp.get_property("media-title"), +    ["%%M"] = (mp.get_property_native('aid') and not mp.get_property_native('mute') and hasAudioCodec) and '-audio' or '', +    ["%%R"] = (options.scale_height ~= -1) and "-" .. tostring(options.scale_height) .. "p" or "-" .. tostring(mp.get_property_native('height')) .. "p", +    ["%%t%%"] = "%%" +  } +  local filename = options.output_template +  for format, value in pairs(replaceFirst) do +    local _ +    filename, _ = filename:gsub(format, value) +  end +  for format, value in pairs(replaceTable) do +    local _ +    filename, _ = filename:gsub(format, value) +  end +  if mp.get_property_bool("demuxer-via-network", false) then +    local _ +    filename, _ = filename:gsub("%%X{([^}]*)}", "%1") +    filename, _ = filename:gsub("%%x", "") +  else +    local x = string.gsub(mp.get_property("stream-open-filename", ""), string.gsub(mp.get_property("filename", ""), "%W", "%%%1") .. "$", "") +    local _ +    filename, _ = filename:gsub("%%X{[^}]*}", x) +    filename, _ = filename:gsub("%%x", x) +  end +  filename = expand_properties(filename, "%") +  for format in filename:gmatch("%%t([aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ])") do +    local _ +    filename, _ = filename:gsub("%%t" .. format, os.date("%" .. format)) +  end +  local _ +  filename, _ = filename:gsub("[<>:\"/\\|?*]", "") +  return tostring(filename) .. "." .. tostring(videoFormat.outputExtension) +end +local parse_directory +parse_directory = function(dir) +  local home_dir = os.getenv("HOME") +  if not home_dir then +    home_dir = os.getenv("USERPROFILE") +  end +  if not home_dir then +    local drive = os.getenv("HOMEDRIVE") +    local path = os.getenv("HOMEPATH") +    if drive and path then +      home_dir = utils.join_path(drive, path) +    else +      msg.warn("Couldn't find home dir.") +      home_dir = "" +    end +  end +  local _ +  dir, _ = dir:gsub("^~", home_dir) +  return dir +end +local is_windows = type(package) == "table" and type(package.config) == "string" and package.config:sub(1, 1) == "\\" +local trim +trim = function(s) +  return s:match("^%s*(.-)%s*$") +end +local get_null_path +get_null_path = function() +  if file_exists("/dev/null") then +    return "/dev/null" +  end +  return "NUL" +end +local run_subprocess +run_subprocess = function(params) +  local res = utils.subprocess(params) +  msg.verbose("Command stdout: ") +  msg.verbose(res.stdout) +  if res.status ~= 0 then +    msg.verbose("Command failed! Reason: ", res.error, " Killed by us? ", res.killed_by_us and "yes" or "no") +    return false +  end +  return true +end +local shell_escape +shell_escape = function(args) +  local ret = { } +  for i, a in ipairs(args) do +    local s = tostring(a) +    if string.match(s, "[^A-Za-z0-9_/:=-]") then +      if is_windows then +        s = '"' .. string.gsub(s, '"', '"\\""') .. '"' +      else +        s = "'" .. string.gsub(s, "'", "'\\''") .. "'" +      end +    end +    table.insert(ret, s) +  end +  local concat = table.concat(ret, " ") +  if is_windows then +    concat = '"' .. concat .. '"' +  end +  return concat +end +local run_subprocess_popen +run_subprocess_popen = function(command_line) +  local command_line_string = shell_escape(command_line) +  command_line_string = command_line_string .. " 2>&1" +  msg.verbose("run_subprocess_popen: running " .. tostring(command_line_string)) +  return io.popen(command_line_string) +end +local calculate_scale_factor +calculate_scale_factor = function() +  local baseResY = 720 +  local osd_w, osd_h = mp.get_osd_size() +  return osd_h / baseResY +end +local should_display_progress +should_display_progress = function() +  if options.display_progress == "auto" then +    return not is_windows +  end +  return options.display_progress +end +local reverse +reverse = function(list) +  local _accum_0 = { } +  local _len_0 = 1 +  local _max_0 = 1 +  for _index_0 = #list, _max_0 < 0 and #list + _max_0 or _max_0, -1 do +    local element = list[_index_0] +    _accum_0[_len_0] = element +    _len_0 = _len_0 + 1 +  end +  return _accum_0 +end +local get_pass_logfile_path +get_pass_logfile_path = function(encode_out_path) +  return tostring(encode_out_path) .. "-video-pass1.log" +end +local dimensions_changed = true +local _video_dimensions = { } +local get_video_dimensions +get_video_dimensions = function() +  if not (dimensions_changed) then +    return _video_dimensions +  end +  local video_params = mp.get_property_native("video-out-params") +  if not video_params then +    return nil +  end +  dimensions_changed = false +  local keep_aspect = mp.get_property_bool("keepaspect") +  local w = video_params["w"] +  local h = video_params["h"] +  local dw = video_params["dw"] +  local dh = video_params["dh"] +  if mp.get_property_number("video-rotate") % 180 == 90 then +    w, h = h, w +    dw, dh = dh, dw +  end +  _video_dimensions = { +    top_left = { }, +    bottom_right = { }, +    ratios = { } +  } +  local window_w, window_h = mp.get_osd_size() +  if keep_aspect then +    local unscaled = mp.get_property_native("video-unscaled") +    local panscan = mp.get_property_number("panscan") +    local fwidth = window_w +    local fheight = math.floor(window_w / dw * dh) +    if fheight > window_h or fheight < h then +      local tmpw = math.floor(window_h / dh * dw) +      if tmpw <= window_w then +        fheight = window_h +        fwidth = tmpw +      end +    end +    local vo_panscan_area = window_h - fheight +    local f_w = fwidth / fheight +    local f_h = 1 +    if vo_panscan_area == 0 then +      vo_panscan_area = window_h - fwidth +      f_w = 1 +      f_h = fheight / fwidth +    end +    if unscaled or unscaled == "downscale-big" then +      vo_panscan_area = 0 +      if unscaled or (dw <= window_w and dh <= window_h) then +        fwidth = dw +        fheight = dh +      end +    end +    local scaled_width = fwidth + math.floor(vo_panscan_area * panscan * f_w) +    local scaled_height = fheight + math.floor(vo_panscan_area * panscan * f_h) +    local split_scaling +    split_scaling = function(dst_size, scaled_src_size, zoom, align, pan) +      scaled_src_size = math.floor(scaled_src_size * 2 ^ zoom) +      align = (align + 1) / 2 +      local dst_start = math.floor((dst_size - scaled_src_size) * align + pan * scaled_src_size) +      if dst_start < 0 then +        dst_start = dst_start + 1 +      end +      local dst_end = dst_start + scaled_src_size +      if dst_start >= dst_end then +        dst_start = 0 +        dst_end = 1 +      end +      return dst_start, dst_end +    end +    local zoom = mp.get_property_number("video-zoom") +    local align_x = mp.get_property_number("video-align-x") +    local pan_x = mp.get_property_number("video-pan-x") +    _video_dimensions.top_left.x, _video_dimensions.bottom_right.x = split_scaling(window_w, scaled_width, zoom, align_x, pan_x) +    local align_y = mp.get_property_number("video-align-y") +    local pan_y = mp.get_property_number("video-pan-y") +    _video_dimensions.top_left.y, _video_dimensions.bottom_right.y = split_scaling(window_h, scaled_height, zoom, align_y, pan_y) +  else +    _video_dimensions.top_left.x = 0 +    _video_dimensions.bottom_right.x = window_w +    _video_dimensions.top_left.y = 0 +    _video_dimensions.bottom_right.y = window_h +  end +  _video_dimensions.ratios.w = w / (_video_dimensions.bottom_right.x - _video_dimensions.top_left.x) +  _video_dimensions.ratios.h = h / (_video_dimensions.bottom_right.y - _video_dimensions.top_left.y) +  return _video_dimensions +end +local set_dimensions_changed +set_dimensions_changed = function() +  dimensions_changed = true +end +local monitor_dimensions +monitor_dimensions = function() +  local properties = { +    "keepaspect", +    "video-out-params", +    "video-unscaled", +    "panscan", +    "video-zoom", +    "video-align-x", +    "video-pan-x", +    "video-align-y", +    "video-pan-y", +    "osd-width", +    "osd-height" +  } +  for _, p in ipairs(properties) do +    mp.observe_property(p, "native", set_dimensions_changed) +  end +end +local clamp +clamp = function(min, val, max) +  if val <= min then +    return min +  end +  if val >= max then +    return max +  end +  return val +end +local clamp_point +clamp_point = function(top_left, point, bottom_right) +  return { +    x = clamp(top_left.x, point.x, bottom_right.x), +    y = clamp(top_left.y, point.y, bottom_right.y) +  } +end +local VideoPoint +do +  local _class_0 +  local _base_0 = { +    set_from_screen = function(self, sx, sy) +      local d = get_video_dimensions() +      local point = clamp_point(d.top_left, { +        x = sx, +        y = sy +      }, d.bottom_right) +      self.x = math.floor(d.ratios.w * (point.x - d.top_left.x) + 0.5) +      self.y = math.floor(d.ratios.h * (point.y - d.top_left.y) + 0.5) +    end, +    to_screen = function(self) +      local d = get_video_dimensions() +      return { +        x = math.floor(self.x / d.ratios.w + d.top_left.x + 0.5), +        y = math.floor(self.y / d.ratios.h + d.top_left.y + 0.5) +      } +    end +  } +  _base_0.__index = _base_0 +  _class_0 = setmetatable({ +    __init = function(self) +      self.x = -1 +      self.y = -1 +    end, +    __base = _base_0, +    __name = "VideoPoint" +  }, { +    __index = _base_0, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  VideoPoint = _class_0 +end +local Region +do +  local _class_0 +  local _base_0 = { +    is_valid = function(self) +      return self.x > -1 and self.y > -1 and self.w > -1 and self.h > -1 +    end, +    set_from_points = function(self, p1, p2) +      self.x = math.min(p1.x, p2.x) +      self.y = math.min(p1.y, p2.y) +      self.w = math.abs(p1.x - p2.x) +      self.h = math.abs(p1.y - p2.y) +    end +  } +  _base_0.__index = _base_0 +  _class_0 = setmetatable({ +    __init = function(self) +      self.x = -1 +      self.y = -1 +      self.w = -1 +      self.h = -1 +    end, +    __base = _base_0, +    __name = "Region" +  }, { +    __index = _base_0, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  Region = _class_0 +end +local make_fullscreen_region +make_fullscreen_region = function() +  local r = Region() +  local d = get_video_dimensions() +  local a = VideoPoint() +  local b = VideoPoint() +  local xa, ya +  do +    local _obj_0 = d.top_left +    xa, ya = _obj_0.x, _obj_0.y +  end +  a:set_from_screen(xa, ya) +  local xb, yb +  do +    local _obj_0 = d.bottom_right +    xb, yb = _obj_0.x, _obj_0.y +  end +  b:set_from_screen(xb, yb) +  r:set_from_points(a, b) +  return r +end +local read_double +read_double = function(bytes) +  local sign = 1 +  local mantissa = bytes[2] % 2 ^ 4 +  for i = 3, 8 do +    mantissa = mantissa * 256 + bytes[i] +  end +  if bytes[1] > 127 then +    sign = -1 +  end +  local exponent = (bytes[1] % 128) * 2 ^ 4 + math.floor(bytes[2] / 2 ^ 4) +  if exponent == 0 then +    return 0 +  end +  mantissa = (math.ldexp(mantissa, -52) + 1) * sign +  return math.ldexp(mantissa, exponent - 1023) +end +local write_double +write_double = function(num) +  local bytes = { +    0, +    0, +    0, +    0, +    0, +    0, +    0, +    0 +  } +  if num == 0 then +    return bytes +  end +  local anum = math.abs(num) +  local mantissa, exponent = math.frexp(anum) +  exponent = exponent - 1 +  mantissa = mantissa * 2 - 1 +  local sign = num ~= anum and 128 or 0 +  exponent = exponent + 1023 +  bytes[1] = sign + math.floor(exponent / 2 ^ 4) +  mantissa = mantissa * 2 ^ 4 +  local currentmantissa = math.floor(mantissa) +  mantissa = mantissa - currentmantissa +  bytes[2] = (exponent % 2 ^ 4) * 2 ^ 4 + currentmantissa +  for i = 3, 8 do +    mantissa = mantissa * 2 ^ 8 +    currentmantissa = math.floor(mantissa) +    mantissa = mantissa - currentmantissa +    bytes[i] = currentmantissa +  end +  return bytes +end +local FirstpassStats +do +  local _class_0 +  local duration_multiplier, fields_before_duration, fields_after_duration +  local _base_0 = { +    get_duration = function(self) +      local big_endian_binary_duration = reverse(self.binary_duration) +      return read_double(reversed_binary_duration) / duration_multiplier +    end, +    set_duration = function(self, duration) +      local big_endian_binary_duration = write_double(duration * duration_multiplier) +      self.binary_duration = reverse(big_endian_binary_duration) +    end, +    _bytes_to_string = function(self, bytes) +      return string.char(unpack(bytes)) +    end, +    as_binary_string = function(self) +      local before_duration_string = self:_bytes_to_string(self.binary_data_before_duration) +      local duration_string = self:_bytes_to_string(self.binary_duration) +      local after_duration_string = self:_bytes_to_string(self.binary_data_after_duration) +      return before_duration_string .. duration_string .. after_duration_string +    end +  } +  _base_0.__index = _base_0 +  _class_0 = setmetatable({ +    __init = function(self, before_duration, duration, after_duration) +      self.binary_data_before_duration = before_duration +      self.binary_duration = duration +      self.binary_data_after_duration = after_duration +    end, +    __base = _base_0, +    __name = "FirstpassStats" +  }, { +    __index = _base_0, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  local self = _class_0 +  duration_multiplier = 10000000.0 +  fields_before_duration = 16 +  fields_after_duration = 1 +  self.data_before_duration_size = function(self) +    return fields_before_duration * 8 +  end +  self.data_after_duration_size = function(self) +    return fields_after_duration * 8 +  end +  self.size = function(self) +    return (fields_before_duration + 1 + fields_after_duration) * 8 +  end +  self.from_bytes = function(self, bytes) +    local before_duration +    do +      local _accum_0 = { } +      local _len_0 = 1 +      local _max_0 = self:data_before_duration_size() +      for _index_0 = 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do +        local b = bytes[_index_0] +        _accum_0[_len_0] = b +        _len_0 = _len_0 + 1 +      end +      before_duration = _accum_0 +    end +    local duration +    do +      local _accum_0 = { } +      local _len_0 = 1 +      local _max_0 = self:data_before_duration_size() + 8 +      for _index_0 = self:data_before_duration_size() + 1, _max_0 < 0 and #bytes + _max_0 or _max_0 do +        local b = bytes[_index_0] +        _accum_0[_len_0] = b +        _len_0 = _len_0 + 1 +      end +      duration = _accum_0 +    end +    local after_duration +    do +      local _accum_0 = { } +      local _len_0 = 1 +      for _index_0 = self:data_before_duration_size() + 8 + 1, #bytes do +        local b = bytes[_index_0] +        _accum_0[_len_0] = b +        _len_0 = _len_0 + 1 +      end +      after_duration = _accum_0 +    end +    return self(before_duration, duration, after_duration) +  end +  FirstpassStats = _class_0 +end +local read_logfile_into_stats_array +read_logfile_into_stats_array = function(logfile_path) +  local file = assert(io.open(logfile_path, "rb")) +  local logfile_string = base64_decode(file:read()) +  file:close() +  local stats_size = FirstpassStats:size() +  assert(logfile_string:len() % stats_size == 0) +  local stats = { } +  for offset = 1, #logfile_string, stats_size do +    local bytes = { +      logfile_string:byte(offset, offset + stats_size - 1) +    } +    assert(#bytes == stats_size) +    stats[#stats + 1] = FirstpassStats:from_bytes(bytes) +  end +  return stats +end +local write_stats_array_to_logfile +write_stats_array_to_logfile = function(stats_array, logfile_path) +  local file = assert(io.open(logfile_path, "wb")) +  local logfile_string = "" +  for _index_0 = 1, #stats_array do +    local stat = stats_array[_index_0] +    logfile_string = logfile_string .. stat:as_binary_string() +  end +  file:write(base64_encode(logfile_string)) +  return file:close() +end +local vp8_patch_logfile +vp8_patch_logfile = function(logfile_path, encode_total_duration) +  local stats_array = read_logfile_into_stats_array(logfile_path) +  local average_duration = encode_total_duration / (#stats_array - 1) +  for i = 1, #stats_array - 1 do +    stats_array[i]:set_duration(average_duration) +  end +  stats_array[#stats_array]:set_duration(encode_total_duration) +  return write_stats_array_to_logfile(stats_array, logfile_path) +end +local formats = { } +local Format +do +  local _class_0 +  local _base_0 = { +    getPreFilters = function(self) +      return { } +    end, +    getPostFilters = function(self) +      return { } +    end, +    getFlags = function(self) +      return { } +    end, +    getCodecFlags = function(self) +      local codecs = { } +      if self.videoCodec ~= "" then +        codecs[#codecs + 1] = "--ovc=" .. tostring(self.videoCodec) +      end +      if self.audioCodec ~= "" then +        codecs[#codecs + 1] = "--oac=" .. tostring(self.audioCodec) +      end +      return codecs +    end, +    postCommandModifier = function(self, command, region, startTime, endTime) +      return command +    end +  } +  _base_0.__index = _base_0 +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "Basic" +      self.supportsTwopass = true +      self.videoCodec = "" +      self.audioCodec = "" +      self.outputExtension = "" +      self.acceptsBitrate = true +    end, +    __base = _base_0, +    __name = "Format" +  }, { +    __index = _base_0, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  Format = _class_0 +end +local RawVideo +do +  local _class_0 +  local _parent_0 = Format +  local _base_0 = { +    getColorspace = function(self) +      local csp = mp.get_property("colormatrix") +      local _exp_0 = csp +      if "bt.601" == _exp_0 then +        return "bt601" +      elseif "bt.709" == _exp_0 then +        return "bt709" +      elseif "bt.2020" == _exp_0 then +        return "bt2020" +      elseif "smpte-240m" == _exp_0 then +        return "smpte240m" +      else +        msg.info("Warning, unknown colorspace " .. tostring(csp) .. " detected, using bt.601.") +        return "bt601" +      end +    end, +    getPostFilters = function(self) +      return { +        "format=yuv444p16", +        "lavfi-scale=in_color_matrix=" .. self:getColorspace(), +        "format=bgr24" +      } +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "Raw" +      self.supportsTwopass = false +      self.videoCodec = "rawvideo" +      self.audioCodec = "pcm_s16le" +      self.outputExtension = "avi" +      self.acceptsBitrate = false +    end, +    __base = _base_0, +    __name = "RawVideo", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  RawVideo = _class_0 +end +formats["raw"] = RawVideo() +local WebmVP8 +do +  local _class_0 +  local _parent_0 = Format +  local _base_0 = { +    getPreFilters = function(self) +      local colormatrixFilter = { +        ["bt.709"] = "bt709", +        ["bt.2020"] = "bt2020", +        ["smpte-240m"] = "smpte240m" +      } +      local ret = { } +      local colormatrix = mp.get_property_native("video-params/colormatrix") +      if colormatrixFilter[colormatrix] then +        append(ret, { +          "lavfi-colormatrix=" .. tostring(colormatrixFilter[colormatrix]) .. ":bt601" +        }) +      end +      return ret +    end, +    getFlags = function(self) +      return { +        "--ovcopts-add=threads=" .. tostring(options.libvpx_threads), +        "--ovcopts-add=auto-alt-ref=1", +        "--ovcopts-add=lag-in-frames=25", +        "--ovcopts-add=quality=good", +        "--ovcopts-add=cpu-used=0" +      } +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "WebM" +      self.supportsTwopass = true +      self.videoCodec = "libvpx" +      self.audioCodec = "libvorbis" +      self.outputExtension = "webm" +      self.acceptsBitrate = true +    end, +    __base = _base_0, +    __name = "WebmVP8", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  WebmVP8 = _class_0 +end +formats["webm-vp8"] = WebmVP8() +local WebmVP9 +do +  local _class_0 +  local _parent_0 = Format +  local _base_0 = { +    getFlags = function(self) +      return { +        "--ovcopts-add=threads=" .. tostring(options.libvpx_threads) +      } +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "WebM (VP9)" +      self.supportsTwopass = false +      self.videoCodec = "libvpx-vp9" +      self.audioCodec = "libopus" +      self.outputExtension = "webm" +      self.acceptsBitrate = true +    end, +    __base = _base_0, +    __name = "WebmVP9", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  WebmVP9 = _class_0 +end +formats["webm-vp9"] = WebmVP9() +local MP4 +do +  local _class_0 +  local _parent_0 = Format +  local _base_0 = { } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "MP4 (h264/AAC)" +      self.supportsTwopass = true +      self.videoCodec = "libx264" +      self.audioCodec = "aac" +      self.outputExtension = "mp4" +      self.acceptsBitrate = true +    end, +    __base = _base_0, +    __name = "MP4", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  MP4 = _class_0 +end +formats["mp4"] = MP4() +local MP4NVENC +do +  local _class_0 +  local _parent_0 = Format +  local _base_0 = { } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "MP4 (h264-NVENC/AAC)" +      self.supportsTwopass = true +      self.videoCodec = "h264_nvenc" +      self.audioCodec = "aac" +      self.outputExtension = "mp4" +      self.acceptsBitrate = true +    end, +    __base = _base_0, +    __name = "MP4NVENC", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  MP4NVENC = _class_0 +end +formats["mp4-nvenc"] = MP4NVENC() +local MP3 +do +  local _class_0 +  local _parent_0 = Format +  local _base_0 = { } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "MP3 (libmp3lame)" +      self.supportsTwopass = false +      self.videoCodec = "" +      self.audioCodec = "libmp3lame" +      self.outputExtension = "mp3" +      self.acceptsBitrate = true +    end, +    __base = _base_0, +    __name = "MP3", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  MP3 = _class_0 +end +formats["mp3"] = MP3() +local GIF +do +  local _class_0 +  local _parent_0 = Format +  local _base_0 = { +    postCommandModifier = function(self, command, region, startTime, endTime) +      local new_command = { } +      local start_ts = seconds_to_time_string(startTime, false, true) +      local end_ts = seconds_to_time_string(endTime, false, true) +      start_ts = start_ts:gsub(":", "\\\\:") +      end_ts = end_ts:gsub(":", "\\\\:") +      local cfilter = "[vid1]trim=start=" .. tostring(start_ts) .. ":end=" .. tostring(end_ts) .. "[vidtmp];" +      if mp.get_property("deinterlace") == "yes" then +        cfilter = cfilter .. "[vidtmp]yadif=mode=1[vidtmp];" +      end +      for _, v in ipairs(command) do +        local _continue_0 = false +        repeat +          if v:match("^%-%-vf%-add=lavfi%-scale") or v:match("^%-%-vf%-add=lavfi%-crop") or v:match("^%-%-vf%-add=fps") or v:match("^%-%-vf%-add=lavfi%-eq") then +            local n = v:gsub("^%-%-vf%-add=", ""):gsub("^lavfi%-", "") +            cfilter = cfilter .. "[vidtmp]" .. tostring(n) .. "[vidtmp];" +          else +            if v:match("^%-%-video%-rotate=90") then +              cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];" +            else +              if v:match("^%-%-video%-rotate=270") then +                cfilter = cfilter .. "[vidtmp]transpose=2[vidtmp];" +              else +                if v:match("^%-%-video%-rotate=180") then +                  cfilter = cfilter .. "[vidtmp]transpose=1[vidtmp];[vidtmp]transpose=1[vidtmp];" +                else +                  if v:match("^%-%-deinterlace=") then +                    _continue_0 = true +                    break +                  else +                    append(new_command, { +                      v +                    }) +                    _continue_0 = true +                    break +                  end +                end +              end +            end +          end +          _continue_0 = true +        until true +        if not _continue_0 then +          break +        end +      end +      cfilter = cfilter .. "[vidtmp]split[topal][vidf];" +      cfilter = cfilter .. "[topal]palettegen[pal];" +      cfilter = cfilter .. "[vidf]fifo[vidf];" +      if options.gif_dither == 6 then +        cfilter = cfilter .. "[vidf][pal]paletteuse[vo]" +      else +        cfilter = cfilter .. "[vidf][pal]paletteuse=dither=bayer:bayer_scale=" .. tostring(options.gif_dither) .. ":diff_mode=rectangle[vo]" +      end +      append(new_command, { +        "--lavfi-complex=" .. tostring(cfilter) +      }) +      return new_command +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.displayName = "GIF" +      self.supportsTwopass = false +      self.videoCodec = "gif" +      self.audioCodec = "" +      self.outputExtension = "gif" +      self.acceptsBitrate = false +    end, +    __base = _base_0, +    __name = "GIF", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  GIF = _class_0 +end +formats["gif"] = GIF() +local Page +do +  local _class_0 +  local _base_0 = { +    add_keybinds = function(self) +      if not self.keybinds then +        return  +      end +      for key, func in pairs(self.keybinds) do +        mp.add_forced_key_binding(key, key, func, { +          repeatable = true +        }) +      end +    end, +    remove_keybinds = function(self) +      if not self.keybinds then +        return  +      end +      for key, _ in pairs(self.keybinds) do +        mp.remove_key_binding(key) +      end +    end, +    observe_properties = function(self) +      self.sizeCallback = function() +        return self:draw() +      end +      local properties = { +        "keepaspect", +        "video-out-params", +        "video-unscaled", +        "panscan", +        "video-zoom", +        "video-align-x", +        "video-pan-x", +        "video-align-y", +        "video-pan-y", +        "osd-width", +        "osd-height" +      } +      for _index_0 = 1, #properties do +        local p = properties[_index_0] +        mp.observe_property(p, "native", self.sizeCallback) +      end +    end, +    unobserve_properties = function(self) +      if self.sizeCallback then +        mp.unobserve_property(self.sizeCallback) +        self.sizeCallback = nil +      end +    end, +    clear = function(self) +      local window_w, window_h = mp.get_osd_size() +      mp.set_osd_ass(window_w, window_h, "") +      return mp.osd_message("", 0) +    end, +    prepare = function(self) +      return nil +    end, +    dispose = function(self) +      return nil +    end, +    show = function(self) +      if self.visible then +        return  +      end +      self.visible = true +      self:observe_properties() +      self:add_keybinds() +      self:prepare() +      self:clear() +      return self:draw() +    end, +    hide = function(self) +      if not self.visible then +        return  +      end +      self.visible = false +      self:unobserve_properties() +      self:remove_keybinds() +      self:clear() +      return self:dispose() +    end, +    setup_text = function(self, ass) +      local scale = calculate_scale_factor() +      local margin = options.margin * scale +      ass:append("{\\an7}") +      ass:pos(margin, margin) +      return ass:append("{\\fs" .. tostring(options.font_size * scale) .. "}") +    end +  } +  _base_0.__index = _base_0 +  _class_0 = setmetatable({ +    __init = function() end, +    __base = _base_0, +    __name = "Page" +  }, { +    __index = _base_0, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  Page = _class_0 +end +local EncodeWithProgress +do +  local _class_0 +  local _parent_0 = Page +  local _base_0 = { +    draw = function(self) +      local progress = 100 * ((self.currentTime - self.startTime) / self.duration) +      local progressText = string.format("%d%%", progress) +      local window_w, window_h = mp.get_osd_size() +      local ass = assdraw.ass_new() +      ass:new_event() +      self:setup_text(ass) +      ass:append("Encoding (" .. tostring(bold(progressText)) .. ")\\N") +      return mp.set_osd_ass(window_w, window_h, ass.text) +    end, +    parseLine = function(self, line) +      local matchTime = string.match(line, "Encode time[-]pos: ([0-9.]+)") +      local matchExit = string.match(line, "Exiting... [(]([%a ]+)[)]") +      if matchTime == nil and matchExit == nil then +        return  +      end +      if matchTime ~= nil and tonumber(matchTime) > self.currentTime then +        self.currentTime = tonumber(matchTime) +      end +      if matchExit ~= nil then +        self.finished = true +        self.finishedReason = matchExit +      end +    end, +    startEncode = function(self, command_line) +      local copy_command_line +      do +        local _accum_0 = { } +        local _len_0 = 1 +        for _index_0 = 1, #command_line do +          local arg = command_line[_index_0] +          _accum_0[_len_0] = arg +          _len_0 = _len_0 + 1 +        end +        copy_command_line = _accum_0 +      end +      append(copy_command_line, { +        '--term-status-msg=Encode time-pos: ${=time-pos}\\n' +      }) +      self:show() +      local processFd = run_subprocess_popen(copy_command_line) +      for line in processFd:lines() do +        msg.verbose(string.format('%q', line)) +        self:parseLine(line) +        self:draw() +      end +      processFd:close() +      self:hide() +      if self.finishedReason == "End of file" then +        return true +      end +      return false +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self, startTime, endTime) +      self.startTime = startTime +      self.endTime = endTime +      self.duration = endTime - startTime +      self.currentTime = startTime +    end, +    __base = _base_0, +    __name = "EncodeWithProgress", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  EncodeWithProgress = _class_0 +end +local get_active_tracks +get_active_tracks = function() +  local accepted = { +    video = true, +    audio = not mp.get_property_bool("mute"), +    sub = mp.get_property_bool("sub-visibility") +  } +  local active = { +    video = { }, +    audio = { }, +    sub = { } +  } +  for _, track in ipairs(mp.get_property_native("track-list")) do +    if track["selected"] and accepted[track["type"]] then +      local count = #active[track["type"]] +      active[track["type"]][count + 1] = track +    end +  end +  return active +end +local filter_tracks_supported_by_format +filter_tracks_supported_by_format = function(active_tracks, format) +  local has_video_codec = format.videoCodec ~= "" +  local has_audio_codec = format.audioCodec ~= "" +  local supported = { +    video = has_video_codec and active_tracks["video"] or { }, +    audio = has_audio_codec and active_tracks["audio"] or { }, +    sub = has_video_codec and active_tracks["sub"] or { } +  } +  return supported +end +local append_track +append_track = function(out, track) +  local external_flag = { +    ["audio"] = "audio-file", +    ["sub"] = "sub-file" +  } +  local internal_flag = { +    ["video"] = "vid", +    ["audio"] = "aid", +    ["sub"] = "sid" +  } +  if track['external'] and string.len(track['external-filename']) <= 2048 then +    return append(out, { +      "--" .. tostring(external_flag[track['type']]) .. "=" .. tostring(track['external-filename']) +    }) +  else +    return append(out, { +      "--" .. tostring(internal_flag[track['type']]) .. "=" .. tostring(track['id']) +    }) +  end +end +local append_audio_tracks +append_audio_tracks = function(out, tracks) +  local internal_tracks = { } +  for _index_0 = 1, #tracks do +    local track = tracks[_index_0] +    if track['external'] then +      append_track(out, track) +    else +      append(internal_tracks, { +        track +      }) +    end +  end +  if #internal_tracks > 1 then +    local filter_string = "" +    for _index_0 = 1, #internal_tracks do +      local track = internal_tracks[_index_0] +      filter_string = filter_string .. "[aid" .. tostring(track['id']) .. "]" +    end +    filter_string = filter_string .. "amix[ao]" +    return append(out, { +      "--lavfi-complex=" .. tostring(filter_string) +    }) +  else +    if #internal_tracks == 1 then +      return append_track(out, internal_tracks[1]) +    end +  end +end +local get_scale_filters +get_scale_filters = function() +  local filters = { } +  if options.force_square_pixels then +    append(filters, { +      "lavfi-scale=iw*sar:ih" +    }) +  end +  if options.scale_height > 0 then +    append(filters, { +      "lavfi-scale=-2:" .. tostring(options.scale_height) +    }) +  end +  return filters +end +local get_fps_filters +get_fps_filters = function() +  if options.fps > 0 then +    return { +      "fps=" .. tostring(options.fps) +    } +  end +  return { } +end +local get_contrast_brightness_and_saturation_filters +get_contrast_brightness_and_saturation_filters = function() +  local mpv_brightness = mp.get_property("brightness") +  local mpv_contrast = mp.get_property("contrast") +  local mpv_saturation = mp.get_property("saturation") +  if mpv_brightness == 0 and mpv_contrast == 0 and mpv_saturation == 0 then +    return { } +  end +  local eq_saturation = (mpv_saturation + 100) / 100.0 +  local eq_contrast = (mpv_contrast + 100) / 100.0 +  local eq_brightness = (mpv_brightness / 50.0 + eq_contrast - 1) / 2.0 +  return { +    "lavfi-eq=contrast=" .. tostring(eq_contrast) .. ":saturation=" .. tostring(eq_saturation) .. ":brightness=" .. tostring(eq_brightness) +  } +end +local append_property +append_property = function(out, property_name, option_name) +  option_name = option_name or property_name +  local prop = mp.get_property(property_name) +  if prop and prop ~= "" then +    return append(out, { +      "--" .. tostring(option_name) .. "=" .. tostring(prop) +    }) +  end +end +local append_list_options +append_list_options = function(out, property_name, option_prefix) +  option_prefix = option_prefix or property_name +  local prop = mp.get_property_native(property_name) +  if prop then +    for _index_0 = 1, #prop do +      local value = prop[_index_0] +      append(out, { +        "--" .. tostring(option_prefix) .. "-append=" .. tostring(value) +      }) +    end +  end +end +local get_playback_options +get_playback_options = function() +  local ret = { } +  append_property(ret, "sub-ass-override") +  append_property(ret, "sub-ass-force-style") +  append_property(ret, "sub-ass-vsfilter-aspect-compat") +  append_property(ret, "sub-auto") +  append_property(ret, "sub-pos") +  append_property(ret, "sub-delay") +  append_property(ret, "video-rotate") +  append_property(ret, "ytdl-format") +  append_property(ret, "deinterlace") +  return ret +end +local get_speed_flags +get_speed_flags = function() +  local ret = { } +  local speed = mp.get_property_native("speed") +  if speed ~= 1 then +    append(ret, { +      "--vf-add=setpts=PTS/" .. tostring(speed), +      "--af-add=atempo=" .. tostring(speed), +      "--sub-speed=1/" .. tostring(speed) +    }) +  end +  return ret +end +local get_metadata_flags +get_metadata_flags = function() +  local title = mp.get_property("filename/no-ext") +  return { +    "--oset-metadata=title=%" .. tostring(string.len(title)) .. "%" .. tostring(title) +  } +end +local apply_current_filters +apply_current_filters = function(filters) +  local vf = mp.get_property_native("vf") +  msg.verbose("apply_current_filters: got " .. tostring(#vf) .. " currently applied.") +  for _index_0 = 1, #vf do +    local _continue_0 = false +    repeat +      local filter = vf[_index_0] +      msg.verbose("apply_current_filters: filter name: " .. tostring(filter['name'])) +      if filter["enabled"] == false then +        _continue_0 = true +        break +      end +      local str = filter["name"] +      local params = filter["params"] or { } +      for k, v in pairs(params) do +        str = str .. ":" .. tostring(k) .. "=%" .. tostring(string.len(v)) .. "%" .. tostring(v) +      end +      append(filters, { +        str +      }) +      _continue_0 = true +    until true +    if not _continue_0 then +      break +    end +  end +end +local get_video_filters +get_video_filters = function(format, region) +  local filters = { } +  append(filters, format:getPreFilters()) +  if options.apply_current_filters then +    apply_current_filters(filters) +  end +  if region and region:is_valid() then +    append(filters, { +      "lavfi-crop=" .. tostring(region.w) .. ":" .. tostring(region.h) .. ":" .. tostring(region.x) .. ":" .. tostring(region.y) +    }) +  end +  append(filters, get_scale_filters()) +  append(filters, get_fps_filters()) +  append(filters, get_contrast_brightness_and_saturation_filters()) +  append(filters, format:getPostFilters()) +  return filters +end +local get_video_encode_flags +get_video_encode_flags = function(format, region) +  local flags = { } +  append(flags, get_playback_options()) +  local filters = get_video_filters(format, region) +  for _index_0 = 1, #filters do +    local f = filters[_index_0] +    append(flags, { +      "--vf-add=" .. tostring(f) +    }) +  end +  append(flags, get_speed_flags()) +  return flags +end +local calculate_bitrate +calculate_bitrate = function(active_tracks, format, length) +  if format.videoCodec == "" then +    return nil, options.target_filesize * 8 / length +  end +  local video_kilobits = options.target_filesize * 8 +  local audio_kilobits = nil +  local has_audio_track = #active_tracks["audio"] > 0 +  if options.strict_filesize_constraint and has_audio_track then +    audio_kilobits = length * options.strict_audio_bitrate +    video_kilobits = video_kilobits - audio_kilobits +  end +  local video_bitrate = math.floor(video_kilobits / length) +  local audio_bitrate = audio_kilobits and math.floor(audio_kilobits / length) or nil +  return video_bitrate, audio_bitrate +end +local find_path +find_path = function(startTime, endTime) +  local path = mp.get_property('path') +  if not path then +    return nil, nil, nil, nil, nil +  end +  local is_stream = not file_exists(path) +  local is_temporary = false +  if is_stream then +    if mp.get_property('file-format') == 'hls' then +      path = utils.join_path(parse_directory('~'), 'cache_dump.ts') +      mp.command_native({ +        'dump_cache', +        seconds_to_time_string(startTime, false, true), +        seconds_to_time_string(endTime + 5, false, true), +        path +      }) +      endTime = endTime - startTime +      startTime = 0 +      is_temporary = true +    end +  end +  return path, is_stream, is_temporary, startTime, endTime +end +local encode +encode = function(region, startTime, endTime) +  local format = formats[options.output_format] +  local originalStartTime = startTime +  local originalEndTime = endTime +  local path, is_stream, is_temporary +  path, is_stream, is_temporary, startTime, endTime = find_path(startTime, endTime) +  if not path then +    message("No file is being played") +    return  +  end +  local command = { +    "mpv", +    path, +    "--start=" .. seconds_to_time_string(startTime, false, true), +    "--end=" .. seconds_to_time_string(endTime, false, true), +    "--loop-file=no", +    "--no-pause" +  } +  append(command, format:getCodecFlags()) +  local active_tracks = get_active_tracks() +  local supported_active_tracks = filter_tracks_supported_by_format(active_tracks, format) +  for track_type, tracks in pairs(supported_active_tracks) do +    if track_type == "audio" then +      append_audio_tracks(command, tracks) +    else +      for _index_0 = 1, #tracks do +        local track = tracks[_index_0] +        append_track(command, track) +      end +    end +  end +  for track_type, tracks in pairs(supported_active_tracks) do +    local _continue_0 = false +    repeat +      if #tracks > 0 then +        _continue_0 = true +        break +      end +      local _exp_0 = track_type +      if "video" == _exp_0 then +        append(command, { +          "--vid=no" +        }) +      elseif "audio" == _exp_0 then +        append(command, { +          "--aid=no" +        }) +      elseif "sub" == _exp_0 then +        append(command, { +          "--sid=no" +        }) +      end +      _continue_0 = true +    until true +    if not _continue_0 then +      break +    end +  end +  if format.videoCodec ~= "" then +    append(command, get_video_encode_flags(format, region)) +  end +  append(command, format:getFlags()) +  if options.write_filename_on_metadata then +    append(command, get_metadata_flags()) +  end +  if format.acceptsBitrate then +    if options.target_filesize > 0 then +      local length = endTime - startTime +      local video_bitrate, audio_bitrate = calculate_bitrate(supported_active_tracks, format, length) +      if video_bitrate then +        append(command, { +          "--ovcopts-add=b=" .. tostring(video_bitrate) .. "k" +        }) +      end +      if audio_bitrate then +        append(command, { +          "--oacopts-add=b=" .. tostring(audio_bitrate) .. "k" +        }) +      end +      if options.strict_filesize_constraint then +        local type = format.videoCodec ~= "" and "ovc" or "oac" +        append(command, { +          "--" .. tostring(type) .. "opts-add=minrate=" .. tostring(bitrate) .. "k", +          "--" .. tostring(type) .. "opts-add=maxrate=" .. tostring(bitrate) .. "k" +        }) +      end +    else +      local type = format.videoCodec ~= "" and "ovc" or "oac" +      append(command, { +        "--" .. tostring(type) .. "opts-add=b=0" +      }) +    end +  end +  for token in string.gmatch(options.additional_flags, "[^%s]+") do +    command[#command + 1] = token +  end +  if not options.strict_filesize_constraint then +    for token in string.gmatch(options.non_strict_additional_flags, "[^%s]+") do +      command[#command + 1] = token +    end +    if options.crf >= 0 then +      append(command, { +        "--ovcopts-add=crf=" .. tostring(options.crf) +      }) +    end +  end +  local dir = "" +  if is_stream then +    dir = parse_directory("~") +  else +    local _ +    dir, _ = utils.split_path(path) +  end +  if options.output_directory ~= "" then +    dir = parse_directory(options.output_directory) +  end +  local formatted_filename = format_filename(originalStartTime, originalEndTime, format) +  local out_path = utils.join_path(dir, formatted_filename) +  append(command, { +    "--o=" .. tostring(out_path) +  }) +  emit_event("encode-started") +  if options.twopass and format.supportsTwopass and not is_stream then +    local first_pass_cmdline +    do +      local _accum_0 = { } +      local _len_0 = 1 +      for _index_0 = 1, #command do +        local arg = command[_index_0] +        _accum_0[_len_0] = arg +        _len_0 = _len_0 + 1 +      end +      first_pass_cmdline = _accum_0 +    end +    append(first_pass_cmdline, { +      "--ovcopts-add=flags=+pass1" +    }) +    message("Starting first pass...") +    msg.verbose("First-pass command line: ", table.concat(first_pass_cmdline, " ")) +    local res = run_subprocess({ +      args = first_pass_cmdline, +      cancellable = false +    }) +    if not res then +      message("First pass failed! Check the logs for details.") +      emit_event("encode-finished", "fail") +      return  +    end +    append(command, { +      "--ovcopts-add=flags=+pass2" +    }) +    if format.videoCodec == "libvpx" then +      msg.verbose("Patching libvpx pass log file...") +      vp8_patch_logfile(get_pass_logfile_path(out_path), endTime - startTime) +    end +  end +  command = format:postCommandModifier(command, region, startTime, endTime) +  msg.info("Encoding to", out_path) +  msg.verbose("Command line:", table.concat(command, " ")) +  if options.run_detached then +    message("Started encode, process was detached.") +    return utils.subprocess_detached({ +      args = command +    }) +  else +    local res = false +    if not should_display_progress() then +      message("Started encode...") +      res = run_subprocess({ +        args = command, +        cancellable = false +      }) +    else +      local ewp = EncodeWithProgress(startTime, endTime) +      res = ewp:startEncode(command) +    end +    if res then +      message("Encoded successfully! Saved to\\N" .. tostring(bold(out_path))) +      emit_event("encode-finished", "success") +    else +      message("Encode failed! Check the logs for details.") +      emit_event("encode-finished", "fail") +    end +    os.remove(get_pass_logfile_path(out_path)) +    if is_temporary then +      return os.remove(path) +    end +  end +end +local CropPage +do +  local _class_0 +  local _parent_0 = Page +  local _base_0 = { +    reset = function(self) +      local dimensions = get_video_dimensions() +      local xa, ya +      do +        local _obj_0 = dimensions.top_left +        xa, ya = _obj_0.x, _obj_0.y +      end +      self.pointA:set_from_screen(xa, ya) +      local xb, yb +      do +        local _obj_0 = dimensions.bottom_right +        xb, yb = _obj_0.x, _obj_0.y +      end +      self.pointB:set_from_screen(xb, yb) +      if self.visible then +        return self:draw() +      end +    end, +    setPointA = function(self) +      local posX, posY = mp.get_mouse_pos() +      self.pointA:set_from_screen(posX, posY) +      if self.visible then +        return self:draw() +      end +    end, +    setPointB = function(self) +      local posX, posY = mp.get_mouse_pos() +      self.pointB:set_from_screen(posX, posY) +      if self.visible then +        return self:draw() +      end +    end, +    cancel = function(self) +      self:hide() +      return self.callback(false, nil) +    end, +    finish = function(self) +      local region = Region() +      region:set_from_points(self.pointA, self.pointB) +      self:hide() +      return self.callback(true, region) +    end, +    draw_box = function(self, ass) +      local region = Region() +      region:set_from_points(self.pointA:to_screen(), self.pointB:to_screen()) +      local d = get_video_dimensions() +      ass:new_event() +      ass:append("{\\an7}") +      ass:pos(0, 0) +      ass:append('{\\bord0}') +      ass:append('{\\shad0}') +      ass:append('{\\c&H000000&}') +      ass:append('{\\alpha&H77}') +      ass:draw_start() +      ass:rect_cw(d.top_left.x, d.top_left.y, region.x, region.y + region.h) +      ass:rect_cw(region.x, d.top_left.y, d.bottom_right.x, region.y) +      ass:rect_cw(d.top_left.x, region.y + region.h, region.x + region.w, d.bottom_right.y) +      ass:rect_cw(region.x + region.w, region.y, d.bottom_right.x, d.bottom_right.y) +      return ass:draw_stop() +    end, +    draw = function(self) +      local window = { } +      window.w, window.h = mp.get_osd_size() +      local ass = assdraw.ass_new() +      self:draw_box(ass) +      ass:new_event() +      self:setup_text(ass) +      ass:append(tostring(bold('Crop:')) .. "\\N") +      ass:append(tostring(bold('1:')) .. " change point A (" .. tostring(self.pointA.x) .. ", " .. tostring(self.pointA.y) .. ")\\N") +      ass:append(tostring(bold('2:')) .. " change point B (" .. tostring(self.pointB.x) .. ", " .. tostring(self.pointB.y) .. ")\\N") +      ass:append(tostring(bold('r:')) .. " reset to whole screen\\N") +      ass:append(tostring(bold('ESC:')) .. " cancel crop\\N") +      local width, height = math.abs(self.pointA.x - self.pointB.x), math.abs(self.pointA.y - self.pointB.y) +      ass:append(tostring(bold('ENTER:')) .. " confirm crop (" .. tostring(width) .. "x" .. tostring(height) .. ")\\N") +      return mp.set_osd_ass(window.w, window.h, ass.text) +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self, callback, region) +      self.pointA = VideoPoint() +      self.pointB = VideoPoint() +      self.keybinds = { +        ["1"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.setPointA +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["2"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.setPointB +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["r"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.reset +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["ESC"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.cancel +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["ENTER"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.finish +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)() +      } +      self:reset() +      self.callback = callback +      if region and region:is_valid() then +        self.pointA.x = region.x +        self.pointA.y = region.y +        self.pointB.x = region.x + region.w +        self.pointB.y = region.y + region.h +      end +    end, +    __base = _base_0, +    __name = "CropPage", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  CropPage = _class_0 +end +local Option +do +  local _class_0 +  local _base_0 = { +    hasPrevious = function(self) +      local _exp_0 = self.optType +      if "bool" == _exp_0 then +        return true +      elseif "int" == _exp_0 then +        if self.opts.min then +          return self.value > self.opts.min +        else +          return true +        end +      elseif "list" == _exp_0 then +        return self.value > 1 +      end +    end, +    hasNext = function(self) +      local _exp_0 = self.optType +      if "bool" == _exp_0 then +        return true +      elseif "int" == _exp_0 then +        if self.opts.max then +          return self.value < self.opts.max +        else +          return true +        end +      elseif "list" == _exp_0 then +        return self.value < #self.opts.possibleValues +      end +    end, +    leftKey = function(self) +      local _exp_0 = self.optType +      if "bool" == _exp_0 then +        self.value = not self.value +      elseif "int" == _exp_0 then +        self.value = self.value - self.opts.step +        if self.opts.min and self.opts.min > self.value then +          self.value = self.opts.min +        end +      elseif "list" == _exp_0 then +        if self.value > 1 then +          self.value = self.value - 1 +        end +      end +    end, +    rightKey = function(self) +      local _exp_0 = self.optType +      if "bool" == _exp_0 then +        self.value = not self.value +      elseif "int" == _exp_0 then +        self.value = self.value + self.opts.step +        if self.opts.max and self.opts.max < self.value then +          self.value = self.opts.max +        end +      elseif "list" == _exp_0 then +        if self.value < #self.opts.possibleValues then +          self.value = self.value + 1 +        end +      end +    end, +    getValue = function(self) +      local _exp_0 = self.optType +      if "bool" == _exp_0 then +        return self.value +      elseif "int" == _exp_0 then +        return self.value +      elseif "list" == _exp_0 then +        local value, _ +        do +          local _obj_0 = self.opts.possibleValues[self.value] +          value, _ = _obj_0[1], _obj_0[2] +        end +        return value +      end +    end, +    setValue = function(self, value) +      local _exp_0 = self.optType +      if "bool" == _exp_0 then +        self.value = value +      elseif "int" == _exp_0 then +        self.value = value +      elseif "list" == _exp_0 then +        local set = false +        for i, possiblePair in ipairs(self.opts.possibleValues) do +          local possibleValue, _ +          possibleValue, _ = possiblePair[1], possiblePair[2] +          if possibleValue == value then +            set = true +            self.value = i +            break +          end +        end +        if not set then +          return msg.warn("Tried to set invalid value " .. tostring(value) .. " to " .. tostring(self.displayText) .. " option.") +        end +      end +    end, +    getDisplayValue = function(self) +      local _exp_0 = self.optType +      if "bool" == _exp_0 then +        return self.value and "yes" or "no" +      elseif "int" == _exp_0 then +        if self.opts.altDisplayNames and self.opts.altDisplayNames[self.value] then +          return self.opts.altDisplayNames[self.value] +        else +          return tostring(self.value) +        end +      elseif "list" == _exp_0 then +        local value, displayValue +        do +          local _obj_0 = self.opts.possibleValues[self.value] +          value, displayValue = _obj_0[1], _obj_0[2] +        end +        return displayValue or value +      end +    end, +    draw = function(self, ass, selected) +      if selected then +        ass:append(tostring(bold(self.displayText)) .. ": ") +      else +        ass:append(tostring(self.displayText) .. ": ") +      end +      if self:hasPrevious() then +        ass:append("◀ ") +      end +      ass:append(self:getDisplayValue()) +      if self:hasNext() then +        ass:append(" ▶") +      end +      return ass:append("\\N") +    end, +    optVisible = function(self) +      if self.visibleCheckFn == nil then +        return true +      else +        return self.visibleCheckFn() +      end +    end +  } +  _base_0.__index = _base_0 +  _class_0 = setmetatable({ +    __init = function(self, optType, displayText, value, opts, visibleCheckFn) +      self.optType = optType +      self.displayText = displayText +      self.opts = opts +      self.value = 1 +      self.visibleCheckFn = visibleCheckFn +      return self:setValue(value) +    end, +    __base = _base_0, +    __name = "Option" +  }, { +    __index = _base_0, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  Option = _class_0 +end +local EncodeOptionsPage +do +  local _class_0 +  local _parent_0 = Page +  local _base_0 = { +    getCurrentOption = function(self) +      return self.options[self.currentOption][2] +    end, +    leftKey = function(self) +      (self:getCurrentOption()):leftKey() +      return self:draw() +    end, +    rightKey = function(self) +      (self:getCurrentOption()):rightKey() +      return self:draw() +    end, +    prevOpt = function(self) +      for i = self.currentOption - 1, 1, -1 do +        if self.options[i][2]:optVisible() then +          self.currentOption = i +          break +        end +      end +      return self:draw() +    end, +    nextOpt = function(self) +      for i = self.currentOption + 1, #self.options do +        if self.options[i][2]:optVisible() then +          self.currentOption = i +          break +        end +      end +      return self:draw() +    end, +    confirmOpts = function(self) +      for _, optPair in ipairs(self.options) do +        local optName, opt +        optName, opt = optPair[1], optPair[2] +        options[optName] = opt:getValue() +      end +      self:hide() +      return self.callback(true) +    end, +    cancelOpts = function(self) +      self:hide() +      return self.callback(false) +    end, +    draw = function(self) +      local window_w, window_h = mp.get_osd_size() +      local ass = assdraw.ass_new() +      ass:new_event() +      self:setup_text(ass) +      ass:append(tostring(bold('Options:')) .. "\\N\\N") +      for i, optPair in ipairs(self.options) do +        local opt = optPair[2] +        if opt:optVisible() then +          opt:draw(ass, self.currentOption == i) +        end +      end +      ass:append("\\N▲ / ▼: navigate\\N") +      ass:append(tostring(bold('ENTER:')) .. " confirm options\\N") +      ass:append(tostring(bold('ESC:')) .. " cancel\\N") +      return mp.set_osd_ass(window_w, window_h, ass.text) +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self, callback) +      self.callback = callback +      self.currentOption = 1 +      local scaleHeightOpts = { +        possibleValues = { +          { +            -1, +            "no" +          }, +          { +            144 +          }, +          { +            240 +          }, +          { +            360 +          }, +          { +            480 +          }, +          { +            540 +          }, +          { +            720 +          }, +          { +            1080 +          }, +          { +            1440 +          }, +          { +            2160 +          } +        } +      } +      local filesizeOpts = { +        step = 250, +        min = 0, +        altDisplayNames = { +          [0] = "0 (constant quality)" +        } +      } +      local crfOpts = { +        step = 1, +        min = -1, +        altDisplayNames = { +          [-1] = "disabled" +        } +      } +      local fpsOpts = { +        possibleValues = { +          { +            -1, +            "source" +          }, +          { +            15 +          }, +          { +            24 +          }, +          { +            30 +          }, +          { +            48 +          }, +          { +            50 +          }, +          { +            60 +          }, +          { +            120 +          }, +          { +            240 +          } +        } +      } +      local formatIds = { +        "webm-vp8", +        "webm-vp9", +        "mp4", +        "mp4-nvenc", +        "raw", +        "mp3", +        "gif" +      } +      local formatOpts = { +        possibleValues = (function() +          local _accum_0 = { } +          local _len_0 = 1 +          for _index_0 = 1, #formatIds do +            local fId = formatIds[_index_0] +            _accum_0[_len_0] = { +              fId, +              formats[fId].displayName +            } +            _len_0 = _len_0 + 1 +          end +          return _accum_0 +        end)() +      } +      local gifDitherOpts = { +        possibleValues = { +          { +            0, +            "bayer_scale 0" +          }, +          { +            1, +            "bayer_scale 1" +          }, +          { +            2, +            "bayer_scale 2" +          }, +          { +            3, +            "bayer_scale 3" +          }, +          { +            4, +            "bayer_scale 4" +          }, +          { +            5, +            "bayer_scale 5" +          }, +          { +            6, +            "sierra2_4a" +          } +        } +      } +      self.options = { +        { +          "output_format", +          Option("list", "Output Format", options.output_format, formatOpts) +        }, +        { +          "twopass", +          Option("bool", "Two Pass", options.twopass) +        }, +        { +          "apply_current_filters", +          Option("bool", "Apply Current Video Filters", options.apply_current_filters) +        }, +        { +          "scale_height", +          Option("list", "Scale Height", options.scale_height, scaleHeightOpts) +        }, +        { +          "strict_filesize_constraint", +          Option("bool", "Strict Filesize Constraint", options.strict_filesize_constraint) +        }, +        { +          "write_filename_on_metadata", +          Option("bool", "Write Filename on Metadata", options.write_filename_on_metadata) +        }, +        { +          "target_filesize", +          Option("int", "Target Filesize", options.target_filesize, filesizeOpts) +        }, +        { +          "crf", +          Option("int", "CRF", options.crf, crfOpts) +        }, +        { +          "fps", +          Option("list", "FPS", options.fps, fpsOpts) +        }, +        { +          "gif_dither", +          Option("list", "GIF Dither Type", options.gif_dither, gifDitherOpts, function() +            return self.options[1][2]:getValue() == "gif" +          end) +        }, +        { +          "force_square_pixels", +          Option("bool", "Force Square Pixels", options.force_square_pixels) +        } +      } +      self.keybinds = { +        ["LEFT"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.leftKey +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["RIGHT"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.rightKey +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["UP"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.prevOpt +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["DOWN"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.nextOpt +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["ENTER"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.confirmOpts +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["ESC"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.cancelOpts +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)() +      } +    end, +    __base = _base_0, +    __name = "EncodeOptionsPage", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  EncodeOptionsPage = _class_0 +end +local PreviewPage +do +  local _class_0 +  local _parent_0 = Page +  local _base_0 = { +    prepare = function(self) +      local vf = mp.get_property_native("vf") +      vf[#vf + 1] = { +        name = "sub" +      } +      if self.region:is_valid() then +        vf[#vf + 1] = { +          name = "crop", +          params = { +            w = tostring(self.region.w), +            h = tostring(self.region.h), +            x = tostring(self.region.x), +            y = tostring(self.region.y) +          } +        } +      end +      mp.set_property_native("vf", vf) +      if self.startTime > -1 and self.endTime > -1 then +        mp.set_property_native("ab-loop-a", self.startTime) +        mp.set_property_native("ab-loop-b", self.endTime) +        mp.set_property_native("time-pos", self.startTime) +      end +      return mp.set_property_native("pause", false) +    end, +    dispose = function(self) +      mp.set_property("ab-loop-a", "no") +      mp.set_property("ab-loop-b", "no") +      for prop, value in pairs(self.originalProperties) do +        mp.set_property_native(prop, value) +      end +    end, +    draw = function(self) +      local window_w, window_h = mp.get_osd_size() +      local ass = assdraw.ass_new() +      ass:new_event() +      self:setup_text(ass) +      ass:append("Press " .. tostring(bold('ESC')) .. " to exit preview.\\N") +      return mp.set_osd_ass(window_w, window_h, ass.text) +    end, +    cancel = function(self) +      self:hide() +      return self.callback() +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self, callback, region, startTime, endTime) +      self.callback = callback +      self.originalProperties = { +        ["vf"] = mp.get_property_native("vf"), +        ["time-pos"] = mp.get_property_native("time-pos"), +        ["pause"] = mp.get_property_native("pause") +      } +      self.keybinds = { +        ["ESC"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.cancel +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)() +      } +      self.region = region +      self.startTime = startTime +      self.endTime = endTime +      self.isLoop = false +    end, +    __base = _base_0, +    __name = "PreviewPage", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  PreviewPage = _class_0 +end +local MainPage +do +  local _class_0 +  local _parent_0 = Page +  local _base_0 = { +    setStartTime = function(self) +      self.startTime = mp.get_property_number("time-pos") +      if self.visible then +        self:clear() +        return self:draw() +      end +    end, +    setEndTime = function(self) +      self.endTime = mp.get_property_number("time-pos") +      if self.visible then +        self:clear() +        return self:draw() +      end +    end, +    setupStartAndEndTimes = function(self) +      if mp.get_property_native("duration") then +        self.startTime = 0 +        self.endTime = mp.get_property_native("duration") +      else +        self.startTime = -1 +        self.endTime = -1 +      end +      if self.visible then +        self:clear() +        return self:draw() +      end +    end, +    draw = function(self) +      local window_w, window_h = mp.get_osd_size() +      local ass = assdraw.ass_new() +      ass:new_event() +      self:setup_text(ass) +      ass:append(tostring(bold('WebM maker')) .. "\\N\\N") +      ass:append(tostring(bold('c:')) .. " crop\\N") +      ass:append(tostring(bold('1:')) .. " set start time (current is " .. tostring(seconds_to_time_string(self.startTime)) .. ")\\N") +      ass:append(tostring(bold('2:')) .. " set end time (current is " .. tostring(seconds_to_time_string(self.endTime)) .. ")\\N") +      ass:append(tostring(bold('o:')) .. " change encode options\\N") +      ass:append(tostring(bold('p:')) .. " preview\\N") +      ass:append(tostring(bold('e:')) .. " encode\\N\\N") +      ass:append(tostring(bold('ESC:')) .. " close\\N") +      return mp.set_osd_ass(window_w, window_h, ass.text) +    end, +    show = function(self) +      _class_0.__parent.show(self) +      return emit_event("show-main-page") +    end, +    onUpdateCropRegion = function(self, updated, newRegion) +      if updated then +        self.region = newRegion +      end +      return self:show() +    end, +    crop = function(self) +      self:hide() +      local cropPage = CropPage((function() +        local _base_1 = self +        local _fn_0 = _base_1.onUpdateCropRegion +        return function(...) +          return _fn_0(_base_1, ...) +        end +      end)(), self.region) +      return cropPage:show() +    end, +    onOptionsChanged = function(self, updated) +      return self:show() +    end, +    changeOptions = function(self) +      self:hide() +      local encodeOptsPage = EncodeOptionsPage((function() +        local _base_1 = self +        local _fn_0 = _base_1.onOptionsChanged +        return function(...) +          return _fn_0(_base_1, ...) +        end +      end)()) +      return encodeOptsPage:show() +    end, +    onPreviewEnded = function(self) +      return self:show() +    end, +    preview = function(self) +      self:hide() +      local previewPage = PreviewPage((function() +        local _base_1 = self +        local _fn_0 = _base_1.onPreviewEnded +        return function(...) +          return _fn_0(_base_1, ...) +        end +      end)(), self.region, self.startTime, self.endTime) +      return previewPage:show() +    end, +    encode = function(self) +      self:hide() +      if self.startTime < 0 then +        message("No start time, aborting") +        return  +      end +      if self.endTime < 0 then +        message("No end time, aborting") +        return  +      end +      if self.startTime >= self.endTime then +        message("Start time is ahead of end time, aborting") +        return  +      end +      return encode(self.region, self.startTime, self.endTime) +    end +  } +  _base_0.__index = _base_0 +  setmetatable(_base_0, _parent_0.__base) +  _class_0 = setmetatable({ +    __init = function(self) +      self.keybinds = { +        ["c"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.crop +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["1"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.setStartTime +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["2"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.setEndTime +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["o"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.changeOptions +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["p"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.preview +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["e"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.encode +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)(), +        ["ESC"] = (function() +          local _base_1 = self +          local _fn_0 = _base_1.hide +          return function(...) +            return _fn_0(_base_1, ...) +          end +        end)() +      } +      self.startTime = -1 +      self.endTime = -1 +      self.region = Region() +    end, +    __base = _base_0, +    __name = "MainPage", +    __parent = _parent_0 +  }, { +    __index = function(cls, name) +      local val = rawget(_base_0, name) +      if val == nil then +        local parent = rawget(cls, "__parent") +        if parent then +          return parent[name] +        end +      else +        return val +      end +    end, +    __call = function(cls, ...) +      local _self_0 = setmetatable({}, _base_0) +      cls.__init(_self_0, ...) +      return _self_0 +    end +  }) +  _base_0.__class = _class_0 +  if _parent_0.__inherited then +    _parent_0.__inherited(_parent_0, _class_0) +  end +  MainPage = _class_0 +end +monitor_dimensions() +local mainPage = MainPage() +mp.add_key_binding(options.keybind, "display-webm-encoder", (function() +  local _base_0 = mainPage +  local _fn_0 = _base_0.show +  return function(...) +    return _fn_0(_base_0, ...) +  end +end)(), { +  repeatable = false +}) +mp.register_event("file-loaded", (function() +  local _base_0 = mainPage +  local _fn_0 = _base_0.setupStartAndEndTimes +  return function(...) +    return _fn_0(_base_0, ...) +  end +end)()) +msg.verbose("Loaded mpv-webm script!") +return emit_event("script-loaded") diff --git a/config/common/mpv/scripts/youtube-quality.lua b/config/common/mpv/scripts/youtube-quality.lua new file mode 100755 index 0000000..1331210 --- /dev/null +++ b/config/common/mpv/scripts/youtube-quality.lua @@ -0,0 +1,275 @@ +-- youtube-quality.lua +-- +-- Change youtube video quality on the fly. +-- +-- Diplays a menu that lets you switch to different ytdl-format settings while +-- you're in the middle of a video (just like you were using the web player). +-- +-- Bound to ctrl-f by default. + +local mp = require 'mp' +local utils = require 'mp.utils' +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' + +local opts = { +    --key bindings +    toggle_menu_binding = "ctrl+f", +    up_binding = "UP", +    down_binding = "DOWN", +    select_binding = "ENTER", + +    --formatting / cursors +    selected_and_active     = "▶ - ", +    selected_and_inactive   = "● - ", +    unselected_and_active   = "▷ - ", +    unselected_and_inactive = "○ - ", + +	--font size scales by window, if false requires larger font and padding sizes +	scale_playlist_by_window=false, + +    --playlist ass style overrides inside curly brackets, \keyvalue is one field, extra \ for escape in lua +    --example {\\fnUbuntu\\fs10\\b0\\bord1} equals: font=Ubuntu, size=10, bold=no, border=1 +    --read http://docs.aegisub.org/3.2/ASS_Tags/ for reference of tags +    --undeclared tags will use default osd settings +    --these styles will be used for the whole playlist. More specific styling will need to be hacked in +    -- +    --(a monospaced font is recommended but not required) +    style_ass_tags = "{\\fnmonospace}", + +    --paddings for top left corner +    text_padding_x = 5, +    text_padding_y = 5, + +    --other +    menu_timeout = 10, + +    --use yt-dlp to fetch a list of available formats (overrides quality_strings) +    fetch_formats = true, + +    --default menu entries +    quality_strings=[[ +    [ +    {"4320p" : "bestvideo[height<=?4320p]+bestaudio/best"}, +    {"2160p" : "bestvideo[height<=?2160]+bestaudio/best"}, +    {"1440p" : "bestvideo[height<=?1440]+bestaudio/best"}, +    {"1080p" : "bestvideo[height<=?1080]+bestaudio/best"}, +    {"720p" : "bestvideo[height<=?720]+bestaudio/best"}, +    {"480p" : "bestvideo[height<=?480]+bestaudio/best"}, +    {"360p" : "bestvideo[height<=?360]+bestaudio/best"}, +    {"240p" : "bestvideo[height<=?240]+bestaudio/best"}, +    {"144p" : "bestvideo[height<=?144]+bestaudio/best"} +    ] +    ]], +} +(require 'mp.options').read_options(opts, "youtube-quality") +opts.quality_strings = utils.parse_json(opts.quality_strings) + +local destroyer = nil + + +function show_menu() +    local selected = 1 +    local active = 0 +    local current_ytdl_format = mp.get_property("ytdl-format") +    msg.verbose("current ytdl-format: "..current_ytdl_format) +    local num_options = 0 +    local options = {} + + +    if opts.fetch_formats then +        options, num_options = download_formats() +    end + +    if next(options) == nil then +        for i,v in ipairs(opts.quality_strings) do +            num_options = num_options + 1 +            for k,v2 in pairs(v) do +                options[i] = {label = k, format=v2} +                if v2 == current_ytdl_format then +                    active = i +                    selected = active +                end +            end +        end +    end + +    --set the cursor to the currently format +    for i,v in ipairs(options) do +        if v.format == current_ytdl_format then +            active = i +            selected = active +            break +        end +    end + +    function selected_move(amt) +        selected = selected + amt +        if selected < 1 then selected = num_options +        elseif selected > num_options then selected = 1 end +        timeout:kill() +        timeout:resume() +        draw_menu() +    end +    function choose_prefix(i) +        if     i == selected and i == active then return opts.selected_and_active  +        elseif i == selected then return opts.selected_and_inactive end + +        if     i ~= selected and i == active then return opts.unselected_and_active +        elseif i ~= selected then return opts.unselected_and_inactive end +        return "> " --shouldn't get here. +    end + +    function draw_menu() +        local ass = assdraw.ass_new() + +        ass:pos(opts.text_padding_x, opts.text_padding_y) +        ass:append(opts.style_ass_tags) + +        for i,v in ipairs(options) do +            ass:append(choose_prefix(i)..v.label.."\\N") +        end + +		local w, h = mp.get_osd_size() +		if opts.scale_playlist_by_window then w,h = 0, 0 end +		mp.set_osd_ass(w, h, ass.text) +    end + +    function destroy() +        timeout:kill() +        mp.set_osd_ass(0,0,"") +        mp.remove_key_binding("move_up") +        mp.remove_key_binding("move_down") +        mp.remove_key_binding("select") +        mp.remove_key_binding("escape") +        destroyer = nil +    end +    timeout = mp.add_periodic_timer(opts.menu_timeout, destroy) +    destroyer = destroy + +    mp.add_forced_key_binding(opts.up_binding,     "move_up",   function() selected_move(-1) end, {repeatable=true}) +    mp.add_forced_key_binding(opts.down_binding,   "move_down", function() selected_move(1)  end, {repeatable=true}) +    mp.add_forced_key_binding(opts.select_binding, "select",    function() +        destroy() +        mp.set_property("ytdl-format", options[selected].format) +        reload_resume() +    end) +    mp.add_forced_key_binding(opts.toggle_menu_binding, "escape", destroy) + +    draw_menu() +    return  +end + +local ytdl = { +    path = "yt-dlp", +    searched = false, +    blacklisted = {} +} + +format_cache={} +function download_formats() +    local function exec(args) +        local ret = utils.subprocess({args = args}) +        return ret.status, ret.stdout, ret +    end + +    local function table_size(t) +        s = 0 +        for i,v in ipairs(t) do +            s = s+1 +        end +        return s +    end + +    local url = mp.get_property("path") + +    url = string.gsub(url, "ytdl://", "") -- Strip possible ytdl:// prefix. + +    -- don't fetch the format list if we already have it +    if format_cache[url] ~= nil then  +        local res = format_cache[url] +        return res, table_size(res) +    end +    mp.osd_message("fetching available formats with yt-dlp...", 60) + +    if not (ytdl.searched) then +        local ytdl_mcd = mp.find_config_file("yt-dlp") +        if not (ytdl_mcd == nil) then +            msg.verbose("found yt-dlp at: " .. ytdl_mcd) +            ytdl.path = ytdl_mcd +        end +        ytdl.searched = true +    end + +    local command = {ytdl.path, "--no-warnings", "--no-playlist", "-J"} +    table.insert(command, url) +    local es, json, result = exec(command) + +    if (es < 0) or (json == nil) or (json == "") then +        mp.osd_message("fetching formats failed...", 1) +        msg.error("failed to get format list: " .. err) +        return {}, 0 +    end + +    local json, err = utils.parse_json(json) + +    if (json == nil) then +        mp.osd_message("fetching formats failed...", 1) +        msg.error("failed to parse JSON data: " .. err) +        return {}, 0 +    end + +    res = {} +    msg.verbose("yt-dlp succeeded!") +    for i,v in ipairs(json.formats) do +        if v.vcodec ~= "none" then +            local fps = v.fps and v.fps.."fps" or "" +            local resolution = string.format("%sx%s", v.width, v.height) +            local l = string.format("%-9s %-5s (%-4s / %s)", resolution, fps, v.ext, v.vcodec) +            local f = string.format("%s+bestaudio/best", v.format_id) +            table.insert(res, {label=l, format=f, width=v.width }) +        end +    end + +    table.sort(res, function(a, b) return a.width > b.width end) + +    mp.osd_message("", 0) +    format_cache[url] = res +    return res, table_size(res) +end + + +-- register script message to show menu +mp.register_script_message("toggle-quality-menu",  +function() +    if destroyer ~= nil then +        destroyer() +    else +        show_menu() +    end +end) + +-- keybind to launch menu +mp.add_key_binding(opts.toggle_menu_binding, "quality-menu", show_menu) + +-- special thanks to reload.lua (https://github.com/4e6/mpv-reload/) +function reload_resume() +    local playlist_pos = mp.get_property_number("playlist-pos") +    local reload_duration = mp.get_property_native("duration") +    local time_pos = mp.get_property("time-pos") + +    mp.set_property_number("playlist-pos", playlist_pos) + +    -- Tries to determine live stream vs. pre-recordered VOD. VOD has non-zero +    -- duration property. When reloading VOD, to keep the current time position +    -- we should provide offset from the start. Stream doesn't have fixed start. +    -- Decent choice would be to reload stream from it's current 'live' positon. +    -- That's the reason we don't pass the offset when reloading streams. +    if reload_duration and reload_duration > 0 then +        local function seeker() +            mp.commandv("seek", time_pos, "absolute") +            mp.unregister_event(seeker) +        end +        mp.register_event("file-loaded", seeker) +    end +end  | 
