Commit: 08efd193a2dbaa93505d02ea834527ae3cf18c68
Parent: 83b1f5dc597587f3b3b9916634ee16bd0f5d0198
Author: 0x766F6964
Date: Sat, 14 Nov 2020 19:06:12 -0700
mpv: update scripts and move some to submodules
Diffstat:
9 files changed, 876 insertions(+), 947 deletions(-)
diff --git a/.config/mpv/scripts/autoload.lua b/.config/mpv/scripts/autoload.lua
@@ -2,15 +2,34 @@
-- the currently played file. It does so by scanning the directory a file is
-- located in when starting playback. It sorts the directory entries
-- alphabetically, and adds entries before and after the current file to
--- the internal playlist. (It stops if the it would add an already existing
+-- the internal playlist. (It stops if it would add an already existing
-- playlist entry at the same position - this makes it "stable".)
-- Add at most 5000 * 2 files when starting a file (before + after).
+
+--[[
+To configure this script use file autoload.conf in directory script-opts (the "script-opts"
+directory must be in the mpv configuration directory, typically ~/.config/mpv/).
+
+Example configuration would be:
+
+disabled=no
+images=no
+videos=yes
+audio=yes
+
+--]]
+
MAXENTRIES = 5000
+local msg = require 'mp.msg'
local options = require 'mp.options'
+local utils = require 'mp.utils'
o = {
- disabled = false
+ disabled = false,
+ images = true,
+ videos = true,
+ audio = true
}
options.read_options(o)
@@ -20,12 +39,29 @@ function Set (t)
return set
end
-EXTENSIONS = Set {
- 'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp',
- 'mp3', 'wav', 'ogm', 'flac', 'm4a', 'wma', 'ogg', 'opus',
+function SetUnion (a,b)
+ local res = {}
+ for k in pairs(a) do res[k] = true end
+ for k in pairs(b) do res[k] = true end
+ return res
+end
+
+EXTENSIONS_VIDEO = Set {
+ 'mkv', 'avi', 'mp4', 'ogv', 'webm', 'rmvb', 'flv', 'wmv', 'mpeg', 'mpg', 'm4v', '3gp'
}
-mputils = require 'mp.utils'
+EXTENSIONS_AUDIO = Set {
+ 'mp3', 'wav', 'ogm', 'flac', 'm4a', 'wma', 'ogg', 'opus'
+}
+
+EXTENSIONS_IMAGES = Set {
+ 'jpg', 'jpeg', 'png', 'tif', 'tiff', 'gif', 'webp', 'svg', 'bmp'
+}
+
+EXTENSIONS = Set {}
+if o.videos then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_VIDEO) end
+if o.audio then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_AUDIO) end
+if o.images then EXTENSIONS = SetUnion(EXTENSIONS, EXTENSIONS_IMAGES) end
function add_files_at(index, files)
index = index - 1
@@ -84,22 +120,38 @@ function alnumcomp(x, y)
return #xt < #yt
end
+local autoloaded = nil
+
function find_and_add_entries()
local path = mp.get_property("path", "")
- local dir, filename = mputils.split_path(path)
- if o.disabled or #dir == 0 then
+ local dir, filename = utils.split_path(path)
+ msg.trace(("dir: %s, filename: %s"):format(dir, filename))
+ if o.disabled then
+ msg.verbose("stopping: autoload disabled")
+ return
+ elseif #dir == 0 then
+ msg.verbose("stopping: not a local path")
return
end
+
local pl_count = mp.get_property_number("playlist-count", 1)
- if (pl_count > 1 and autoload == nil) or
+ -- check if this is a manually made playlist
+ if (pl_count > 1 and autoloaded == nil) or
(pl_count == 1 and EXTENSIONS[string.lower(get_extension(filename))] == nil) then
+ msg.verbose("stopping: manually made playlist")
return
else
- autoload = true
+ autoloaded = true
end
- local files = mputils.readdir(dir, "files")
+ local pl = mp.get_property_native("playlist", {})
+ local pl_current = mp.get_property_number("playlist-pos-1", 1)
+ msg.trace(("playlist-pos-1: %s, playlist: %s"):format(pl_current,
+ utils.to_string(pl)))
+
+ local files = utils.readdir(dir, "files")
if files == nil then
+ msg.verbose("no other files in directory")
return
end
table.filter(files, function (v, k)
@@ -118,8 +170,6 @@ function find_and_add_entries()
dir = ""
end
- local pl = mp.get_property_native("playlist", {})
- local pl_current = mp.get_property_number("playlist-pos", 0) + 1
-- Find the current pl entry (dir+"/"+filename) in the sorted dir list
local current
for i = 1, #files do
@@ -131,6 +181,7 @@ function find_and_add_entries()
if current == nil then
return
end
+ msg.trace("current file position in files: "..current)
local append = {[-1] = {}, [1] = {}}
for direction = -1, 1, 2 do -- 2 iterations, with direction = -1 and +1
@@ -144,6 +195,7 @@ function find_and_add_entries()
local filepath = dir .. file
if pl_e then
-- If there's a playlist entry, and it's the same file, stop.
+ msg.trace(pl_e.filename.." == "..filepath.." ?")
if pl_e.filename == filepath then
break
end
@@ -151,11 +203,11 @@ function find_and_add_entries()
if direction == -1 then
if pl_current == 1 then -- never add additional entries in the middle
- mp.msg.info("Prepending " .. file)
+ msg.info("Prepending " .. file)
table.insert(append[-1], 1, filepath)
end
else
- mp.msg.info("Adding " .. file)
+ msg.info("Adding " .. file)
table.insert(append[1], filepath)
end
end
diff --git a/.config/mpv/scripts/detect-image.lua b/.config/mpv/scripts/detect-image.lua
@@ -1,89 +0,0 @@
-local opts = {
- command_on_first_image_loaded="",
- command_on_image_loaded="",
- command_on_non_image_loaded="",
-}
-(require 'mp.options').read_options(opts)
-
-if opts.command_on_first_image_loaded == ""
- and opts.command_on_image_loaded == ""
- and opts.command_on_non_image_loaded == ""
-then
- return
-end
-
-local msg = require 'mp.msg'
-
-local was_image = false
-local frame_count = nil
-local audio_tracks = nil
-local out_params_ready = nil
-local path = nil
-
-function run_maybe(str)
- if str ~= "" then
- mp.command(str)
- end
-end
-
-function set_image(is_image)
- if is_image and not was_image then
- msg.verbose("First image detected")
- run_maybe(opts.command_on_first_image_loaded)
- end
- if is_image then
- msg.verbose("Image detected")
- run_maybe(opts.command_on_image_loaded)
- end
- if not is_image and was_image then
- msg.verbose("Non-image detected")
- run_maybe(opts.command_on_non_image_loaded)
- end
- was_image = is_image
-end
-
-function state_changed()
- -- only do things when state is consistent
- if path ~= nil and audio_tracks ~= nil then
- if frame_count == nil and audio_tracks > 0 then
- set_image(false)
- elseif out_params_ready and frame_count ~= nil then
- -- png have 0 frames, jpg 1 ¯\_(ツ)_/¯
- set_image((frame_count == 0 or frame_count == 1) and audio_tracks == 0)
- end
- end
-end
-
-mp.observe_property("dwidth", "number", function(_, val)
- out_params_ready = (val ~= nil and val > 0)
- state_changed()
-end)
-
-mp.observe_property("estimated-frame-count", "number", function(_, val)
- frame_count = val
- state_changed()
-end)
-
-mp.observe_property("path", "string", function(_, val)
- if not val or val == "" then
- path = nil
- else
- path = val
- end
- state_changed()
-end)
-
-mp.register_event("tracks-changed", function()
- audio_tracks = 0
- local tracks = 0
- for _, track in ipairs(mp.get_property_native("track-list")) do
- tracks = tracks + 1
- if track.type == "audio" then
- audio_tracks = audio_tracks + 1
- end
- end
- if tracks == 0 then
- audio_tracks = nil
- end
- state_changed()
-end)
diff --git a/.config/mpv/scripts/gallery-thumbgen.lua b/.config/mpv/scripts/gallery-thumbgen.lua
@@ -180,17 +180,17 @@ function thumbnail_command(input_path, width, height, take_thumbnail_at, output_
if not accurate then
add({ "--hr-seek=no"})
end
- add({ "--start", take_thumbnail_at })
+ add({ "--start="..take_thumbnail_at })
end
add({
"--no-config", "--msg-level=all=no",
- "--vf", "lavfi=[" .. vf .. ",format=bgra]",
- "--audio", "no",
- "--sub", "no",
- "--frames", "1",
- "--image-display-duration", "0",
- "--of", "rawvideo", "--ovc", "rawvideo",
- "--o", output_path
+ "--vf=lavfi=[" .. vf .. ",format=bgra]",
+ "--audio=no",
+ "--sub=no",
+ "--frames=1",
+ "--image-display-duration=0",
+ "--of=rawvideo", "--ovc=rawvideo",
+ "--o="..output_path
})
end
return out
diff --git a/.config/mpv/scripts/image-positioning.lua b/.config/mpv/scripts/image-positioning.lua
@@ -1,341 +0,0 @@
-local opts = {
- pan_follows_cursor_margin = 50,
- pan_follows_cursor_move_if_full_view = false,
-
- drag_to_pan_margin = 50,
- drag_to_pan_move_if_full_view = false,
-}
-(require 'mp.options').read_options(opts)
-
-function clamp(value, low, high)
- if value <= low then
- return low
- elseif value >= high then
- return high
- else
- return value
- end
-end
-
-local msg = require 'mp.msg'
-local assdraw = require 'mp.assdraw'
-
-function get_video_dimensions()
- -- this function is very much ripped from video/out/aspect.c in mpv's source
- local video_params = mp.get_property_native("video-out-params")
- if not video_params then
- _video_dimensions = nil
- return nil
- end
- _video_dimensions = {
- top_left = { 0, 0 },
- bottom_right = { 0, 0 },
- size = { 0, 0 },
- ratios = { 0, 0 }, -- by how much the original video got scaled
- }
- 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
- 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 = 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
- --account for C int cast truncating as opposed to flooring
- 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[1], _video_dimensions.bottom_right[1] = 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[2], _video_dimensions.bottom_right[2] = split_scaling(window_h, scaled_height, zoom, align_y, pan_y)
- else
- _video_dimensions.top_left[1] = 0
- _video_dimensions.bottom_right[1] = window_w
- _video_dimensions.top_left[2] = 0
- _video_dimensions.bottom_right[2] = window_h
- end
- _video_dimensions.size[1] = _video_dimensions.bottom_right[1] - _video_dimensions.top_left[1]
- _video_dimensions.size[2] = _video_dimensions.bottom_right[2] - _video_dimensions.top_left[2]
- _video_dimensions.ratios[1] = _video_dimensions.size[1] / w
- _video_dimensions.ratios[2] = _video_dimensions.size[2] / h
- return _video_dimensions
-end
-
-local cleanup = nil -- function set up by drag-to-pan/pan-follows cursor and must be called to clean lingering state
-
-function drag_to_pan_handler(table)
- if cleanup then
- cleanup()
- cleanup = nil
- end
- if table["event"] == "down" then
- local video_dimensions = get_video_dimensions()
- if not video_dimensions then return end
- local window_w, window_h = mp.get_osd_size()
- local mouse_pos_origin, video_pan_origin = {}, {}
- local moved = false
- mouse_pos_origin[1], mouse_pos_origin[2] = mp.get_mouse_pos()
- video_pan_origin[1] = mp.get_property_number("video-pan-x")
- video_pan_origin[2] = mp.get_property_number("video-pan-y")
- local margin = opts.drag_to_pan_margin
- local move_up = true
- local move_lateral = true
- if not opts.drag_to_pan_move_if_full_view then
- if video_dimensions.size[1] <= window_w then
- move_lateral = false
- end
- if video_dimensions.size[2] <= window_h then
- move_up = false
- end
- end
- if not move_up and not move_lateral then return end
- local idle = function()
- if moved then
- local mX, mY = mp.get_mouse_pos()
- local pX = video_pan_origin[1]
- local pY = video_pan_origin[2]
- if move_lateral then
- pX = video_pan_origin[1] + (mX - mouse_pos_origin[1]) / video_dimensions.size[1]
- if video_dimensions.size[1] + 2 * margin > window_w then
- pX = clamp(pX,
- (-margin + window_w / 2) / video_dimensions.size[1] - 0.5,
- (margin - window_w / 2) / video_dimensions.size[1] + 0.5)
- else
- pX = clamp(pX,
- (margin - window_w / 2) / video_dimensions.size[1] + 0.5,
- (-margin + window_w / 2) / video_dimensions.size[1] - 0.5)
- end
- end
- if move_up then
- pY = video_pan_origin[2] + (mY - mouse_pos_origin[2]) / video_dimensions.size[2]
- if video_dimensions.size[2] + 2 * margin > window_h then
- pY = clamp(pY,
- (-margin + window_h / 2) / video_dimensions.size[2] - 0.5,
- (margin - window_h / 2) / video_dimensions.size[2] + 0.5)
- else
- pY = clamp(pY,
- (margin - window_h / 2) / video_dimensions.size[2] + 0.5,
- (-margin + window_h / 2) / video_dimensions.size[2] - 0.5)
- end
- end
- mp.command("no-osd set video-pan-x " .. clamp(pX, -3, 3) .. "; no-osd set video-pan-y " .. clamp(pY, -3, 3))
- moved = false
- end
- end
- mp.register_idle(idle)
- mp.add_forced_key_binding("mouse_move", "image-viewer-mouse-move", function() moved = true end)
- cleanup = function()
- mp.remove_key_binding("image-viewer-mouse-move")
- mp.unregister_idle(idle)
- end
- end
-end
-
-function pan_follows_cursor_handler(table)
- if cleanup then
- cleanup()
- cleanup = nil
- end
- if table["event"] == "down" then
- local video_dimensions = get_video_dimensions()
- if not video_dimensions then return end
- local window_w, window_h = mp.get_osd_size()
- local moved = true
- local idle = function()
- if moved then
- local mX, mY = mp.get_mouse_pos()
- local x = math.min(1, math.max(- 2 * mX / window_w + 1, -1))
- local y = math.min(1, math.max(- 2 * mY / window_h + 1, -1))
- local command = ""
- local margin, move_full = opts.pan_follows_cursor_margin, opts.pan_follows_cursor_move_if_full_view
- if (not move_full and window_w < video_dimensions.size[1]) then
- command = command .. "no-osd set video-pan-x " .. clamp(x * (video_dimensions.size[1] - window_w + 2 * margin) / (2 * video_dimensions.size[1]), -3, 3) .. ";"
- elseif mp.get_property_number("video-pan-x") ~= 0 then
- command = command .. "no-osd set video-pan-x " .. "0;"
- end
- if (not move_full and window_h < video_dimensions.size[2]) then
- command = command .. "no-osd set video-pan-y " .. clamp(y * (video_dimensions.size[2] - window_h + 2 * margin) / (2 * video_dimensions.size[2]), -3, 3) .. ";"
- elseif mp.get_property_number("video-pan-y") ~= 0 then
- command = command .. "no-osd set video-pan-y " .. "0;"
- end
- if command ~= "" then
- mp.command(command)
- end
- moved = false
- end
- end
- mp.register_idle(idle)
- mp.add_forced_key_binding("mouse_move", "image-viewer-mouse-move", function() moved = true end)
- cleanup = function()
- mp.remove_key_binding("image-viewer-mouse-move")
- mp.unregister_idle(idle)
- end
- end
-end
-
-function cursor_centric_zoom_handler(amt)
- local zoom_inc = tonumber(amt)
- if not zoom_inc or zoom_inc == 0 then return end
- local video_dimensions = get_video_dimensions()
- if not video_dimensions then return end
- local mouse_pos_origin, video_pan_origin = {}, {}
- mouse_pos_origin[1], mouse_pos_origin[2] = mp.get_mouse_pos()
- video_pan_origin[1] = mp.get_property("video-pan-x")
- video_pan_origin[2] = mp.get_property("video-pan-y")
- local zoom_origin = mp.get_property("video-zoom")
- -- how far the cursor is form the middle of the video (in percentage)
- local rx = (video_dimensions.top_left[1] + video_dimensions.size[1] / 2 - mouse_pos_origin[1]) / (video_dimensions.size[1] / 2)
- local ry = (video_dimensions.top_left[2] + video_dimensions.size[2] / 2 - mouse_pos_origin[2]) / (video_dimensions.size[2] / 2)
-
- -- the size in pixels of the (in|de)crement
- local diffHeight = (2 ^ zoom_inc - 1) * video_dimensions.size[2]
- local diffWidth = (2 ^ zoom_inc - 1) * video_dimensions.size[1]
- local newPanX = (video_pan_origin[1] * video_dimensions.size[1] + rx * diffWidth / 2) / (video_dimensions.size[1] + diffWidth)
- local newPanY = (video_pan_origin[2] * video_dimensions.size[2] + ry * diffHeight / 2) / (video_dimensions.size[2] + diffHeight)
- mp.command("no-osd set video-zoom " .. zoom_origin + zoom_inc .. "; no-osd set video-pan-x " .. clamp(newPanX, -3, 3) .. "; no-osd set video-pan-y " .. clamp(newPanY, -3, 3))
-end
-
-function align_border(x, y)
- local video_dimensions = get_video_dimensions()
- if not video_dimensions then return end
- local window_w, window_h = mp.get_osd_size()
- local x, y = tonumber(x), tonumber(y)
- local command = ""
- if x then
- command = command .. "no-osd set video-pan-x " .. clamp(x * (video_dimensions.size[1] - window_w) / (2 * video_dimensions.size[1]), -3, 3) .. ";"
- end
- if y then
- command = command .. "no-osd set video-pan-y " .. clamp(y * (video_dimensions.size[2] - window_h) / (2 * video_dimensions.size[2]), -3, 3) .. ";"
- end
- if command ~= "" then
- mp.command(command)
- end
-end
-
-function pan_image(axis, amount, zoom_invariant, image_constrained)
- amount = tonumber(amount)
- if not amount or amount == 0 or axis ~= "x" and axis ~= "y" then return end
- if zoom_invariant == "yes" then
- amount = amount / 2 ^ mp.get_property_number("video-zoom")
- end
- local prop = "video-pan-" .. axis
- local old_pan = mp.get_property_number(prop)
- axis = (axis == "x") and 1 or 2
- if image_constrained == "yes" then
- local video_dimensions = get_video_dimensions()
- if not video_dimensions then return end
- local window = {}
- window[1], window[2] = mp.get_osd_size()
- local pixels_moved = amount * video_dimensions.size[axis]
- -- should somehow refactor this
- if pixels_moved > 0 then
- if window[axis] > video_dimensions.size[axis] then
- if video_dimensions.bottom_right[axis] >= window[axis] then return end
- if video_dimensions.bottom_right[axis] + pixels_moved > window[axis] then
- amount = (window[axis] - video_dimensions.bottom_right[axis]) / video_dimensions.size[axis]
- end
- else
- if video_dimensions.top_left[axis] >= 0 then return end
- if video_dimensions.top_left[axis] + pixels_moved > 0 then
- amount = (0 - video_dimensions.top_left[axis]) / video_dimensions.size[axis]
- end
- end
- else
- if window[axis] > video_dimensions.size[axis] then
- if video_dimensions.top_left[axis] <= 0 then return end
- if video_dimensions.top_left[axis] + pixels_moved < 0 then
- amount = (0 - video_dimensions.top_left[axis]) / video_dimensions.size[axis]
- end
- else
- if video_dimensions.bottom_right[axis] <= window[axis] then return end
- if video_dimensions.bottom_right[axis] + pixels_moved < window[axis] then
- amount = (window[axis] - video_dimensions.bottom_right[axis]) / video_dimensions.size[axis]
- end
- end
- end
- end
- mp.set_property_number(prop, old_pan + amount)
-end
-
-function rotate_video(amt)
- local rot = mp.get_property_number("video-rotate")
- rot = (rot + amt) % 360
- mp.set_property_number("video-rotate", rot)
-end
-
-function reset_pan_if_visible()
- local video_dimensions = get_video_dimensions()
- if not video_dimensions then return end
- local window_w, window_h = mp.get_osd_size()
- local command = ""
- if (window_w >= video_dimensions.size[1]) then
- command = command .. "no-osd set video-pan-x 0" .. ";"
- end
- if (window_h >= video_dimensions.size[2]) then
- command = command .. "no-osd set video-pan-y 0" .. ";"
- end
- if command ~= "" then
- mp.command(command)
- end
-end
-
-mp.add_key_binding(nil, "drag-to-pan", drag_to_pan_handler, {complex = true})
-mp.add_key_binding(nil, "pan-follows-cursor", pan_follows_cursor_handler, {complex = true})
-mp.add_key_binding(nil, "cursor-centric-zoom", cursor_centric_zoom_handler)
-mp.add_key_binding(nil, "align-border", align_border)
-mp.add_key_binding(nil, "pan-image", pan_image)
-mp.add_key_binding(nil, "rotate-video", rotate_video)
-mp.add_key_binding(nil, "reset-pan-if-visible", reset_pan_if_visible)
-mp.add_key_binding(nil, "force-print-filename", force_print_filename)
diff --git a/.config/mpv/scripts/mpv-image-viewer b/.config/mpv/scripts/mpv-image-viewer
@@ -0,0 +1 @@
+Subproject commit 3ad8a0513355025cd8af9a9621292be59c367113
diff --git a/.config/mpv/scripts/mpv-reload b/.config/mpv/scripts/mpv-reload
@@ -0,0 +1 @@
+Subproject commit 2b8a719fe166d6d42b5f1dd64761f97997b54a86
diff --git a/.config/mpv/scripts/reload.lua b/.config/mpv/scripts/reload.lua
@@ -1,395 +0,0 @@
--- reload.lua
---
--- When an online video is stuck buffering or got very slow CDN
--- source, restarting often helps. This script provides automatic
--- reloading of videos that doesn't have buffering progress for some
--- time while keeping the current time position. It also adds `Ctrl+r`
--- keybinding to reload video manually.
---
--- SETTINGS
---
--- To override default setting put `lua-settings/reload.conf` file in
--- mpv user folder, on linux it is `~/.config/mpv`. NOTE: config file
--- name should match the name of the script.
---
--- Default `reload.conf` settings:
---
--- ```
--- # enable automatic reload on timeout
--- # when paused-for-cache event fired, we will wait
--- # paused_for_cache_timer_timeout sedonds and then reload the video
--- paused_for_cache_timer_enabled=yes
---
--- # checking paused_for_cache property interval in seconds,
--- # can not be less than 0.05 (50 ms)
--- paused_for_cache_timer_interval=1
---
--- # time in seconds to wait until reload
--- paused_for_cache_timer_timeout=10
---
--- # enable automatic reload based on demuxer cache
--- # if demuxer-cache-time property didn't change in demuxer_cache_timer_timeout
--- # time interval, the video will be reloaded as soon as demuxer cache depleated
--- demuxer_cache_timer_enabled=yes
---
--- # checking demuxer-cache-time property interval in seconds,
--- # can not be less than 0.05 (50 ms)
--- demuxer_cache_timer_interval=2
---
--- # if demuxer cache didn't receive any data during demuxer_cache_timer_timeout
--- # we decide that it has no progress and will reload the stream when
--- # paused_for_cache event happens
--- demuxer_cache_timer_timeout=20
---
--- # when the end-of-file is reached, reload the stream to check
--- # if there is more content available.
--- reload_eof_enabled=no
---
--- # keybinding to reload stream from current time position
--- # you can disable keybinding by setting it to empty value
--- # reload_key_binding=
--- reload_key_binding=Ctrl+r
--- ```
---
--- DEBUGGING
---
--- Debug messages will be printed to stdout with mpv command line option
--- `--msg-level='reload=debug'`
-
-local msg = require 'mp.msg'
-local options = require 'mp.options'
-local utils = require 'mp.utils'
-
-
-local settings = {
- paused_for_cache_timer_enabled = true,
- paused_for_cache_timer_interval = 1,
- paused_for_cache_timer_timeout = 10,
- demuxer_cache_timer_enabled = true,
- demuxer_cache_timer_interval = 2,
- demuxer_cache_timer_timeout = 20,
- reload_eof_enabled = false,
- reload_key_binding = "Ctrl+r",
-}
-
--- global state stores properties between reloads
-local property_path = nil
-local property_time_pos = 0
-local property_keep_open = nil
-
--- FSM managing the demuxer cache.
---
--- States:
---
--- * fetch - fetching new data
--- * stale - unable to fetch new data for time < 'demuxer_cache_timer_timeout'
--- * stuck - unable to fetch new data for time >= 'demuxer_cache_timer_timeout'
---
--- State transitions:
---
--- +---------------------------+
--- v |
--- +-------+ +-------+ +-------+
--- + fetch +<--->+ stale +---->+ stuck |
--- +-------+ +-------+ +-------+
--- | ^ | ^ | ^
--- +---+ +---+ +---+
-local demuxer_cache = {
- timer = nil,
-
- state = {
- name = 'uninitialized',
- demuxer_cache_time = 0,
- in_state_time = 0,
- },
-
- events = {
- continue_fetch = { name = 'continue_fetch', from = 'fetch', to = 'fetch' },
- continue_stale = { name = 'continue_stale', from = 'stale', to = 'stale' },
- continue_stuck = { name = 'continue_stuck', from = 'stuck', to = 'stuck' },
- fetch_to_stale = { name = 'fetch_to_stale', from = 'fetch', to = 'stale' },
- stale_to_fetch = { name = 'stale_to_fetch', from = 'stale', to = 'fetch' },
- stale_to_stuck = { name = 'stale_to_stuck', from = 'stale', to = 'stuck' },
- stuck_to_fetch = { name = 'stuck_to_fetch', from = 'stuck', to = 'fetch' },
- },
-
-}
-
--- Always start with 'fetch' state
-function demuxer_cache.reset_state()
- demuxer_cache.state = {
- name = demuxer_cache.events.continue_fetch.to,
- demuxer_cache_time = 0,
- in_state_time = 0,
- }
-end
-
--- Has 'demuxer_cache_time' changed
-function demuxer_cache.has_progress_since(t)
- return demuxer_cache.state.demuxer_cache_time ~= t
-end
-
-function demuxer_cache.is_state_fetch()
- return demuxer_cache.state.name == demuxer_cache.events.continue_fetch.to
-end
-
-function demuxer_cache.is_state_stale()
- return demuxer_cache.state.name == demuxer_cache.events.continue_stale.to
-end
-
-function demuxer_cache.is_state_stuck()
- return demuxer_cache.state.name == demuxer_cache.events.continue_stuck.to
-end
-
-function demuxer_cache.transition(event)
- if demuxer_cache.state.name == event.from then
-
- -- state setup
- demuxer_cache.state.demuxer_cache_time = event.demuxer_cache_time
-
- if event.name == 'continue_fetch' then
- demuxer_cache.state.in_state_time = demuxer_cache.state.in_state_time + event.interval
- elseif event.name == 'continue_stale' then
- demuxer_cache.state.in_state_time = demuxer_cache.state.in_state_time + event.interval
- elseif event.name == 'continue_stuck' then
- demuxer_cache.state.in_state_time = demuxer_cache.state.in_state_time + event.interval
- elseif event.name == 'fetch_to_stale' then
- demuxer_cache.state.in_state_time = 0
- elseif event.name == 'stale_to_fetch' then
- demuxer_cache.state.in_state_time = 0
- elseif event.name == 'stale_to_stuck' then
- demuxer_cache.state.in_state_time = 0
- elseif event.name == 'stuck_to_fetch' then
- demuxer_cache.state.in_state_time = 0
- end
-
- -- state transition
- demuxer_cache.state.name = event.to
-
- msg.debug('demuxer_cache.transition', event.name, utils.to_string(demuxer_cache.state))
- else
- msg.error(
- 'demuxer_cache.transition',
- 'illegal transition', event.name,
- 'from state', demuxer_cache.state.name)
- end
-end
-
-function demuxer_cache.initialize(demuxer_cache_timer_interval)
- demuxer_cache.reset_state()
- demuxer_cache.timer = mp.add_periodic_timer(
- demuxer_cache_timer_interval,
- function()
- demuxer_cache.demuxer_cache_timer_tick(
- mp.get_property_native('demuxer-cache-time'),
- demuxer_cache_timer_interval)
- end
- )
-end
-
--- If there is no progress of demuxer_cache_time in
--- settings.demuxer_cache_timer_timeout time interval switch state to
--- 'stuck' and switch back to 'fetch' as soon as any progress is made
-function demuxer_cache.demuxer_cache_timer_tick(demuxer_cache_time, demuxer_cache_timer_interval)
- local event = nil
- local cache_has_progress = demuxer_cache.has_progress_since(demuxer_cache_time)
-
- -- I miss pattern matching so much
- if demuxer_cache.is_state_fetch() then
- if cache_has_progress then
- event = demuxer_cache.events.continue_fetch
- else
- event = demuxer_cache.events.fetch_to_stale
- end
- elseif demuxer_cache.is_state_stale() then
- if cache_has_progress then
- event = demuxer_cache.events.stale_to_fetch
- elseif demuxer_cache.state.in_state_time < settings.demuxer_cache_timer_timeout then
- event = demuxer_cache.events.continue_stale
- else
- event = demuxer_cache.events.stale_to_stuck
- end
- elseif demuxer_cache.is_state_stuck() then
- if cache_has_progress then
- event = demuxer_cache.events.stuck_to_fetch
- else
- event = demuxer_cache.events.continue_stuck
- end
- end
-
- event.demuxer_cache_time = demuxer_cache_time
- event.interval = demuxer_cache_timer_interval
- demuxer_cache.transition(event)
-end
-
-
-local paused_for_cache = {
- timer = nil,
- time = 0,
-}
-
-function paused_for_cache.reset_timer()
- msg.debug('paused_for_cache.reset_timer', paused_for_cache.time)
- if paused_for_cache.timer then
- paused_for_cache.timer:kill()
- paused_for_cache.timer = nil
- paused_for_cache.time = 0
- end
-end
-
-function paused_for_cache.start_timer(interval_seconds, timeout_seconds)
- msg.debug('paused_for_cache.start_timer', paused_for_cache.time)
- if not paused_for_cache.timer then
- paused_for_cache.timer = mp.add_periodic_timer(
- interval_seconds,
- function()
- paused_for_cache.time = paused_for_cache.time + interval_seconds
- if paused_for_cache.time >= timeout_seconds then
- paused_for_cache.reset_timer()
- reload_resume()
- end
- msg.debug('paused_for_cache', 'tick', paused_for_cache.time)
- end
- )
- end
-end
-
-function paused_for_cache.handler(property, is_paused)
- if is_paused then
-
- if demuxer_cache.is_state_stuck() then
- msg.info("demuxer cache has no progress")
- -- reset demuxer state to avoid immediate reload if
- -- paused_for_cache event triggered right after reload
- demuxer_cache.reset_state()
- reload_resume()
- end
-
- paused_for_cache.start_timer(
- settings.paused_for_cache_timer_interval,
- settings.paused_for_cache_timer_timeout)
- else
- paused_for_cache.reset_timer()
- end
-end
-
-function read_settings()
- options.read_options(settings, mp.get_script_name())
- msg.debug(utils.to_string(settings))
-end
-
-function debug_info(event)
- msg.debug("event =", utils.to_string(event))
- msg.debug("path =", mp.get_property("path"))
- msg.debug("time-pos =", mp.get_property("time-pos"))
- msg.debug("paused-for-cache =", mp.get_property("paused-for-cache"))
- msg.debug("stream-path =", mp.get_property("stream-path"))
- msg.debug("stream-pos =", mp.get_property("stream-pos"))
- msg.debug("stream-end =", mp.get_property("stream-end"))
- msg.debug("duration =", mp.get_property("duration"))
- msg.debug("seekable =", mp.get_property("seekable"))
-end
-
-function reload(path, time_pos)
- msg.debug("reload", path, time_pos)
- if time_pos == nil then
- mp.commandv("loadfile", path, "replace")
- else
- mp.commandv("loadfile", path, "replace", "start=+" .. time_pos)
- end
-end
-
-function reload_resume()
- local path = mp.get_property("path", property_path)
- local time_pos = mp.get_property("time-pos")
- local reload_duration = mp.get_property_native("duration")
-
- local playlist_count = mp.get_property_number("playlist/count")
- local playlist_pos = mp.get_property_number("playlist-pos")
- local playlist = {}
- for i = 0, playlist_count-1 do
- playlist[i] = mp.get_property("playlist/" .. i .. "/filename")
- end
- -- 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
- msg.info("reloading video from", time_pos, "second")
- reload(path, time_pos)
- else
- msg.info("reloading stream")
- reload(path, nil)
- end
- msg.info("file ", playlist_pos+1, " of ", playlist_count, "in playlist")
- for i = 0, playlist_pos-1 do
- mp.commandv("loadfile", playlist[i], "append")
- end
- mp.commandv("playlist-move", 0, playlist_pos+1)
- for i = playlist_pos+1, playlist_count-1 do
- mp.commandv("loadfile", playlist[i], "append")
- end
-end
-
-function reload_eof(property, eof_reached)
- msg.debug("reload_eof", property, eof_reached)
- local time_pos = mp.get_property_number("time-pos")
- local duration = mp.get_property_number("duration")
-
- if eof_reached and math.floor(time_pos) == math.floor(duration) then
- msg.debug("property_time_pos", property_time_pos, "time_pos", time_pos)
-
- -- Check that playback time_pos made progress after the last reload. When
- -- eof is reached we try to reload video, in case there is more content
- -- available. If time_pos stayed the same after reload, it means that vidkk
- -- to avoid infinite reload loop when playback ended
- -- math.floor function rounds time_pos to a second, to avoid inane reloads
- if math.floor(property_time_pos) == math.floor(time_pos) then
- msg.info("eof reached, playback ended")
- mp.set_property("keep-open", property_keep_open)
- else
- msg.info("eof reached, checking if more content available")
- reload_resume()
- mp.set_property_bool("pause", false)
- property_time_pos = time_pos
- end
- end
-end
-
--- main
-
-read_settings()
-
-if settings.reload_key_binding ~= "" then
- mp.add_key_binding(settings.reload_key_binding, "reload_resume", reload_resume)
-end
-
-if settings.paused_for_cache_timer_enabled then
- mp.observe_property("paused-for-cache", "bool", paused_for_cache.handler)
-end
-
-if settings.demuxer_cache_timer_enabled then
- demuxer_cache.initialize(settings.demuxer_cache_timer_interval)
-end
-
-if settings.reload_eof_enabled then
- -- vo-configured == video output created && its configuration went ok
- mp.observe_property(
- "vo-configured",
- "bool",
- function(name, vo_configured)
- msg.debug(name, vo_configured)
- if vo_configured then
- property_path = mp.get_property("path")
- property_keep_open = mp.get_property("keep-open")
- mp.set_property("keep-open", "yes")
- mp.set_property("keep-open-pause", "no")
- end
- end
- )
-
- mp.observe_property("eof-reached", "bool", reload_eof)
-end
-
---mp.register_event("file-loaded", debug_info)
diff --git a/.config/mpv/scripts/webm.lua b/.config/mpv/scripts/webm.lua
@@ -5,7 +5,7 @@ local utils = require("mp.utils")
local mpopts = require("mp.options")
local options = {
-- Defaults to shift+w
- keybind = "E",
+ 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
@@ -20,11 +20,17 @@ local options = {
-- %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 time, without milliseconds
+ -- %S, %E - Start and end time, without milliseconds
-- %M - "-audio", if audio is enabled, empty otherwise
- output_template = "%F-[%S-%E]%M",
+ -- %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
@@ -50,9 +56,12 @@ local options = {
-- Set the number of encoding threads, for codecs libvpx and libvpx-vp9
libvpx_threads = 4,
additional_flags = "",
- -- Useful for flags that may impact output filesize, such as crf, qmin, qmax etc
+ -- 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 = "--ovcopts-add=crf=10",
+ 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",
@@ -63,6 +72,37 @@ local options = {
}
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 bold
bold = function(text)
return "{\\b1}" .. tostring(text) .. "{\\b0}"
@@ -110,9 +150,84 @@ file_exists = function(name)
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),
@@ -120,13 +235,34 @@ format_filename = function(startTime, endTime, videoFormat)
["%%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 '-audio' or ''
+ ["%%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)
@@ -166,10 +302,10 @@ 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")
- msg.verbose("Command stdout: ")
- msg.verbose(res.stdout)
return false
end
return true
@@ -214,6 +350,22 @@ should_display_progress = function()
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
@@ -438,6 +590,189 @@ make_fullscreen_region = function()
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
@@ -451,6 +786,16 @@ do
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
}
_base_0.__index = _base_0
@@ -703,6 +1048,138 @@ do
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 = { }
+ _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
@@ -765,6 +1242,9 @@ do
return nil
end,
show = function(self)
+ if self.visible then
+ return
+ end
self.visible = true
self:observe_properties()
self:add_keybinds()
@@ -773,6 +1253,9 @@ do
return self:draw()
end,
hide = function(self)
+ if not self.visible then
+ return
+ end
self.visible = false
self:unobserve_properties()
self:remove_keybinds()
@@ -782,6 +1265,7 @@ do
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
@@ -844,7 +1328,7 @@ do
copy_command_line = _accum_0
end
append(copy_command_line, {
- '--term-status-msg=Encode time-pos: ${=time-pos}'
+ '--term-status-msg=Encode time-pos: ${=time-pos}\\n'
})
self:show()
local processFd = run_subprocess_popen(copy_command_line)
@@ -904,14 +1388,80 @@ get_active_tracks = function()
audio = not mp.get_property_bool("mute"),
sub = mp.get_property_bool("sub-visibility")
}
- local active = { }
+ local active = {
+ video = { },
+ audio = { },
+ sub = { }
+ }
for _, track in ipairs(mp.get_property_native("track-list")) do
if track["selected"] and accepted[track["type"]] then
- active[#active + 1] = track
+ 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()
if options.scale_height > 0 then
@@ -921,6 +1471,15 @@ get_scale_filters = function()
end
return { }
end
+local get_fps_filters
+get_fps_filters = function()
+ if options.fps > 0 then
+ return {
+ "fps=" .. tostring(options.fps)
+ }
+ end
+ return { }
+end
local append_property
append_property = function(out, property_name, option_name)
option_name = option_name or property_name
@@ -949,16 +1508,11 @@ get_playback_options = function()
local ret = { }
append_property(ret, "sub-ass-override")
append_property(ret, "sub-ass-force-style")
+ append_property(ret, "sub-ass-vsfilter-aspect-compat")
append_property(ret, "sub-auto")
append_property(ret, "sub-delay")
append_property(ret, "video-rotate")
- for _, track in ipairs(mp.get_property_native("track-list")) do
- if track["type"] == "sub" and track["external"] then
- append(ret, {
- "--sub-files-append=" .. tostring(track['external-filename'])
- })
- end
- end
+ append_property(ret, "ytdl-format")
return ret
end
local get_speed_flags
@@ -1009,43 +1563,8 @@ apply_current_filters = function(filters)
end
end
end
-local encode
-encode = function(region, startTime, endTime)
- local format = formats[options.output_format]
- local path = mp.get_property("path")
- if not path then
- message("No file is being played")
- return
- end
- local is_stream = not file_exists(path)
- local command = {
- "mpv",
- path,
- "--start=" .. seconds_to_time_string(startTime, false, true),
- "--end=" .. seconds_to_time_string(endTime, false, true),
- "--ovc=" .. tostring(format.videoCodec),
- "--oac=" .. tostring(format.audioCodec),
- "--loop-file=no"
- }
- local vid = -1
- local aid = -1
- local sid = -1
- for _, track in ipairs(get_active_tracks()) do
- local _exp_0 = track["type"]
- if "video" == _exp_0 then
- vid = track['id']
- elseif "audio" == _exp_0 then
- aid = track['id']
- elseif "sub" == _exp_0 then
- sid = track['id']
- end
- end
- append(command, {
- "--vid=" .. (vid >= 0 and tostring(vid) or "no"),
- "--aid=" .. (aid >= 0 and tostring(aid) or "no"),
- "--sid=" .. (sid >= 0 and tostring(sid) or "no")
- })
- append(command, get_playback_options())
+local get_video_filters
+get_video_filters = function(format, region)
local filters = { }
append(filters, format:getPreFilters())
if options.apply_current_filters then
@@ -1057,45 +1576,157 @@ encode = function(region, startTime, endTime)
})
end
append(filters, get_scale_filters())
+ append(filters, get_fps_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(command, {
+ append(flags, {
"--vf-add=" .. tostring(f)
})
end
- append(command, get_speed_flags())
+ 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_temporary, is_stream
+ path, is_temporary, is_stream, 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 options.target_filesize > 0 and format.acceptsBitrate then
- local dT = endTime - startTime
- if options.strict_filesize_constraint then
- local video_kilobits = options.target_filesize * 8
- if aid >= 0 then
- video_kilobits = video_kilobits - dT * options.strict_audio_bitrate
+ 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, {
- "--oacopts-add=b=" .. tostring(options.strict_audio_bitrate) .. "k"
+ "--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
- video_kilobits = video_kilobits * options.strict_bitrate_multiplier
- local bitrate = math.floor(video_kilobits / dT)
- append(command, {
- "--ovcopts-add=b=" .. tostring(bitrate) .. "k",
- "--ovcopts-add=minrate=" .. tostring(bitrate) .. "k",
- "--ovcopts-add=maxrate=" .. tostring(bitrate) .. "k"
- })
else
- local bitrate = math.floor(options.target_filesize * 8 / dT)
+ local type = format.videoCodec ~= "" and "ovc" or "oac"
append(command, {
- "--ovcopts-add=b=" .. tostring(bitrate) .. "k"
+ "--" .. tostring(type) .. "opts-add=b=0"
})
end
- elseif options.target_filesize <= 0 and format.acceptsBitrate then
- append(command, {
- "--ovcopts-add=b=0"
- })
end
for token in string.gmatch(options.additional_flags, "[^%s]+") do
command[#command + 1] = token
@@ -1104,7 +1735,27 @@ encode = function(region, startTime, endTime)
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)
+ })
if options.twopass and format.supportsTwopass and not is_stream then
local first_pass_cmdline
do
@@ -1118,9 +1769,7 @@ encode = function(region, startTime, endTime)
first_pass_cmdline = _accum_0
end
append(first_pass_cmdline, {
- "--ovcopts-add=flags=+pass1",
- "-of=" .. tostring(format.outputExtension),
- "-o=" .. tostring(get_null_path())
+ "--ovcopts-add=flags=+pass1"
})
message("Starting first pass...")
msg.verbose("First-pass command line: ", table.concat(first_pass_cmdline, " "))
@@ -1135,22 +1784,11 @@ encode = function(region, startTime, endTime)
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
- 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(startTime, endTime, format)
- local out_path = utils.join_path(dir, formatted_filename)
- append(command, {
- "-o=" .. tostring(out_path)
- })
msg.info("Encoding to", out_path)
msg.verbose("Command line:", table.concat(command, " "))
if options.run_detached then
@@ -1171,9 +1809,13 @@ encode = function(region, startTime, endTime)
res = ewp:startEncode(command)
end
if res then
- return message("Encoded successfully! Saved to\\N" .. tostring(bold(out_path)))
+ message("Encoded successfully! Saved to\\N" .. tostring(bold(out_path)))
else
- return message("Encode failed! Check the logs for details.")
+ message("Encode failed! Check the logs for details.")
+ end
+ os.remove(get_pass_logfile_path(out_path))
+ if is_temporary then
+ return os.remove(path)
end
end
end
@@ -1229,6 +1871,7 @@ do
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}')
@@ -1253,7 +1896,8 @@ do
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")
- ass:append(tostring(bold('ENTER:')) .. " confirm 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
}
@@ -1587,11 +2231,53 @@ do
[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",
- "raw"
+ "mp4-nvenc",
+ "raw",
+ "mp3",
+ "gif"
}
local formatOpts = {
possibleValues = (function()
@@ -1636,6 +2322,14 @@ do
{
"target_filesize",
Option("int", "Target Filesize", options.target_filesize, filesizeOpts)
+ },
+ {
+ "crf",
+ Option("int", "CRF", options.crf, crfOpts)
+ },
+ {
+ "fps",
+ Option("list", "FPS", options.fps, fpsOpts)
}
}
self.keybinds = {
diff --git a/.gitmodules b/.gitmodules
@@ -10,3 +10,9 @@
[submodule "src/c/doas"]
path = src/c/doas
url = git@github.com:0x766F6964/doas.git
+[submodule ".config/mpv/scripts/mpv-reload"]
+ path = .config/mpv/scripts/mpv-reload
+ url = https://github.com/4e6/mpv-reload.git
+[submodule ".config/mpv/scripts/mpv-image-viewer"]
+ path = .config/mpv/scripts/mpv-image-viewer
+ url = https://github.com/occivink/mpv-image-viewer.git